diff --git a/CHANGES.md b/CHANGES.md index 0dc1a5d3c2..38551d1a4a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ Improvements 🙌: - Do not load room members in e2e after init sync Bugfix 🐛: + - Add option to cancel stuck messages at bottom of timeline see #516 - Ensure message are decrypted in the room list after a clear cache Translations 🗣: diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index ecbd0b0d30..72e614c18c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -58,7 +58,7 @@ sealed class RoomDetailAction : VectorViewModelAction { data class ResendMessage(val eventId: String) : RoomDetailAction() data class RemoveFailedEcho(val eventId: String) : RoomDetailAction() - data class CancelSend(val eventId: String) : RoomDetailAction() + data class CancelSend(val eventId: String, val force: Boolean) : RoomDetailAction() data class ReplyToOptions(val eventId: String, val optionIndex: Int, val optionValue: String) : RoomDetailAction() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 1a487eb065..a80aeb65b0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -1570,14 +1570,18 @@ class RoomDetailFragment @Inject constructor( } private fun handleCancelSend(action: EventSharedAction.Cancel) { - AlertDialog.Builder(requireContext()) - .setTitle(R.string.dialog_title_confirmation) - .setMessage(getString(R.string.event_status_cancel_sending_dialog_message)) - .setNegativeButton(R.string.no, null) - .setPositiveButton(R.string.yes) { _, _ -> - roomDetailViewModel.handle(RoomDetailAction.CancelSend(action.eventId)) - } - .show() + if (action.force) { + roomDetailViewModel.handle(RoomDetailAction.CancelSend(action.eventId, true)) + } else { + AlertDialog.Builder(requireContext()) + .setTitle(R.string.dialog_title_confirmation) + .setMessage(getString(R.string.event_status_cancel_sending_dialog_message)) + .setNegativeButton(R.string.no, null) + .setPositiveButton(R.string.yes) { _, _ -> + roomDetailViewModel.handle(RoomDetailAction.CancelSend(action.eventId, false)) + } + .show() + } } override fun onAvatarClicked(informationData: MessageInformationData) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 115b0b29dd..af3d5461ef 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -1208,6 +1208,10 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun handleCancel(action: RoomDetailAction.CancelSend) { + if (action.force) { + room.cancelSend(action.eventId) + return + } val targetEventId = action.eventId room.getTimeLineEvent(targetEventId)?.let { // State must be in one of the sending states diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt index c21d552409..d9ee7f3ccf 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt @@ -63,7 +63,7 @@ sealed class EventSharedAction(@StringRes val titleRes: Int, data class Redact(val eventId: String, val askForReason: Boolean) : EventSharedAction(R.string.message_action_item_redact, R.drawable.ic_delete, true) - data class Cancel(val eventId: String) : + data class Cancel(val eventId: String, val force: Boolean) : EventSharedAction(R.string.cancel, R.drawable.ic_close_round) data class ViewSource(val content: String) : diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index adf315a955..ac1c2258aa 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -250,6 +250,9 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted timelineEvent.root.sendState == SendState.SYNCED -> { addActionsForSyncedState(timelineEvent, actionPermissions, messageContent, msgType) } + timelineEvent.root.sendState == SendState.SENT -> { + addActionsForSentNotSyncedState(timelineEvent) + } } } } @@ -287,10 +290,22 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun ArrayList.addActionsForSendingState(timelineEvent: TimelineEvent) { // TODO is uploading attachment? if (canCancel(timelineEvent)) { - add(EventSharedAction.Cancel(timelineEvent.eventId)) + add(EventSharedAction.Cancel(timelineEvent.eventId, false)) } } + private fun ArrayList.addActionsForSentNotSyncedState(timelineEvent: TimelineEvent) { + // If sent but not synced (synapse stuck at bottom bug) + // Still offer action to cancel (will only remove local echo) + timelineEvent.root.eventId?.let { + add(EventSharedAction.Cancel(it, true)) + } + + // TODO Can be redacted + + // TODO sent by me or sufficient power level + } + private fun ArrayList.addActionsForSyncedState(timelineEvent: TimelineEvent, actionPermissions: ActionPermissions, messageContent: MessageContent?, @@ -337,12 +352,6 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted if (canSave(msgType) && messageContent is MessageWithAttachmentContent) { add(EventSharedAction.Save(timelineEvent.eventId, messageContent)) } - - if (timelineEvent.root.sendState == SendState.SENT) { - // TODO Can be redacted - - // TODO sent by me or sufficient power level - } } if (vectorPreferences.developerMode()) {