diff --git a/CHANGES.md b/CHANGES.md index 262fba061c..cc697ddaf8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,7 @@ Features ✨: - Polls and Bot Buttons (MSC 2192 matrix-org/matrix-doc#2192) Improvements 🙌: - - Show confirmation dialog before deleting a message (#967) + - Show confirmation dialog before deleting a message (#967, #1003) - Open room member profile from reactions list and read receipts list (#875) Bugfix 🐛: diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt index fb94d61c0b..d131960893 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt @@ -157,6 +157,11 @@ data class Event( */ fun isRedacted() = unsignedData?.redactedEvent != null + /** + * Tells if the event is redacted by the user himself. + */ + fun isRedactedBySameUser() = senderId == unsignedData?.redactedEvent?.senderId + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false 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 5cb3024712..d744c7f443 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 @@ -788,12 +788,15 @@ class RoomDetailFragment @Inject constructor( .show() } - private fun promptReasonToRedactEvent(eventId: String) { + private fun promptConfirmationToRedactEvent(action: EventSharedAction.Redact) { val layout = requireActivity().layoutInflater.inflate(R.layout.dialog_delete_event, null) val reasonCheckBox = layout.findViewById(R.id.deleteEventReasonCheck) val reasonTextInputLayout = layout.findViewById(R.id.deleteEventReasonTextInputLayout) val reasonInput = layout.findViewById(R.id.deleteEventReasonInput) + reasonCheckBox.isVisible = action.askForReason + reasonTextInputLayout.isVisible = action.askForReason + reasonCheckBox.setOnCheckedChangeListener { _, isChecked -> reasonTextInputLayout.isEnabled = isChecked } AlertDialog.Builder(requireActivity()) @@ -801,9 +804,10 @@ class RoomDetailFragment @Inject constructor( .setView(layout) .setPositiveButton(R.string.remove) { _, _ -> val reason = reasonInput.text.toString() - .takeIf { reasonCheckBox.isChecked } + .takeIf { action.askForReason } + ?.takeIf { reasonCheckBox.isChecked } ?.takeIf { it.isNotBlank() } - roomDetailViewModel.handle(RoomDetailAction.RedactAction(eventId, reason)) + roomDetailViewModel.handle(RoomDetailAction.RedactAction(action.eventId, reason)) } .setNegativeButton(R.string.cancel, null) .show() @@ -1121,7 +1125,7 @@ class RoomDetailFragment @Inject constructor( showSnackWithMessage(getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT) } is EventSharedAction.Redact -> { - promptReasonToRedactEvent(action.eventId) + promptConfirmationToRedactEvent(action) } is EventSharedAction.Share -> { // TODO current data communication is too limited diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt index 8a8766c3ef..cba89d8481 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt @@ -55,7 +55,7 @@ sealed class EventSharedAction(@StringRes val titleRes: Int, data class Remove(val eventId: String) : EventSharedAction(R.string.remove, R.drawable.ic_trash, true) - data class Redact(val eventId: String) : + 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) : diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 4b130e2103..a36215007d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -168,6 +168,10 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } private fun computeMessageBody(timelineEvent: TimelineEvent): CharSequence { + if (timelineEvent.root.isRedacted()) { + return getRedactionReason(timelineEvent) + } + return when (timelineEvent.root.getClearType()) { EventType.MESSAGE, EventType.STICKER -> { @@ -200,6 +204,31 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } ?: "" } + private fun getRedactionReason(timelineEvent: TimelineEvent): String { + return (timelineEvent + .root + .unsignedData + ?.redactedEvent + ?.content + ?.get("reason") as? String) + ?.takeIf { it.isNotBlank() } + .let { reason -> + if (reason == null) { + if (timelineEvent.root.isRedactedBySameUser()) { + stringProvider.getString(R.string.event_redacted_by_user_reason) + } else { + stringProvider.getString(R.string.event_redacted_by_admin_reason) + } + } else { + if (timelineEvent.root.isRedactedBySameUser()) { + stringProvider.getString(R.string.event_redacted_by_user_reason_with_reason, reason) + } else { + stringProvider.getString(R.string.event_redacted_by_admin_reason_with_reason, reason) + } + } + } + } + private fun actionsForEvent(timelineEvent: TimelineEvent): List { val messageContent: MessageContent? = timelineEvent.annotations?.editSummary?.aggregatedContent.toModel() ?: timelineEvent.root.getClearContent().toModel() @@ -227,7 +256,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } if (canRedact(timelineEvent, session.myUserId)) { - add(EventSharedAction.Redact(eventId)) + add(EventSharedAction.Redact(eventId, askForReason = informationData.senderId != session.myUserId)) } if (canCopy(msgType)) { diff --git a/vector/src/main/res/layout/dialog_delete_event.xml b/vector/src/main/res/layout/dialog_delete_event.xml index 8ca7a25113..08b0131f6a 100644 --- a/vector/src/main/res/layout/dialog_delete_event.xml +++ b/vector/src/main/res/layout/dialog_delete_event.xml @@ -43,8 +43,7 @@ + android:layout_height="wrap_content" /> diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 75790348de..ee70ac91ff 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -33,6 +33,9 @@ Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change. Include a reason Reason for redacting + + Event deleted by user, reason: %1$s + Event moderated by room admin, reason: %1$s