diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt index dda5f611b6..4aeb4f973a 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt @@ -39,7 +39,7 @@ sealed class RoomDetailActions { object AcceptInvite : RoomDetailActions() object RejectInvite : RoomDetailActions() - data class EnterEditMode(val eventId: String) : RoomDetailActions() + data class EnterEditMode(val eventId: String, val draft: String) : RoomDetailActions() data class EnterQuoteMode(val eventId: String, val draft: String) : RoomDetailActions() data class EnterReplyMode(val eventId: String, val draft: String) : RoomDetailActions() data class ExitSpecialMode(val draft: String) : RoomDetailActions() diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index b32d08ea7d..7bc5cf7016 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -53,7 +53,6 @@ import com.google.android.material.snackbar.Snackbar import com.jaiselrahman.filepicker.activity.FilePickerActivity import com.jaiselrahman.filepicker.config.Configurations import com.jaiselrahman.filepicker.model.MediaFile -import com.jakewharton.rxbinding3.widget.afterTextChangeEvents import com.otaliastudios.autocomplete.Autocomplete import com.otaliastudios.autocomplete.AutocompleteCallback import com.otaliastudios.autocomplete.CharPolicy @@ -107,7 +106,6 @@ import im.vector.riotx.features.notifications.NotificationDrawerManager import im.vector.riotx.features.reactions.EmojiReactionPickerActivity import im.vector.riotx.features.settings.VectorPreferences import im.vector.riotx.features.themes.ThemeUtils -import io.reactivex.rxkotlin.subscribeBy import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_detail.* import kotlinx.android.synthetic.main.merge_composer_layout.view.* @@ -115,7 +113,6 @@ import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* import org.commonmark.parser.Parser import timber.log.Timber import java.io.File -import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -189,7 +186,6 @@ class RoomDetailFragment : @Inject lateinit var eventHtmlRenderer: EventHtmlRenderer @Inject lateinit var vectorPreferences: VectorPreferences - override fun getLayoutResId() = R.layout.fragment_room_detail override fun getMenuRes() = R.menu.menu_timeline @@ -199,6 +195,8 @@ class RoomDetailFragment : @BindView(R.id.composerLayout) lateinit var composerLayout: TextComposerView + private var lockSendButton = false + override fun injectWith(injector: ScreenComponent) { injector.inject(this) } @@ -350,7 +348,6 @@ class RoomDetailFragment : // Do not update if this is the same text to avoid the cursor to move if (text != composerLayout.composerEditText.text.toString()) { // Ignore update to avoid saving a draft - filterComposerTextChange = true composerLayout.composerEditText.setText(text) composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text.length) } @@ -366,6 +363,8 @@ class RoomDetailFragment : super.onPause() notificationDrawerManager.setCurrentRoom(null) + + roomDetailViewModel.process(RoomDetailActions.SaveDraft(composerLayout.composerEditText.text.toString())) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -437,21 +436,7 @@ class RoomDetailFragment : } } - private var filterComposerTextChange = true - private fun setupComposer() { - composerLayout.composerEditText.afterTextChangeEvents() - .debounce(100, TimeUnit.MILLISECONDS) - .subscribeBy { - if (filterComposerTextChange) { - Timber.d("Draft: ignore text update") - filterComposerTextChange = false - return@subscribeBy - } - roomDetailViewModel.process(RoomDetailActions.SaveDraft(it.editable.toString())) - } - .disposeOnDestroy() - val elevation = 6f val backgroundDrawable = ColorDrawable(ThemeUtils.getColor(requireContext(), R.attr.riotx_background)) Autocomplete.on(composerLayout.composerEditText) @@ -515,8 +500,13 @@ class RoomDetailFragment : .build() composerLayout.sendButton.setOnClickListener { + if (lockSendButton) { + Timber.w("Send button is locked") + return@setOnClickListener + } val textMessage = composerLayout.composerEditText.text.toString() if (textMessage.isNotBlank()) { + lockSendButton = true roomDetailViewModel.process(RoomDetailActions.SendMessage(textMessage, vectorPreferences.isMarkdownEnabled())) } } @@ -673,11 +663,11 @@ class RoomDetailFragment : private fun renderSendMessageResult(sendMessageResult: SendMessageResult) { when (sendMessageResult) { is SendMessageResult.MessageSent -> { - // Nothing to do, the composer will be cleared with the draft update + updateComposerText("") } is SendMessageResult.SlashCommandHandled -> { sendMessageResult.messageRes?.let { showSnackWithMessage(getString(it)) } - // The composer will be cleared with the draft update + updateComposerText("") } is SendMessageResult.SlashCommandError -> { displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command)) @@ -686,7 +676,7 @@ class RoomDetailFragment : displayCommandError(getString(R.string.unrecognized_command, sendMessageResult.command)) } is SendMessageResult.SlashCommandResultOk -> { - // Ignore + updateComposerText("") } is SendMessageResult.SlashCommandResultError -> { displayCommandError(sendMessageResult.throwable.localizedMessage) @@ -695,6 +685,8 @@ class RoomDetailFragment : displayCommandError(getString(R.string.not_implemented)) } } + + lockSendButton = false } private fun displayCommandError(message: String) { @@ -939,7 +931,7 @@ class RoomDetailFragment : roomDetailViewModel.process(RoomDetailActions.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add)) } is SimpleAction.Edit -> { - roomDetailViewModel.process(RoomDetailActions.EnterEditMode(action.eventId)) + roomDetailViewModel.process(RoomDetailActions.EnterEditMode(action.eventId, composerLayout.composerEditText.text.toString())) } is SimpleAction.Quote -> { roomDetailViewModel.process(RoomDetailActions.EnterQuoteMode(action.eventId, composerLayout.composerEditText.text.toString())) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 593a3dfd04..80f333a76e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -85,9 +85,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro private var timeline = room.createTimeline(eventId, timelineSettings) - // Filter to avoid infinite loop when user enter text in the composer and call SaveDraft - private var filterDraftUpdate = false - // Slot to keep a pending action during permission request var pendingAction: RoomDetailActions? = null @@ -151,9 +148,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro * Convert a send mode to a draft and save the draft */ private fun handleSaveDraft(action: RoomDetailActions.SaveDraft) { - // The text is changed, ignore the next update from DB - filterDraftUpdate = true - withState { when (it.sendMode) { is SendMode.REGULAR -> room.saveDraft(UserDraft.REGULAR(action.draft)) @@ -167,14 +161,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro private fun observeDrafts() { room.rx().liveDrafts() .subscribe { - Timber.d("Draft update!") - if (filterDraftUpdate) { - Timber.d(" --> Ignore") - return@subscribe - } - - Timber.d(" --> SetState") - + Timber.d("Draft update --> SetState") setState { val draft = it.lastOrNull() ?: UserDraft.REGULAR("") copy( @@ -337,7 +324,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } } is SendMode.EDIT -> { - //is original event a reply? val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel()?.relatesTo?.inReplyTo?.eventId ?: state.sendMode.timelineEvent.root.content.toModel()?.relatesTo?.inReplyTo?.eventId @@ -396,7 +382,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } private fun popDraft() { - filterDraftUpdate = false room.deleteDraft() } @@ -513,20 +498,22 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } private fun handleEditAction(action: RoomDetailActions.EnterEditMode) { + saveCurrentDraft(action.draft) + room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> timelineEvent.root.eventId?.let { - filterDraftUpdate = false room.saveDraft(UserDraft.EDIT(it, timelineEvent.getTextEditableContent() ?: "")) } } } private fun handleQuoteAction(action: RoomDetailActions.EnterQuoteMode) { + saveCurrentDraft(action.draft) + room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> withState { state -> // Save a new draft and keep the previously entered text, if it was not an edit timelineEvent.root.eventId?.let { - filterDraftUpdate = false if (state.sendMode is SendMode.EDIT) { room.saveDraft(UserDraft.QUOTE(it, "")) } else { @@ -538,11 +525,12 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } private fun handleReplyAction(action: RoomDetailActions.EnterReplyMode) { + saveCurrentDraft(action.draft) + room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> withState { state -> // Save a new draft and keep the previously entered text, if it was not an edit timelineEvent.root.eventId?.let { - filterDraftUpdate = false if (state.sendMode is SendMode.EDIT) { room.saveDraft(UserDraft.REPLY(it, "")) } else { @@ -553,11 +541,23 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } } + private fun saveCurrentDraft(draft: String) { + // Save the draft with the current text if any + withState { + if (draft.isNotBlank()) { + when (it.sendMode) { + is SendMode.REGULAR -> room.saveDraft(UserDraft.REGULAR(draft)) + is SendMode.REPLY -> room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, draft)) + is SendMode.QUOTE -> room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, draft)) + is SendMode.EDIT -> room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, draft)) + } + } + } + } + private fun handleExitSpecialMode(action: RoomDetailActions.ExitSpecialMode) { withState { state -> // For edit, just delete the current draft - filterDraftUpdate = false - if (state.sendMode is SendMode.EDIT) { room.deleteDraft() } else {