Merge branch 'develop' into feature/ons/fix_email_confirmation_flow
This commit is contained in:
commit
7722e6d8e7
|
@ -12,12 +12,16 @@ Improvements 🙌:
|
||||||
- Allow user to reset cross signing if he has no way to recover (#2052)
|
- Allow user to reset cross signing if he has no way to recover (#2052)
|
||||||
- Create home shortcut for any room (#1525)
|
- Create home shortcut for any room (#1525)
|
||||||
- Can't confirm email due to killing by Android (#2021)
|
- Can't confirm email due to killing by Android (#2021)
|
||||||
|
- Add a menu item to open the setting in room list and in room (#2171)
|
||||||
|
- Add a menu item in the timeline as a shortcut to invite user (#2171)
|
||||||
|
- Drawer: move settings access and add sign out action (#2171)
|
||||||
- Filter room member (and banned users) by name (#2184)
|
- Filter room member (and banned users) by name (#2184)
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Improve support for image/audio/video/file selection with intent changes (#1376)
|
- Improve support for image/audio/video/file selection with intent changes (#1376)
|
||||||
- Fix Splash layout on small screens
|
- Fix Splash layout on small screens
|
||||||
- Invalid popup when pressing back (#1635)
|
- Invalid popup when pressing back (#1635)
|
||||||
|
- Simplifies draft management and should fix bunch of draft issues (#952, #683)
|
||||||
|
|
||||||
Translations 🗣:
|
Translations 🗣:
|
||||||
-
|
-
|
||||||
|
|
|
@ -101,8 +101,11 @@ class RxRoom(private val room: Room) {
|
||||||
return room.getEventReadReceiptsLive(eventId).asObservable()
|
return room.getEventReadReceiptsLive(eventId).asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveDrafts(): Observable<List<UserDraft>> {
|
fun liveDraft(): Observable<Optional<UserDraft>> {
|
||||||
return room.getDraftsLive().asObservable()
|
return room.getDraftLive().asObservable()
|
||||||
|
.startWithCallable {
|
||||||
|
room.getDraft().toOptional()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveNotificationState(): Observable<RoomNotificationState> {
|
fun liveNotificationState(): Observable<RoomNotificationState> {
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.matrix.android.sdk.api.session.room.send
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
|
||||||
interface DraftService {
|
interface DraftService {
|
||||||
|
|
||||||
|
@ -34,8 +35,12 @@ interface DraftService {
|
||||||
fun deleteDraft(callback: MatrixCallback<Unit>): Cancelable
|
fun deleteDraft(callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current drafts if any, as a live data
|
* Return the current draft or null
|
||||||
* The draft list can contain one draft for {regular, reply, quote} and an arbitrary number of {edit} drafts
|
|
||||||
*/
|
*/
|
||||||
fun getDraftsLive(): LiveData<List<UserDraft>>
|
fun getDraft(): UserDraft?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current draft if any, as a live data
|
||||||
|
*/
|
||||||
|
fun getDraftLive(): LiveData<Optional<UserDraft>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.session.room.send.DraftService
|
import org.matrix.android.sdk.api.session.room.send.DraftService
|
||||||
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.task.launchToCallback
|
import org.matrix.android.sdk.internal.task.launchToCallback
|
||||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||||
|
@ -55,7 +56,11 @@ internal class DefaultDraftService @AssistedInject constructor(@Assisted private
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDraftsLive(): LiveData<List<UserDraft>> {
|
override fun getDraft(): UserDraft? {
|
||||||
|
return draftRepository.getDraft(roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDraftLive(): LiveData<Optional<UserDraft>> {
|
||||||
return draftRepository.getDraftsLive(roomId)
|
return draftRepository.getDraftsLive(roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,43 +20,67 @@ package org.matrix.android.sdk.internal.session.room.draft
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.Transformations
|
import androidx.lifecycle.Transformations
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.kotlin.createObject
|
||||||
import org.matrix.android.sdk.BuildConfig
|
import org.matrix.android.sdk.BuildConfig
|
||||||
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
import org.matrix.android.sdk.api.util.toOptional
|
||||||
|
import org.matrix.android.sdk.internal.database.RealmSessionProvider
|
||||||
import org.matrix.android.sdk.internal.database.mapper.DraftMapper
|
import org.matrix.android.sdk.internal.database.mapper.DraftMapper
|
||||||
import org.matrix.android.sdk.internal.database.model.DraftEntity
|
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.UserDraftsEntity
|
import org.matrix.android.sdk.internal.database.model.UserDraftsEntity
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.kotlin.createObject
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DraftRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy) {
|
internal class DraftRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
|
||||||
|
private val realmSessionProvider: RealmSessionProvider) {
|
||||||
|
|
||||||
suspend fun saveDraft(roomId: String, userDraft: UserDraft) {
|
suspend fun saveDraft(roomId: String, userDraft: UserDraft) {
|
||||||
monarchy.awaitTransaction {
|
monarchy.awaitTransaction {
|
||||||
saveDraft(it, userDraft, roomId)
|
saveDraftInDb(it, userDraft, roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteDraft(roomId: String) {
|
suspend fun deleteDraft(roomId: String) {
|
||||||
monarchy.awaitTransaction {
|
monarchy.awaitTransaction {
|
||||||
deleteDraft(it, roomId)
|
deleteDraftFromDb(it, roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteDraft(realm: Realm, roomId: String) {
|
fun getDraft(roomId: String): UserDraft? {
|
||||||
UserDraftsEntity.where(realm, roomId).findFirst()?.let { userDraftsEntity ->
|
return realmSessionProvider.withRealm { realm ->
|
||||||
if (userDraftsEntity.userDrafts.isNotEmpty()) {
|
UserDraftsEntity.where(realm, roomId).findFirst()
|
||||||
userDraftsEntity.userDrafts.removeAt(userDraftsEntity.userDrafts.size - 1)
|
?.userDrafts
|
||||||
}
|
?.firstOrNull()
|
||||||
|
?.let {
|
||||||
|
DraftMapper.map(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveDraft(realm: Realm, draft: UserDraft, roomId: String) {
|
fun getDraftsLive(roomId: String): LiveData<Optional<UserDraft>> {
|
||||||
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
|
{ UserDraftsEntity.where(it, roomId) },
|
||||||
|
{
|
||||||
|
it.userDrafts.map { draft ->
|
||||||
|
DraftMapper.map(draft)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return Transformations.map(liveData) {
|
||||||
|
it.firstOrNull()?.firstOrNull().toOptional()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteDraftFromDb(realm: Realm, roomId: String) {
|
||||||
|
UserDraftsEntity.where(realm, roomId).findFirst()?.userDrafts?.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveDraftInDb(realm: Realm, draft: UserDraft, roomId: String) {
|
||||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||||
?: realm.createObject(roomId)
|
?: realm.createObject(roomId)
|
||||||
|
|
||||||
|
@ -68,62 +92,15 @@ class DraftRepository @Inject constructor(@SessionDatabase private val monarchy:
|
||||||
userDraftsEntity.let { userDraftEntity ->
|
userDraftsEntity.let { userDraftEntity ->
|
||||||
// Save only valid draft
|
// Save only valid draft
|
||||||
if (draft.isValid()) {
|
if (draft.isValid()) {
|
||||||
// Add a new draft or update the current one?
|
// Replace the current draft
|
||||||
val newDraft = DraftMapper.map(draft)
|
val newDraft = DraftMapper.map(draft)
|
||||||
|
Timber.d("Draft: create a new draft ${privacySafe(draft)}")
|
||||||
// Is it an update of the top draft?
|
userDraftEntity.userDrafts.clear()
|
||||||
val topDraft = userDraftEntity.userDrafts.lastOrNull()
|
userDraftEntity.userDrafts.add(newDraft)
|
||||||
|
|
||||||
if (topDraft == null) {
|
|
||||||
Timber.d("Draft: create a new draft ${privacySafe(draft)}")
|
|
||||||
userDraftEntity.userDrafts.add(newDraft)
|
|
||||||
} else if (topDraft.draftMode == DraftEntity.MODE_EDIT) {
|
|
||||||
// top draft is an edit
|
|
||||||
if (newDraft.draftMode == DraftEntity.MODE_EDIT) {
|
|
||||||
if (topDraft.linkedEventId == newDraft.linkedEventId) {
|
|
||||||
// Update the top draft
|
|
||||||
Timber.d("Draft: update the top edit draft ${privacySafe(draft)}")
|
|
||||||
topDraft.content = newDraft.content
|
|
||||||
} else {
|
|
||||||
// Check a previously EDIT draft with the same id
|
|
||||||
val existingEditDraftOfSameEvent = userDraftEntity.userDrafts.find {
|
|
||||||
it.draftMode == DraftEntity.MODE_EDIT && it.linkedEventId == newDraft.linkedEventId
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingEditDraftOfSameEvent != null) {
|
|
||||||
// Ignore the new text, restore what was typed before, by putting the draft to the top
|
|
||||||
Timber.d("Draft: restore a previously edit draft ${privacySafe(draft)}")
|
|
||||||
userDraftEntity.userDrafts.remove(existingEditDraftOfSameEvent)
|
|
||||||
userDraftEntity.userDrafts.add(existingEditDraftOfSameEvent)
|
|
||||||
} else {
|
|
||||||
Timber.d("Draft: add a new edit draft ${privacySafe(draft)}")
|
|
||||||
userDraftEntity.userDrafts.add(newDraft)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Add a new regular draft to the top
|
|
||||||
Timber.d("Draft: add a new draft ${privacySafe(draft)}")
|
|
||||||
userDraftEntity.userDrafts.add(newDraft)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Top draft is not an edit
|
|
||||||
if (newDraft.draftMode == DraftEntity.MODE_EDIT) {
|
|
||||||
Timber.d("Draft: create a new edit draft ${privacySafe(draft)}")
|
|
||||||
userDraftEntity.userDrafts.add(newDraft)
|
|
||||||
} else {
|
|
||||||
// Update the top draft
|
|
||||||
Timber.d("Draft: update the top draft ${privacySafe(draft)}")
|
|
||||||
topDraft.draftMode = newDraft.draftMode
|
|
||||||
topDraft.content = newDraft.content
|
|
||||||
topDraft.linkedEventId = newDraft.linkedEventId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// There is no draft to save, so the composer was clear
|
// There is no draft to save, so the composer was clear
|
||||||
Timber.d("Draft: delete a draft")
|
Timber.d("Draft: delete a draft")
|
||||||
|
|
||||||
val topDraft = userDraftEntity.userDrafts.lastOrNull()
|
val topDraft = userDraftEntity.userDrafts.lastOrNull()
|
||||||
|
|
||||||
if (topDraft == null) {
|
if (topDraft == null) {
|
||||||
Timber.d("Draft: nothing to do")
|
Timber.d("Draft: nothing to do")
|
||||||
} else {
|
} else {
|
||||||
|
@ -135,20 +112,6 @@ class DraftRepository @Inject constructor(@SessionDatabase private val monarchy:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDraftsLive(roomId: String): LiveData<List<UserDraft>> {
|
|
||||||
val liveData = monarchy.findAllMappedWithChanges(
|
|
||||||
{ UserDraftsEntity.where(it, roomId) },
|
|
||||||
{
|
|
||||||
it.userDrafts.map { draft ->
|
|
||||||
DraftMapper.map(draft)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return Transformations.map(liveData) {
|
|
||||||
it.firstOrNull().orEmpty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun privacySafe(o: Any): Any {
|
private fun privacySafe(o: Any): Any {
|
||||||
if (BuildConfig.LOG_PRIVATE_DATA) {
|
if (BuildConfig.LOG_PRIVATE_DATA) {
|
||||||
return o
|
return o
|
||||||
|
|
|
@ -305,6 +305,10 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
||||||
navigator.openRoomsFiltering(this)
|
navigator.openRoomsFiltering(this)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
R.id.menu_home_setting -> {
|
||||||
|
navigator.openSettings(this)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
|
|
|
@ -18,18 +18,22 @@ package im.vector.app.features.home
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.observeK
|
import im.vector.app.core.extensions.observeK
|
||||||
import im.vector.app.core.extensions.replaceChildFragment
|
import im.vector.app.core.extensions.replaceChildFragment
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.features.grouplist.GroupListFragment
|
import im.vector.app.features.grouplist.GroupListFragment
|
||||||
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
|
import im.vector.app.features.workers.signout.SignOutUiWorker
|
||||||
|
import kotlinx.android.synthetic.main.fragment_home_drawer.*
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
import kotlinx.android.synthetic.main.fragment_home_drawer.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class HomeDrawerFragment @Inject constructor(
|
class HomeDrawerFragment @Inject constructor(
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val avatarRenderer: AvatarRenderer
|
private val avatarRenderer: AvatarRenderer
|
||||||
) : VectorBaseFragment() {
|
) : VectorBaseFragment() {
|
||||||
|
|
||||||
|
@ -53,12 +57,19 @@ class HomeDrawerFragment @Inject constructor(
|
||||||
homeDrawerUserIdView.text = user.userId
|
homeDrawerUserIdView.text = user.userId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Settings
|
||||||
homeDrawerHeaderSettingsView.debouncedClicks {
|
homeDrawerHeaderSettingsView.debouncedClicks {
|
||||||
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
|
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
|
||||||
navigator.openSettings(requireActivity())
|
navigator.openSettings(requireActivity())
|
||||||
}
|
}
|
||||||
|
// Sign out
|
||||||
|
homeDrawerHeaderSignoutView.debouncedClicks {
|
||||||
|
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
|
||||||
|
SignOutUiWorker(requireActivity()).perform()
|
||||||
|
}
|
||||||
|
|
||||||
// Debug menu
|
// Debug menu
|
||||||
|
homeDrawerHeaderDebugView.isVisible = vectorPreferences.developerMode()
|
||||||
homeDrawerHeaderDebugView.debouncedClicks {
|
homeDrawerHeaderDebugView.debouncedClicks {
|
||||||
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
|
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
|
||||||
navigator.openDebug(requireActivity())
|
navigator.openDebug(requireActivity())
|
||||||
|
|
|
@ -51,7 +51,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
||||||
data class EnterEditMode(val eventId: String, val text: String) : RoomDetailAction()
|
data class EnterEditMode(val eventId: String, val text: String) : RoomDetailAction()
|
||||||
data class EnterQuoteMode(val eventId: String, val text: String) : RoomDetailAction()
|
data class EnterQuoteMode(val eventId: String, val text: String) : RoomDetailAction()
|
||||||
data class EnterReplyMode(val eventId: String, val text: String) : RoomDetailAction()
|
data class EnterReplyMode(val eventId: String, val text: String) : RoomDetailAction()
|
||||||
data class ExitSpecialMode(val text: String) : RoomDetailAction()
|
data class EnterRegularMode(val text: String, val fromSharing: Boolean) : RoomDetailAction()
|
||||||
|
|
||||||
data class ResendMessage(val eventId: String) : RoomDetailAction()
|
data class ResendMessage(val eventId: String) : RoomDetailAction()
|
||||||
data class RemoveFailedEcho(val eventId: String) : RoomDetailAction()
|
data class RemoveFailedEcho(val eventId: String) : RoomDetailAction()
|
||||||
|
|
|
@ -485,8 +485,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
when (val sharedData = roomDetailArgs.sharedData) {
|
when (val sharedData = roomDetailArgs.sharedData) {
|
||||||
is SharedData.Text -> {
|
is SharedData.Text -> {
|
||||||
// Save a draft to set the shared text to the composer
|
roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(sharedData.text, fromSharing = true))
|
||||||
roomDetailViewModel.handle(RoomDetailAction.SaveDraft(sharedData.text))
|
|
||||||
}
|
}
|
||||||
is SharedData.Attachments -> {
|
is SharedData.Attachments -> {
|
||||||
// open share edition
|
// open share edition
|
||||||
|
@ -656,6 +655,14 @@ class RoomDetailFragment @Inject constructor(
|
||||||
roomDetailViewModel.handle(RoomDetailAction.ClearSendQueue)
|
roomDetailViewModel.handle(RoomDetailAction.ClearSendQueue)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
R.id.invite -> {
|
||||||
|
navigator.openInviteUsersToRoom(requireActivity(), roomDetailArgs.roomId)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.timeline_setting -> {
|
||||||
|
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
|
||||||
|
true
|
||||||
|
}
|
||||||
R.id.resend_all -> {
|
R.id.resend_all -> {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.ResendAll)
|
roomDetailViewModel.handle(RoomDetailAction.ResendAll)
|
||||||
true
|
true
|
||||||
|
@ -1014,7 +1021,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCloseRelatedMessage() {
|
override fun onCloseRelatedMessage() {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.ExitSpecialMode(composerLayout.text.toString()))
|
roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(composerLayout.text.toString(), false))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRichContentSelected(contentUri: Uri): Boolean {
|
override fun onRichContentSelected(contentUri: Uri): Boolean {
|
||||||
|
@ -1147,12 +1154,8 @@ class RoomDetailFragment @Inject constructor(
|
||||||
|
|
||||||
private fun renderSendMessageResult(sendMessageResult: RoomDetailViewEvents.SendMessageResult) {
|
private fun renderSendMessageResult(sendMessageResult: RoomDetailViewEvents.SendMessageResult) {
|
||||||
when (sendMessageResult) {
|
when (sendMessageResult) {
|
||||||
is RoomDetailViewEvents.MessageSent -> {
|
|
||||||
updateComposerText("")
|
|
||||||
}
|
|
||||||
is RoomDetailViewEvents.SlashCommandHandled -> {
|
is RoomDetailViewEvents.SlashCommandHandled -> {
|
||||||
sendMessageResult.messageRes?.let { showSnackWithMessage(getString(it)) }
|
sendMessageResult.messageRes?.let { showSnackWithMessage(getString(it)) }
|
||||||
updateComposerText("")
|
|
||||||
}
|
}
|
||||||
is RoomDetailViewEvents.SlashCommandError -> {
|
is RoomDetailViewEvents.SlashCommandError -> {
|
||||||
displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command))
|
displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command))
|
||||||
|
|
|
@ -164,7 +164,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
getUnreadState()
|
getUnreadState()
|
||||||
observeSyncState()
|
observeSyncState()
|
||||||
observeEventDisplayedActions()
|
observeEventDisplayedActions()
|
||||||
observeDrafts()
|
getDraftIfAny()
|
||||||
observeUnreadState()
|
observeUnreadState()
|
||||||
observeMyRoomMember()
|
observeMyRoomMember()
|
||||||
observeActiveRoomWidgets()
|
observeActiveRoomWidgets()
|
||||||
|
@ -180,11 +180,13 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
PowerLevelsObservableFactory(room).createObservable()
|
PowerLevelsObservableFactory(room).createObservable()
|
||||||
.subscribe {
|
.subscribe {
|
||||||
val canSendMessage = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
|
val canSendMessage = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
|
||||||
|
val canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId)
|
||||||
val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId)
|
val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId)
|
||||||
val isAllowedToStartWebRTCCall = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE)
|
val isAllowedToStartWebRTCCall = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE)
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
canSendMessage = canSendMessage,
|
canSendMessage = canSendMessage,
|
||||||
|
canInvite = canInvite,
|
||||||
isAllowedToManageWidgets = isAllowedToManageWidgets,
|
isAllowedToManageWidgets = isAllowedToManageWidgets,
|
||||||
isAllowedToStartWebRTCCall = isAllowedToStartWebRTCCall
|
isAllowedToStartWebRTCCall = isAllowedToStartWebRTCCall
|
||||||
)
|
)
|
||||||
|
@ -226,52 +228,52 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
override fun handle(action: RoomDetailAction) {
|
override fun handle(action: RoomDetailAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomDetailAction.UserIsTyping -> handleUserIsTyping(action)
|
is RoomDetailAction.UserIsTyping -> handleUserIsTyping(action)
|
||||||
is RoomDetailAction.SaveDraft -> handleSaveDraft(action)
|
is RoomDetailAction.SaveDraft -> handleSaveDraft(action)
|
||||||
is RoomDetailAction.SendMessage -> handleSendMessage(action)
|
is RoomDetailAction.SendMessage -> handleSendMessage(action)
|
||||||
is RoomDetailAction.SendMedia -> handleSendMedia(action)
|
is RoomDetailAction.SendMedia -> handleSendMedia(action)
|
||||||
is RoomDetailAction.SendSticker -> handleSendSticker(action)
|
is RoomDetailAction.SendSticker -> handleSendSticker(action)
|
||||||
is RoomDetailAction.TimelineEventTurnsVisible -> handleEventVisible(action)
|
is RoomDetailAction.TimelineEventTurnsVisible -> handleEventVisible(action)
|
||||||
is RoomDetailAction.TimelineEventTurnsInvisible -> handleEventInvisible(action)
|
is RoomDetailAction.TimelineEventTurnsInvisible -> handleEventInvisible(action)
|
||||||
is RoomDetailAction.LoadMoreTimelineEvents -> handleLoadMore(action)
|
is RoomDetailAction.LoadMoreTimelineEvents -> handleLoadMore(action)
|
||||||
is RoomDetailAction.SendReaction -> handleSendReaction(action)
|
is RoomDetailAction.SendReaction -> handleSendReaction(action)
|
||||||
is RoomDetailAction.AcceptInvite -> handleAcceptInvite()
|
is RoomDetailAction.AcceptInvite -> handleAcceptInvite()
|
||||||
is RoomDetailAction.RejectInvite -> handleRejectInvite()
|
is RoomDetailAction.RejectInvite -> handleRejectInvite()
|
||||||
is RoomDetailAction.RedactAction -> handleRedactEvent(action)
|
is RoomDetailAction.RedactAction -> handleRedactEvent(action)
|
||||||
is RoomDetailAction.UndoReaction -> handleUndoReact(action)
|
is RoomDetailAction.UndoReaction -> handleUndoReact(action)
|
||||||
is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action)
|
is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action)
|
||||||
is RoomDetailAction.ExitSpecialMode -> handleExitSpecialMode(action)
|
is RoomDetailAction.EnterRegularMode -> handleEnterRegularMode(action)
|
||||||
is RoomDetailAction.EnterEditMode -> handleEditAction(action)
|
is RoomDetailAction.EnterEditMode -> handleEditAction(action)
|
||||||
is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action)
|
is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action)
|
||||||
is RoomDetailAction.EnterReplyMode -> handleReplyAction(action)
|
is RoomDetailAction.EnterReplyMode -> handleReplyAction(action)
|
||||||
is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action)
|
is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action)
|
||||||
is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action)
|
is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action)
|
||||||
is RoomDetailAction.HandleTombstoneEvent -> handleTombstoneEvent(action)
|
is RoomDetailAction.HandleTombstoneEvent -> handleTombstoneEvent(action)
|
||||||
is RoomDetailAction.ResendMessage -> handleResendEvent(action)
|
is RoomDetailAction.ResendMessage -> handleResendEvent(action)
|
||||||
is RoomDetailAction.RemoveFailedEcho -> handleRemove(action)
|
is RoomDetailAction.RemoveFailedEcho -> handleRemove(action)
|
||||||
is RoomDetailAction.ClearSendQueue -> handleClearSendQueue()
|
is RoomDetailAction.ClearSendQueue -> handleClearSendQueue()
|
||||||
is RoomDetailAction.ResendAll -> handleResendAll()
|
is RoomDetailAction.ResendAll -> handleResendAll()
|
||||||
is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead()
|
is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead()
|
||||||
is RoomDetailAction.ReportContent -> handleReportContent(action)
|
is RoomDetailAction.ReportContent -> handleReportContent(action)
|
||||||
is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action)
|
is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action)
|
||||||
is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages()
|
is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages()
|
||||||
is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages()
|
is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages()
|
||||||
is RoomDetailAction.ReplyToOptions -> handleReplyToOptions(action)
|
is RoomDetailAction.ReplyToOptions -> handleReplyToOptions(action)
|
||||||
is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action)
|
is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action)
|
||||||
is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action)
|
is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action)
|
||||||
is RoomDetailAction.RequestVerification -> handleRequestVerification(action)
|
is RoomDetailAction.RequestVerification -> handleRequestVerification(action)
|
||||||
is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action)
|
is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action)
|
||||||
is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action)
|
is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action)
|
||||||
is RoomDetailAction.TapOnFailedToDecrypt -> handleTapOnFailedToDecrypt(action)
|
is RoomDetailAction.TapOnFailedToDecrypt -> handleTapOnFailedToDecrypt(action)
|
||||||
is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment()
|
is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment()
|
||||||
is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager()
|
is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager()
|
||||||
is RoomDetailAction.StartCall -> handleStartCall(action)
|
is RoomDetailAction.StartCall -> handleStartCall(action)
|
||||||
is RoomDetailAction.EndCall -> handleEndCall()
|
is RoomDetailAction.EndCall -> handleEndCall()
|
||||||
is RoomDetailAction.ManageIntegrations -> handleManageIntegrations()
|
is RoomDetailAction.ManageIntegrations -> handleManageIntegrations()
|
||||||
is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action)
|
is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action)
|
||||||
is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId)
|
is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId)
|
||||||
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
|
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
|
||||||
is RoomDetailAction.CancelSend -> handleCancel(action)
|
is RoomDetailAction.CancelSend -> handleCancel(action)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,47 +451,52 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
/**
|
/**
|
||||||
* Convert a send mode to a draft and save the draft
|
* Convert a send mode to a draft and save the draft
|
||||||
*/
|
*/
|
||||||
private fun handleSaveDraft(action: RoomDetailAction.SaveDraft) {
|
private fun handleSaveDraft(action: RoomDetailAction.SaveDraft) = withState {
|
||||||
withState {
|
when {
|
||||||
when (it.sendMode) {
|
it.sendMode is SendMode.REGULAR && !it.sendMode.fromSharing -> {
|
||||||
is SendMode.REGULAR -> room.saveDraft(UserDraft.REGULAR(action.draft), NoOpMatrixCallback())
|
setState { copy(sendMode = it.sendMode.copy(action.draft)) }
|
||||||
is SendMode.REPLY -> room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
|
room.saveDraft(UserDraft.REGULAR(action.draft), NoOpMatrixCallback())
|
||||||
is SendMode.QUOTE -> room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
|
}
|
||||||
is SendMode.EDIT -> room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
|
it.sendMode is SendMode.REPLY -> {
|
||||||
}.exhaustive
|
setState { copy(sendMode = it.sendMode.copy(text = action.draft)) }
|
||||||
|
room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
|
||||||
|
}
|
||||||
|
it.sendMode is SendMode.QUOTE -> {
|
||||||
|
setState { copy(sendMode = it.sendMode.copy(text = action.draft)) }
|
||||||
|
room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
|
||||||
|
}
|
||||||
|
it.sendMode is SendMode.EDIT -> {
|
||||||
|
setState { copy(sendMode = it.sendMode.copy(text = action.draft)) }
|
||||||
|
room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeDrafts() {
|
private fun getDraftIfAny() {
|
||||||
room.rx().liveDrafts()
|
val currentDraft = room.getDraft() ?: return
|
||||||
.subscribe {
|
setState {
|
||||||
Timber.d("Draft update --> SetState")
|
copy(
|
||||||
setState {
|
// Create a sendMode from a draft and retrieve the TimelineEvent
|
||||||
val draft = it.lastOrNull() ?: UserDraft.REGULAR("")
|
sendMode = when (currentDraft) {
|
||||||
copy(
|
is UserDraft.REGULAR -> SendMode.REGULAR(currentDraft.text, false)
|
||||||
// Create a sendMode from a draft and retrieve the TimelineEvent
|
is UserDraft.QUOTE -> {
|
||||||
sendMode = when (draft) {
|
room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
|
||||||
is UserDraft.REGULAR -> SendMode.REGULAR(draft.text)
|
SendMode.QUOTE(timelineEvent, currentDraft.text)
|
||||||
is UserDraft.QUOTE -> {
|
}
|
||||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
}
|
||||||
SendMode.QUOTE(timelineEvent, draft.text)
|
is UserDraft.REPLY -> {
|
||||||
}
|
room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
|
||||||
}
|
SendMode.REPLY(timelineEvent, currentDraft.text)
|
||||||
is UserDraft.REPLY -> {
|
}
|
||||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
}
|
||||||
SendMode.REPLY(timelineEvent, draft.text)
|
is UserDraft.EDIT -> {
|
||||||
}
|
room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
|
||||||
}
|
SendMode.EDIT(timelineEvent, currentDraft.text)
|
||||||
is UserDraft.EDIT -> {
|
}
|
||||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
}
|
||||||
SendMode.EDIT(timelineEvent, draft.text)
|
} ?: SendMode.REGULAR("", fromSharing = false)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
} ?: SendMode.REGULAR("")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.disposeOnClear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleUserIsTyping(action: RoomDetailAction.UserIsTyping) {
|
private fun handleUserIsTyping(action: RoomDetailAction.UserIsTyping) {
|
||||||
|
@ -533,6 +540,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
// For now always disable when not in developer mode, worker cancellation is not working properly
|
// For now always disable when not in developer mode, worker cancellation is not working properly
|
||||||
timeline.pendingEventCount() > 0 && vectorPreferences.developerMode()
|
timeline.pendingEventCount() > 0 && vectorPreferences.developerMode()
|
||||||
R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
||||||
|
R.id.timeline_setting -> true
|
||||||
|
R.id.invite -> state.canInvite
|
||||||
R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
||||||
R.id.open_matrix_apps -> true
|
R.id.open_matrix_apps -> true
|
||||||
R.id.voice_call,
|
R.id.voice_call,
|
||||||
|
@ -680,7 +689,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
is SendMode.EDIT -> {
|
is SendMode.EDIT -> {
|
||||||
// is original event a reply?
|
// is original event a reply?
|
||||||
val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel<MessageContent>()?.relatesTo?.inReplyTo?.eventId
|
val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel<MessageContent>()?.relatesTo?.inReplyTo?.eventId
|
||||||
?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId
|
?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId
|
||||||
|
@ -706,7 +715,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
||||||
popDraft()
|
popDraft()
|
||||||
}
|
}
|
||||||
is SendMode.QUOTE -> {
|
is SendMode.QUOTE -> {
|
||||||
val messageContent: MessageContent? =
|
val messageContent: MessageContent? =
|
||||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||||
|
@ -729,7 +738,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
||||||
popDraft()
|
popDraft()
|
||||||
}
|
}
|
||||||
is SendMode.REPLY -> {
|
is SendMode.REPLY -> {
|
||||||
state.sendMode.timelineEvent.let {
|
state.sendMode.timelineEvent.let {
|
||||||
room.replyToMessage(it, action.text.toString(), action.autoMarkdown)
|
room.replyToMessage(it, action.text.toString(), action.autoMarkdown)
|
||||||
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
||||||
|
@ -740,8 +749,15 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun popDraft() {
|
private fun popDraft() = withState {
|
||||||
room.deleteDraft(NoOpMatrixCallback())
|
if (it.sendMode is SendMode.REGULAR && it.sendMode.fromSharing) {
|
||||||
|
// If we were sharing, we want to get back our last value from draft
|
||||||
|
getDraftIfAny()
|
||||||
|
} else {
|
||||||
|
// Otherwise we clear the composer and remove the draft from db
|
||||||
|
setState { copy(sendMode = SendMode.REGULAR("", false)) }
|
||||||
|
room.deleteDraft(NoOpMatrixCallback())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleJoinToAnotherRoomSlashCommand(command: ParsedCommand.JoinRoom) {
|
private fun handleJoinToAnotherRoomSlashCommand(command: ParsedCommand.JoinRoom) {
|
||||||
|
@ -915,74 +931,25 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEditAction(action: RoomDetailAction.EnterEditMode) {
|
private fun handleEditAction(action: RoomDetailAction.EnterEditMode) {
|
||||||
saveCurrentDraft(action.text)
|
|
||||||
|
|
||||||
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
||||||
setState { copy(sendMode = SendMode.EDIT(timelineEvent, action.text)) }
|
setState { copy(sendMode = SendMode.EDIT(timelineEvent, timelineEvent.getTextEditableContent() ?: "")) }
|
||||||
timelineEvent.root.eventId?.let {
|
|
||||||
room.saveDraft(UserDraft.EDIT(it, timelineEvent.getTextEditableContent() ?: ""), NoOpMatrixCallback())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleQuoteAction(action: RoomDetailAction.EnterQuoteMode) {
|
private fun handleQuoteAction(action: RoomDetailAction.EnterQuoteMode) {
|
||||||
saveCurrentDraft(action.text)
|
|
||||||
|
|
||||||
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
||||||
setState { copy(sendMode = SendMode.QUOTE(timelineEvent, action.text)) }
|
setState { copy(sendMode = SendMode.QUOTE(timelineEvent, action.text)) }
|
||||||
withState { state ->
|
|
||||||
// Save a new draft and keep the previously entered text, if it was not an edit
|
|
||||||
timelineEvent.root.eventId?.let {
|
|
||||||
if (state.sendMode is SendMode.EDIT) {
|
|
||||||
room.saveDraft(UserDraft.QUOTE(it, ""), NoOpMatrixCallback())
|
|
||||||
} else {
|
|
||||||
room.saveDraft(UserDraft.QUOTE(it, action.text), NoOpMatrixCallback())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleReplyAction(action: RoomDetailAction.EnterReplyMode) {
|
private fun handleReplyAction(action: RoomDetailAction.EnterReplyMode) {
|
||||||
saveCurrentDraft(action.text)
|
|
||||||
|
|
||||||
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
||||||
setState { copy(sendMode = SendMode.REPLY(timelineEvent, action.text)) }
|
setState { copy(sendMode = SendMode.REPLY(timelineEvent, action.text)) }
|
||||||
withState { state ->
|
|
||||||
// Save a new draft and keep the previously entered text, if it was not an edit
|
|
||||||
timelineEvent.root.eventId?.let {
|
|
||||||
if (state.sendMode is SendMode.EDIT) {
|
|
||||||
room.saveDraft(UserDraft.REPLY(it, ""), NoOpMatrixCallback())
|
|
||||||
} else {
|
|
||||||
room.saveDraft(UserDraft.REPLY(it, action.text), NoOpMatrixCallback())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveCurrentDraft(draft: String) {
|
private fun handleEnterRegularMode(action: RoomDetailAction.EnterRegularMode) = setState {
|
||||||
// Save the draft with the current text if any
|
copy(sendMode = SendMode.REGULAR(action.text, action.fromSharing))
|
||||||
withState {
|
|
||||||
if (draft.isNotBlank()) {
|
|
||||||
when (it.sendMode) {
|
|
||||||
is SendMode.REGULAR -> room.saveDraft(UserDraft.REGULAR(draft), NoOpMatrixCallback())
|
|
||||||
is SendMode.REPLY -> room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, draft), NoOpMatrixCallback())
|
|
||||||
is SendMode.QUOTE -> room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, draft), NoOpMatrixCallback())
|
|
||||||
is SendMode.EDIT -> room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, draft), NoOpMatrixCallback())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleExitSpecialMode(action: RoomDetailAction.ExitSpecialMode) = withState {
|
|
||||||
if (it.sendMode is SendMode.EDIT) {
|
|
||||||
room.deleteDraft(NoOpMatrixCallback())
|
|
||||||
} else {
|
|
||||||
// Save a new draft and keep the previously entered text
|
|
||||||
room.saveDraft(UserDraft.REGULAR(action.text), NoOpMatrixCallback())
|
|
||||||
}
|
|
||||||
setState { copy(sendMode = SendMode.REGULAR(action.text)) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleOpenOrDownloadFile(action: RoomDetailAction.DownloadOrOpen) {
|
private fun handleOpenOrDownloadFile(action: RoomDetailAction.DownloadOrOpen) {
|
||||||
|
|
|
@ -37,7 +37,12 @@ import org.matrix.android.sdk.api.session.widgets.model.Widget
|
||||||
* Depending on the state the bottom toolbar will change (icons/preview/actions...)
|
* Depending on the state the bottom toolbar will change (icons/preview/actions...)
|
||||||
*/
|
*/
|
||||||
sealed class SendMode(open val text: String) {
|
sealed class SendMode(open val text: String) {
|
||||||
data class REGULAR(override val text: String) : SendMode(text)
|
data class REGULAR(
|
||||||
|
override val text: String,
|
||||||
|
val fromSharing: Boolean,
|
||||||
|
// This is necessary for forcing refresh on selectSubscribe
|
||||||
|
private val ts: Long = System.currentTimeMillis()
|
||||||
|
) : SendMode(text)
|
||||||
data class QUOTE(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text)
|
data class QUOTE(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text)
|
||||||
data class EDIT(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text)
|
data class EDIT(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text)
|
||||||
data class REPLY(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text)
|
data class REPLY(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text)
|
||||||
|
@ -58,7 +63,7 @@ data class RoomDetailViewState(
|
||||||
val asyncRoomSummary: Async<RoomSummary> = Uninitialized,
|
val asyncRoomSummary: Async<RoomSummary> = Uninitialized,
|
||||||
val activeRoomWidgets: Async<List<Widget>> = Uninitialized,
|
val activeRoomWidgets: Async<List<Widget>> = Uninitialized,
|
||||||
val typingMessage: String? = null,
|
val typingMessage: String? = null,
|
||||||
val sendMode: SendMode = SendMode.REGULAR(""),
|
val sendMode: SendMode = SendMode.REGULAR("", false),
|
||||||
val tombstoneEvent: Event? = null,
|
val tombstoneEvent: Event? = null,
|
||||||
val tombstoneEventHandling: Async<String> = Uninitialized,
|
val tombstoneEventHandling: Async<String> = Uninitialized,
|
||||||
val syncState: SyncState = SyncState.Idle,
|
val syncState: SyncState = SyncState.Idle,
|
||||||
|
@ -67,6 +72,7 @@ data class RoomDetailViewState(
|
||||||
val canShowJumpToReadMarker: Boolean = true,
|
val canShowJumpToReadMarker: Boolean = true,
|
||||||
val changeMembershipState: ChangeMembershipState = ChangeMembershipState.Unknown,
|
val changeMembershipState: ChangeMembershipState = ChangeMembershipState.Unknown,
|
||||||
val canSendMessage: Boolean = true,
|
val canSendMessage: Boolean = true,
|
||||||
|
val canInvite: Boolean = true,
|
||||||
val isAllowedToManageWidgets: Boolean = false,
|
val isAllowedToManageWidgets: Boolean = false,
|
||||||
val isAllowedToStartWebRTCCall: Boolean = true
|
val isAllowedToStartWebRTCCall: Boolean = true
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
|
@ -25,12 +25,15 @@ import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_SWIPE
|
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_SWIPE
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
import com.airbnb.epoxy.EpoxyTouchHelperCallback
|
import com.airbnb.epoxy.EpoxyTouchHelperCallback
|
||||||
import com.airbnb.epoxy.EpoxyViewHolder
|
import com.airbnb.epoxy.EpoxyViewHolder
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.features.themes.ThemeUtils
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
@ -52,7 +55,16 @@ class RoomMessageTouchHelperCallback(private val context: Context,
|
||||||
private var replyButtonProgress: Float = 0F
|
private var replyButtonProgress: Float = 0F
|
||||||
private var lastReplyButtonAnimationTime: Long = 0
|
private var lastReplyButtonAnimationTime: Long = 0
|
||||||
|
|
||||||
private var imageDrawable: Drawable = ContextCompat.getDrawable(context, actionIcon)!!
|
private val imageDrawable: Drawable = DrawableCompat.wrap(
|
||||||
|
ContextCompat.getDrawable(context, actionIcon)!!
|
||||||
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
DrawableCompat.setTint(
|
||||||
|
imageDrawable,
|
||||||
|
ThemeUtils.getColor(context, R.attr.riotx_text_primary)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private val triggerDistance = convertToPx(100)
|
private val triggerDistance = convertToPx(100)
|
||||||
private val minShowDistance = convertToPx(20)
|
private val minShowDistance = convertToPx(20)
|
||||||
|
|
|
@ -259,8 +259,7 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() {
|
||||||
findPreference<VectorPreference>("SETTINGS_SIGN_OUT_KEY")!!
|
findPreference<VectorPreference>("SETTINGS_SIGN_OUT_KEY")!!
|
||||||
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
activity?.let {
|
activity?.let {
|
||||||
SignOutUiWorker(requireActivity())
|
SignOutUiWorker(requireActivity()).perform()
|
||||||
.perform(requireContext())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
|
|
@ -31,7 +31,7 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.setTextOrHide
|
import im.vector.app.core.extensions.setTextOrHide
|
||||||
import im.vector.app.features.themes.ThemeUtils
|
import im.vector.app.features.themes.ThemeUtils
|
||||||
|
|
||||||
class SignoutBottomSheetActionButton @JvmOverloads constructor(
|
class SignOutBottomSheetActionButton @JvmOverloads constructor(
|
||||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
@ -80,11 +80,11 @@ class SignoutBottomSheetActionButton @JvmOverloads constructor(
|
||||||
inflate(context, R.layout.item_signout_action, this)
|
inflate(context, R.layout.item_signout_action, this)
|
||||||
ButterKnife.bind(this)
|
ButterKnife.bind(this)
|
||||||
|
|
||||||
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.SignoutBottomSheetActionButton, 0, 0)
|
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.SignOutBottomSheetActionButton, 0, 0)
|
||||||
title = typedArray.getString(R.styleable.SignoutBottomSheetActionButton_actionTitle) ?: ""
|
title = typedArray.getString(R.styleable.SignOutBottomSheetActionButton_actionTitle) ?: ""
|
||||||
leftIcon = typedArray.getDrawable(R.styleable.SignoutBottomSheetActionButton_leftIcon)
|
leftIcon = typedArray.getDrawable(R.styleable.SignOutBottomSheetActionButton_leftIcon)
|
||||||
tint = typedArray.getColor(R.styleable.SignoutBottomSheetActionButton_iconTint, ThemeUtils.getColor(context, android.R.attr.textColor))
|
tint = typedArray.getColor(R.styleable.SignOutBottomSheetActionButton_iconTint, ThemeUtils.getColor(context, android.R.attr.textColor))
|
||||||
textColor = typedArray.getColor(R.styleable.SignoutBottomSheetActionButton_textColor, ThemeUtils.getColor(context, android.R.attr.textColor))
|
textColor = typedArray.getColor(R.styleable.SignOutBottomSheetActionButton_textColor, ThemeUtils.getColor(context, android.R.attr.textColor))
|
||||||
|
|
||||||
typedArray.recycle()
|
typedArray.recycle()
|
||||||
|
|
|
@ -35,8 +35,6 @@ import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
|
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ScreenComponent
|
import im.vector.app.core.di.ScreenComponent
|
||||||
import im.vector.app.core.dialogs.ExportKeysDialog
|
import im.vector.app.core.dialogs.ExportKeysDialog
|
||||||
|
@ -45,6 +43,9 @@ import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity
|
import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity
|
||||||
import im.vector.app.features.crypto.recover.BootstrapBottomSheet
|
import im.vector.app.features.crypto.recover.BootstrapBottomSheet
|
||||||
import im.vector.app.features.crypto.recover.SetupMode
|
import im.vector.app.features.crypto.recover.SetupMode
|
||||||
|
import kotlinx.android.synthetic.main.bottom_sheet_logout_and_backup.*
|
||||||
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -57,21 +58,6 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(),
|
||||||
@BindView(R.id.bottom_sheet_signout_backingup_status_group)
|
@BindView(R.id.bottom_sheet_signout_backingup_status_group)
|
||||||
lateinit var backingUpStatusGroup: ViewGroup
|
lateinit var backingUpStatusGroup: ViewGroup
|
||||||
|
|
||||||
@BindView(R.id.setupRecoveryButton)
|
|
||||||
lateinit var setupRecoveryButton: SignoutBottomSheetActionButton
|
|
||||||
|
|
||||||
@BindView(R.id.setupMegolmBackupButton)
|
|
||||||
lateinit var setupMegolmBackupButton: SignoutBottomSheetActionButton
|
|
||||||
|
|
||||||
@BindView(R.id.exportManuallyButton)
|
|
||||||
lateinit var exportManuallyButton: SignoutBottomSheetActionButton
|
|
||||||
|
|
||||||
@BindView(R.id.exitAnywayButton)
|
|
||||||
lateinit var exitAnywayButton: SignoutBottomSheetActionButton
|
|
||||||
|
|
||||||
@BindView(R.id.signOutButton)
|
|
||||||
lateinit var signOutButton: SignoutBottomSheetActionButton
|
|
||||||
|
|
||||||
@BindView(R.id.bottom_sheet_signout_icon_progress_bar)
|
@BindView(R.id.bottom_sheet_signout_icon_progress_bar)
|
||||||
lateinit var backupProgress: ProgressBar
|
lateinit var backupProgress: ProgressBar
|
||||||
|
|
||||||
|
@ -138,6 +124,10 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signOutButton.action = {
|
||||||
|
onSignOut?.run()
|
||||||
|
}
|
||||||
|
|
||||||
exportManuallyButton.action = {
|
exportManuallyButton.action = {
|
||||||
withState(viewModel) { state ->
|
withState(viewModel) { state ->
|
||||||
queryExportKeys(state.userId, QUERY_EXPORT_KEYS)
|
queryExportKeys(state.userId, QUERY_EXPORT_KEYS)
|
||||||
|
@ -182,10 +172,10 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(),
|
||||||
// we should show option to setup 4S
|
// we should show option to setup 4S
|
||||||
setupRecoveryButton.isVisible = true
|
setupRecoveryButton.isVisible = true
|
||||||
setupMegolmBackupButton.isVisible = false
|
setupMegolmBackupButton.isVisible = false
|
||||||
signOutButton.isVisible = false
|
|
||||||
// We let the option to ignore and quit
|
// We let the option to ignore and quit
|
||||||
exportManuallyButton.isVisible = true
|
exportManuallyButton.isVisible = true
|
||||||
exitAnywayButton.isVisible = true
|
exitAnywayButton.isVisible = true
|
||||||
|
signOutButton.isVisible = false
|
||||||
} else if (state.keysBackupState == KeysBackupState.Unknown || state.keysBackupState == KeysBackupState.Disabled) {
|
} else if (state.keysBackupState == KeysBackupState.Unknown || state.keysBackupState == KeysBackupState.Disabled) {
|
||||||
sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_no_backup)
|
sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_no_backup)
|
||||||
backingUpStatusGroup.isVisible = false
|
backingUpStatusGroup.isVisible = false
|
||||||
|
@ -194,10 +184,10 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(),
|
||||||
// we should show option to setup 4S
|
// we should show option to setup 4S
|
||||||
setupRecoveryButton.isVisible = false
|
setupRecoveryButton.isVisible = false
|
||||||
setupMegolmBackupButton.isVisible = true
|
setupMegolmBackupButton.isVisible = true
|
||||||
signOutButton.isVisible = false
|
|
||||||
// We let the option to ignore and quit
|
// We let the option to ignore and quit
|
||||||
exportManuallyButton.isVisible = true
|
exportManuallyButton.isVisible = true
|
||||||
exitAnywayButton.isVisible = true
|
exitAnywayButton.isVisible = true
|
||||||
|
signOutButton.isVisible = false
|
||||||
} else {
|
} else {
|
||||||
// so keybackup is setup
|
// so keybackup is setup
|
||||||
// You should wait until all are uploaded
|
// You should wait until all are uploaded
|
||||||
|
@ -213,13 +203,14 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(),
|
||||||
backupCompleteImage.isVisible = true
|
backupCompleteImage.isVisible = true
|
||||||
backupStatusTex.text = getString(R.string.keys_backup_info_keys_all_backup_up)
|
backupStatusTex.text = getString(R.string.keys_backup_info_keys_all_backup_up)
|
||||||
|
|
||||||
hideViews(setupMegolmBackupButton, exportManuallyButton, exitAnywayButton)
|
setupMegolmBackupButton.isVisible = false
|
||||||
|
exportManuallyButton.isVisible = false
|
||||||
|
exitAnywayButton.isVisible = false
|
||||||
// You can signout
|
// You can signout
|
||||||
signOutButton.isVisible = true
|
signOutButton.isVisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
KeysBackupState.WillBackUp,
|
KeysBackupState.WillBackUp,
|
||||||
KeysBackupState.BackingUp -> {
|
KeysBackupState.BackingUp -> {
|
||||||
sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_backing_up)
|
sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_backing_up)
|
||||||
|
|
||||||
// save in progress
|
// save in progress
|
||||||
|
@ -228,18 +219,21 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(),
|
||||||
backupCompleteImage.isVisible = false
|
backupCompleteImage.isVisible = false
|
||||||
backupStatusTex.text = getString(R.string.sign_out_bottom_sheet_backing_up_keys)
|
backupStatusTex.text = getString(R.string.sign_out_bottom_sheet_backing_up_keys)
|
||||||
|
|
||||||
hideViews(setupMegolmBackupButton, setupMegolmBackupButton, signOutButton, exportManuallyButton)
|
setupMegolmBackupButton.isVisible = false
|
||||||
|
exportManuallyButton.isVisible = false
|
||||||
exitAnywayButton.isVisible = true
|
exitAnywayButton.isVisible = true
|
||||||
|
signOutButton.isVisible = false
|
||||||
}
|
}
|
||||||
KeysBackupState.NotTrusted -> {
|
KeysBackupState.NotTrusted -> {
|
||||||
sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_backup_not_active)
|
sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_backup_not_active)
|
||||||
// It's not trusted and we know there are unsaved keys..
|
// It's not trusted and we know there are unsaved keys..
|
||||||
backingUpStatusGroup.isVisible = false
|
backingUpStatusGroup.isVisible = false
|
||||||
|
|
||||||
exportManuallyButton.isVisible = true
|
|
||||||
// option to enter pass/key
|
// option to enter pass/key
|
||||||
setupMegolmBackupButton.isVisible = true
|
setupMegolmBackupButton.isVisible = true
|
||||||
|
exportManuallyButton.isVisible = true
|
||||||
exitAnywayButton.isVisible = true
|
exitAnywayButton.isVisible = true
|
||||||
|
signOutButton.isVisible = false
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// mmm.. strange state
|
// mmm.. strange state
|
||||||
|
@ -253,21 +247,23 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(),
|
||||||
when (state.hasBeenExportedToFile) {
|
when (state.hasBeenExportedToFile) {
|
||||||
is Loading -> {
|
is Loading -> {
|
||||||
signoutExportingLoading.isVisible = true
|
signoutExportingLoading.isVisible = true
|
||||||
hideViews(setupRecoveryButton,
|
backingUpStatusGroup.isVisible = false
|
||||||
setupMegolmBackupButton,
|
|
||||||
exportManuallyButton,
|
setupRecoveryButton.isVisible = false
|
||||||
backingUpStatusGroup,
|
setupMegolmBackupButton.isVisible = false
|
||||||
signOutButton)
|
exportManuallyButton.isVisible = false
|
||||||
exitAnywayButton.isVisible = true
|
exitAnywayButton.isVisible = true
|
||||||
|
signOutButton.isVisible = false
|
||||||
}
|
}
|
||||||
is Success -> {
|
is Success -> {
|
||||||
if (state.hasBeenExportedToFile.invoke()) {
|
if (state.hasBeenExportedToFile.invoke()) {
|
||||||
sheetTitle.text = getString(R.string.action_sign_out_confirmation_simple)
|
sheetTitle.text = getString(R.string.action_sign_out_confirmation_simple)
|
||||||
hideViews(setupRecoveryButton,
|
backingUpStatusGroup.isVisible = false
|
||||||
setupMegolmBackupButton,
|
|
||||||
exportManuallyButton,
|
setupRecoveryButton.isVisible = false
|
||||||
backingUpStatusGroup,
|
setupMegolmBackupButton.isVisible = false
|
||||||
exitAnywayButton)
|
exportManuallyButton.isVisible = false
|
||||||
|
exitAnywayButton.isVisible = false
|
||||||
signOutButton.isVisible = true
|
signOutButton.isVisible = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -315,8 +311,4 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hideViews(vararg views: View) {
|
|
||||||
views.forEach { it.isVisible = false }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,9 @@
|
||||||
|
|
||||||
package im.vector.app.features.workers.signout
|
package im.vector.app.features.workers.signout
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
|
||||||
import im.vector.app.core.extensions.cannotLogoutSafely
|
import im.vector.app.core.extensions.cannotLogoutSafely
|
||||||
import im.vector.app.core.extensions.vectorComponent
|
import im.vector.app.core.extensions.vectorComponent
|
||||||
import im.vector.app.features.MainActivity
|
import im.vector.app.features.MainActivity
|
||||||
|
@ -28,11 +26,8 @@ import im.vector.app.features.MainActivityArgs
|
||||||
|
|
||||||
class SignOutUiWorker(private val activity: FragmentActivity) {
|
class SignOutUiWorker(private val activity: FragmentActivity) {
|
||||||
|
|
||||||
lateinit var activeSessionHolder: ActiveSessionHolder
|
fun perform() {
|
||||||
|
val session = activity.vectorComponent().activeSessionHolder().getSafeActiveSession() ?: return
|
||||||
fun perform(context: Context) {
|
|
||||||
activeSessionHolder = context.vectorComponent().activeSessionHolder()
|
|
||||||
val session = activeSessionHolder.getActiveSession()
|
|
||||||
if (session.cannotLogoutSafely()) {
|
if (session.cannotLogoutSafely()) {
|
||||||
// The backup check on logout flow has to be displayed if there are keys in the store, and the keys backup state is not Ready
|
// The backup check on logout flow has to be displayed if there are keys in the store, and the keys backup state is not Ready
|
||||||
val signOutDialog = SignOutBottomSheetDialogFragment.newInstance()
|
val signOutDialog = SignOutBottomSheetDialogFragment.newInstance()
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="18dp"
|
||||||
|
android:height="18dp"
|
||||||
|
android:viewportWidth="18"
|
||||||
|
android:viewportHeight="18">
|
||||||
|
<path
|
||||||
|
android:pathData="M14.89,6.83C15.05,7.25 15.46,7.53 15.91,7.53C16.51,7.53 17,8.02 17,8.62V9.38C17,9.98 16.51,10.47 15.91,10.47C15.46,10.47 15.05,10.75 14.89,11.17C14.8768,11.2031 14.8635,11.2366 14.8502,11.2704C14.8031,11.3901 14.7546,11.5131 14.7,11.63C14.51,12.04 14.6,12.52 14.92,12.84C15.35,13.26 15.35,13.95 14.92,14.38L14.38,14.92C13.96,15.35 13.27,15.35 12.84,14.92C12.53,14.6 12.04,14.51 11.63,14.7C11.48,14.77 11.33,14.83 11.17,14.89C10.75,15.05 10.47,15.46 10.47,15.91C10.47,16.51 9.98,17 9.38,17H8.62C8.02,17 7.53,16.51 7.53,15.91C7.53,15.46 7.25,15.05 6.83,14.89C6.7969,14.8768 6.7634,14.8635 6.7296,14.8502C6.61,14.8031 6.4869,14.7546 6.37,14.7C5.96,14.51 5.48,14.6 5.16,14.92C4.74,15.35 4.05,15.35 3.62,14.92L3.08,14.38C2.65,13.96 2.65,13.27 3.08,12.84C3.4,12.53 3.49,12.04 3.3,11.63C3.23,11.48 3.17,11.33 3.11,11.17C2.95,10.75 2.54,10.47 2.09,10.47C1.49,10.47 1,9.98 1,9.38V8.62C1,8.02 1.49,7.53 2.09,7.53C2.54,7.53 2.95,7.25 3.11,6.83C3.1416,6.729 3.1811,6.632 3.221,6.534C3.2444,6.4767 3.2679,6.419 3.29,6.36C3.48,5.95 3.39,5.47 3.07,5.15C2.64,4.73 2.64,4.04 3.07,3.61L3.62,3.08C4.04,2.65 4.73,2.65 5.16,3.08C5.47,3.4 5.96,3.49 6.37,3.3C6.52,3.23 6.67,3.16 6.83,3.11C7.25,2.95 7.53,2.54 7.53,2.09C7.53,1.49 8.02,1 8.62,1H9.38C9.98,1 10.47,1.49 10.47,2.09C10.47,2.55 10.75,2.95 11.17,3.11C11.2031,3.1232 11.2366,3.1365 11.2704,3.1498C11.3901,3.1969 11.5131,3.2454 11.63,3.3C12.04,3.49 12.52,3.4 12.84,3.08C13.26,2.65 13.95,2.65 14.38,3.08L14.92,3.62C15.35,4.04 15.35,4.73 14.92,5.16C14.6,5.47 14.51,5.96 14.7,6.37C14.77,6.52 14.83,6.67 14.89,6.83ZM9,13C6.79,13 5,11.21 5,9C5,6.79 6.79,5 9,5C11.21,5 13,6.79 13,9C13,11.21 11.21,13 9,13Z"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,18 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="18dp"
|
||||||
|
android:height="18dp"
|
||||||
|
android:viewportWidth="18"
|
||||||
|
android:viewportHeight="18">
|
||||||
|
<path
|
||||||
|
android:pathData="M9,2L14.8586,1.1631C15.461,1.077 16,1.5445 16,2.153V15.847C16,16.4555 15.461,16.923 14.8586,16.8369L9,16V2Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M3,2L10,2A1,1 0,0 1,11 3L11,15A1,1 0,0 1,10 16L3,16A1,1 0,0 1,2 15L2,3A1,1 0,0 1,3 2z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M3,2L10,2A1,1 0,0 1,11 3L11,15A1,1 0,0 1,10 16L3,16A1,1 0,0 1,2 15L2,3A1,1 0,0 1,3 2z"
|
||||||
|
android:strokeWidth="3"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
|
@ -81,7 +81,7 @@
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<im.vector.app.features.workers.signout.SignoutBottomSheetActionButton
|
<im.vector.app.features.workers.signout.SignOutBottomSheetActionButton
|
||||||
android:id="@+id/setupRecoveryButton"
|
android:id="@+id/setupRecoveryButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -91,7 +91,7 @@
|
||||||
app:textColor="?riotx_text_secondary" />
|
app:textColor="?riotx_text_secondary" />
|
||||||
|
|
||||||
|
|
||||||
<im.vector.app.features.workers.signout.SignoutBottomSheetActionButton
|
<im.vector.app.features.workers.signout.SignOutBottomSheetActionButton
|
||||||
android:id="@+id/setupMegolmBackupButton"
|
android:id="@+id/setupMegolmBackupButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -100,7 +100,7 @@
|
||||||
app:leftIcon="@drawable/backup_keys"
|
app:leftIcon="@drawable/backup_keys"
|
||||||
app:textColor="?riotx_text_secondary" />
|
app:textColor="?riotx_text_secondary" />
|
||||||
|
|
||||||
<im.vector.app.features.workers.signout.SignoutBottomSheetActionButton
|
<im.vector.app.features.workers.signout.SignOutBottomSheetActionButton
|
||||||
android:id="@+id/exportManuallyButton"
|
android:id="@+id/exportManuallyButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
app:leftIcon="@drawable/ic_download"
|
app:leftIcon="@drawable/ic_download"
|
||||||
app:textColor="?riotx_text_secondary" />
|
app:textColor="?riotx_text_secondary" />
|
||||||
|
|
||||||
<im.vector.app.features.workers.signout.SignoutBottomSheetActionButton
|
<im.vector.app.features.workers.signout.SignOutBottomSheetActionButton
|
||||||
android:id="@+id/exitAnywayButton"
|
android:id="@+id/exitAnywayButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -118,7 +118,7 @@
|
||||||
app:leftIcon="@drawable/ic_material_leave"
|
app:leftIcon="@drawable/ic_material_leave"
|
||||||
app:textColor="@color/riotx_destructive_accent" />
|
app:textColor="@color/riotx_destructive_accent" />
|
||||||
|
|
||||||
<im.vector.app.features.workers.signout.SignoutBottomSheetActionButton
|
<im.vector.app.features.workers.signout.SignOutBottomSheetActionButton
|
||||||
android:id="@+id/signOutButton"
|
android:id="@+id/signOutButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:background="?riotx_background"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true">
|
android:focusable="true">
|
||||||
|
|
||||||
|
@ -42,11 +43,12 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="?riotx_text_primary"
|
android:textColor="?riotx_text_primary"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
app:layout_constraintEnd_toStartOf="@id/homeDrawerHeaderSettingsView"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="@+id/homeDrawerHeaderAvatarView"
|
app:layout_constraintStart_toStartOf="@+id/homeDrawerHeaderAvatarView"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/homeDrawerHeaderAvatarView"
|
app:layout_constraintTop_toBottomOf="@+id/homeDrawerHeaderAvatarView"
|
||||||
tools:text="@sample/matrix.json/data/displayName" />
|
tools:text="@sample/matrix.json/data/displayName" />
|
||||||
|
@ -55,39 +57,71 @@
|
||||||
android:id="@+id/homeDrawerUserIdView"
|
android:id="@+id/homeDrawerUserIdView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||||
android:layout_marginBottom="17dp"
|
android:layout_marginBottom="17dp"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="?riotx_text_secondary"
|
android:textColor="?riotx_text_secondary"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/homeDrawerHeaderSettingsView"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="@+id/homeDrawerHeaderAvatarView"
|
app:layout_constraintStart_toStartOf="@+id/homeDrawerHeaderAvatarView"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/homeDrawerUsernameView"
|
app:layout_constraintTop_toBottomOf="@+id/homeDrawerUsernameView"
|
||||||
tools:text="@sample/matrix.json/data/mxid" />
|
tools:text="@sample/matrix.json/data/mxid" />
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/homeDrawerHeaderSettingsView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/selectableItemBackground"
|
|
||||||
android:contentDescription="@string/room_sliding_menu_settings"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:src="@drawable/ic_settings_x"
|
|
||||||
android:tint="?riotx_text_secondary"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/homeDrawerGroupListContainer"
|
android:id="@+id/homeDrawerGroupListContainer"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:background="?riotx_background"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/homeDrawerBottomSeparator"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/homeDrawerHeader" />
|
app:layout_constraintTop_toBottomOf="@+id/homeDrawerHeader" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/homeDrawerBottomSeparator"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?attr/vctr_list_divider_color"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/homeDrawerHeaderSettingsView" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/homeDrawerHeaderSettingsView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:drawablePadding="9dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minWidth="120dp"
|
||||||
|
android:minHeight="52dp"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:text="@string/settings"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:tint="?riotx_android_secondary"
|
||||||
|
app:drawableStartCompat="@drawable/ic_settings_18dp"
|
||||||
|
app:drawableTint="?riotx_android_secondary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/homeDrawerHeaderSignoutView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:drawablePadding="9dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minWidth="120dp"
|
||||||
|
android:minHeight="52dp"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:text="@string/logout"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:tint="?riotx_android_secondary"
|
||||||
|
app:drawableStartCompat="@drawable/ic_signout_18dp"
|
||||||
|
app:drawableTint="?riotx_android_secondary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -105,6 +105,7 @@
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:src="@drawable/ic_edit"
|
android:src="@drawable/ic_edit"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
android:tint="?riotx_text_primary"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/roomNameView"
|
app:layout_constraintBottom_toBottomOf="@+id/roomNameView"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/roomUnreadCounterBadgeView"
|
app:layout_constraintEnd_toStartOf="@+id/roomUnreadCounterBadgeView"
|
||||||
app:layout_constraintStart_toEndOf="@+id/roomNameView"
|
app:layout_constraintStart_toEndOf="@+id/roomNameView"
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_home_setting"
|
||||||
|
android:icon="@drawable/ic_settings_x"
|
||||||
|
android:title="@string/settings"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_home_suggestion"
|
android:id="@+id/menu_home_suggestion"
|
||||||
android:icon="@drawable/ic_material_bug_report"
|
android:icon="@drawable/ic_material_bug_report"
|
||||||
|
@ -15,8 +21,8 @@
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_home_filter"
|
android:id="@+id/menu_home_filter"
|
||||||
android:icon="@drawable/ic_search"
|
android:icon="@drawable/ic_search"
|
||||||
app:iconTint="?riotx_text_secondary"
|
|
||||||
android:title="@string/home_filter_placeholder_home"
|
android:title="@string/home_filter_placeholder_home"
|
||||||
|
app:iconTint="?riotx_text_secondary"
|
||||||
app:showAsAction="always" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
|
@ -3,11 +3,22 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/timeline_setting"
|
||||||
|
android:icon="@drawable/ic_settings_x"
|
||||||
|
android:title="@string/settings"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/search"
|
android:id="@+id/search"
|
||||||
android:title="@string/search"
|
android:title="@string/search"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/invite"
|
||||||
|
android:title="@string/invite"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/video_call"
|
android:id="@+id/video_call"
|
||||||
android:icon="@drawable/ic_video"
|
android:icon="@drawable/ic_video"
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
<attr name="forceStartPadding" format="boolean" />
|
<attr name="forceStartPadding" format="boolean" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="SignoutBottomSheetActionButton">
|
<declare-styleable name="SignOutBottomSheetActionButton">
|
||||||
<attr name="iconTint" format="color" />
|
<attr name="iconTint" format="color" />
|
||||||
<attr name="actionTitle"/>
|
<attr name="actionTitle"/>
|
||||||
<attr name="leftIcon" />
|
<attr name="leftIcon" />
|
||||||
|
|
Loading…
Reference in New Issue