From 8a3e93ae9641e3e225c12a7eb30a7c8bfbb888d5 Mon Sep 17 00:00:00 2001 From: onurays Date: Thu, 13 Feb 2020 14:59:41 +0300 Subject: [PATCH 1/7] Do not ask for a reason if user wants to delete his own message. Fixes (#1003) --- .../features/home/room/detail/RoomDetailFragment.kt | 10 +++++++--- .../room/detail/timeline/action/EventSharedAction.kt | 2 +- .../detail/timeline/action/MessageActionsViewModel.kt | 2 +- vector/src/main/res/layout/dialog_delete_event.xml | 3 +-- 4 files changed, 10 insertions(+), 7 deletions(-) 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 2fa2243060..cbda6ab13e 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(eventId: String, askForReason: Boolean) { 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 = askForReason + reasonTextInputLayout.isVisible = askForReason + reasonCheckBox.setOnCheckedChangeListener { _, isChecked -> reasonTextInputLayout.isEnabled = isChecked } AlertDialog.Builder(requireActivity()) @@ -801,7 +804,8 @@ class RoomDetailFragment @Inject constructor( .setView(layout) .setPositiveButton(R.string.remove) { _, _ -> val reason = reasonInput.text.toString() - .takeIf { reasonCheckBox.isChecked } + .takeIf { askForReason } + ?.takeIf { reasonCheckBox.isChecked } ?.takeIf { it.isNotBlank() } roomDetailViewModel.handle(RoomDetailAction.RedactAction(eventId, reason)) } @@ -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.eventId, action.askForReason) } 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..5a52907bfa 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 @@ -227,7 +227,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" /> From 1b413934b51b05c84cabed2a6257df78503cdc40 Mon Sep 17 00:00:00 2001 From: onurays Date: Thu, 13 Feb 2020 16:42:13 +0300 Subject: [PATCH 2/7] Set redaction reason as message body. --- .../action/MessageActionsViewModel.kt | 46 ++++++++++++++----- vector/src/main/res/values/strings_riotX.xml | 3 ++ 2 files changed, 38 insertions(+), 11 deletions(-) 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 5a52907bfa..e7c8cfec54 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 @@ -171,18 +171,23 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted return when (timelineEvent.root.getClearType()) { EventType.MESSAGE, EventType.STICKER -> { - val messageContent: MessageContent? = timelineEvent.getLastMessageContent() - if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) { - val html = messageContent.formattedBody - ?.takeIf { it.isNotBlank() } - ?.let { htmlCompressor.compress(it) } - ?: messageContent.body + when (timelineEvent.root.isRedacted()) { + true -> getRedactionReason(timelineEvent) + false -> { + val messageContent: MessageContent? = timelineEvent.getLastMessageContent() + if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) { + val html = messageContent.formattedBody + ?.takeIf { it.isNotBlank() } + ?.let { htmlCompressor.compress(it) } + ?: messageContent.body - eventHtmlRenderer.get().render(html) - } else if (messageContent is MessageVerificationRequestContent) { - stringProvider.getString(R.string.verification_request) - } else { - messageContent?.body + eventHtmlRenderer.get().render(html) + } else if (messageContent is MessageVerificationRequestContent) { + stringProvider.getString(R.string.verification_request) + } else { + messageContent?.body + } + } } } EventType.STATE_ROOM_NAME, @@ -200,6 +205,25 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } ?: "" } + private fun getRedactionReason(timelineEvent: TimelineEvent) = + (timelineEvent + .root + .unsignedData + ?.redactedEvent + ?.content + ?.get("reason") as? String) + ?.takeIf { it.isNotBlank() } + ?.let { reason -> + stringProvider.getString( + (R.string.event_redacted_by_user_reason_with_reason + .takeIf { timelineEvent.root.senderId == session.myUserId } + ?: R.string.event_redacted_by_admin_reason_with_reason), reason + ) + } ?: stringProvider.getString( + R.string.event_redacted_by_user_reason + .takeIf { timelineEvent.root.senderId == session.myUserId } + ?: R.string.event_redacted_by_admin_reason_with_reason) + private fun actionsForEvent(timelineEvent: TimelineEvent): List { val messageContent: MessageContent? = timelineEvent.annotations?.editSummary?.aggregatedContent.toModel() ?: timelineEvent.root.getClearContent().toModel() 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 From 983593d6476431bf0d6927978fda7d36fac7d8ff Mon Sep 17 00:00:00 2001 From: onurays Date: Thu, 13 Feb 2020 17:28:14 +0300 Subject: [PATCH 3/7] getRedactionReason function is refactored. --- .../action/MessageActionsViewModel.kt | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) 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 e7c8cfec54..5e65330d6f 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 @@ -214,15 +214,17 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted ?.get("reason") as? String) ?.takeIf { it.isNotBlank() } ?.let { reason -> - stringProvider.getString( - (R.string.event_redacted_by_user_reason_with_reason - .takeIf { timelineEvent.root.senderId == session.myUserId } - ?: R.string.event_redacted_by_admin_reason_with_reason), reason - ) - } ?: stringProvider.getString( - R.string.event_redacted_by_user_reason - .takeIf { timelineEvent.root.senderId == session.myUserId } - ?: R.string.event_redacted_by_admin_reason_with_reason) + when (timelineEvent.root.senderId == session.myUserId) { + true -> stringProvider.getString(R.string.event_redacted_by_user_reason_with_reason, reason) + false -> stringProvider.getString(R.string.event_redacted_by_admin_reason_with_reason, reason) + } + } + ?: run { + when (timelineEvent.root.senderId == session.myUserId) { + true -> stringProvider.getString(R.string.event_redacted_by_user_reason) + false -> stringProvider.getString(R.string.event_redacted_by_admin_reason) + } + } private fun actionsForEvent(timelineEvent: TimelineEvent): List { val messageContent: MessageContent? = timelineEvent.annotations?.editSummary?.aggregatedContent.toModel() From f28e4cf991e1eee58dd2071d03ae9153833882e6 Mon Sep 17 00:00:00 2001 From: onurays Date: Thu, 13 Feb 2020 17:57:38 +0300 Subject: [PATCH 4/7] Fix comparison of user ids. --- .../room/detail/timeline/action/MessageActionsViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 5e65330d6f..0fbdb61a45 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 @@ -214,13 +214,13 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted ?.get("reason") as? String) ?.takeIf { it.isNotBlank() } ?.let { reason -> - when (timelineEvent.root.senderId == session.myUserId) { + when (timelineEvent.root.senderId == timelineEvent.root.unsignedData?.redactedEvent?.senderId) { true -> stringProvider.getString(R.string.event_redacted_by_user_reason_with_reason, reason) false -> stringProvider.getString(R.string.event_redacted_by_admin_reason_with_reason, reason) } } ?: run { - when (timelineEvent.root.senderId == session.myUserId) { + when (timelineEvent.root.senderId == timelineEvent.root.unsignedData?.redactedEvent?.senderId) { true -> stringProvider.getString(R.string.event_redacted_by_user_reason) false -> stringProvider.getString(R.string.event_redacted_by_admin_reason) } From fd135e1eeb8eede9df5fb9700f1cd5a70809421e Mon Sep 17 00:00:00 2001 From: onurays Date: Thu, 13 Feb 2020 18:09:26 +0300 Subject: [PATCH 5/7] Compute message body for encrypted messages too. --- .../home/room/detail/timeline/action/MessageActionsViewModel.kt | 1 + 1 file changed, 1 insertion(+) 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 0fbdb61a45..04841266ec 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 @@ -170,6 +170,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun computeMessageBody(timelineEvent: TimelineEvent): CharSequence { return when (timelineEvent.root.getClearType()) { EventType.MESSAGE, + EventType.ENCRYPTED, EventType.STICKER -> { when (timelineEvent.root.isRedacted()) { true -> getRedactionReason(timelineEvent) From 030d6824e31a73ecfd8485efe5223169c7bb5b2c Mon Sep 17 00:00:00 2001 From: onurays Date: Fri, 14 Feb 2020 15:04:25 +0300 Subject: [PATCH 6/7] Code review fixes. --- .../android/api/session/events/model/Event.kt | 5 ++ .../home/room/detail/RoomDetailFragment.kt | 12 ++-- .../action/MessageActionsViewModel.kt | 60 ++++++++++--------- 3 files changed, 42 insertions(+), 35 deletions(-) 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 cbda6ab13e..3f574a320c 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,14 +788,14 @@ class RoomDetailFragment @Inject constructor( .show() } - private fun promptConfirmationToRedactEvent(eventId: String, askForReason: Boolean) { + 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 = askForReason - reasonTextInputLayout.isVisible = askForReason + reasonCheckBox.isVisible = action.askForReason + reasonTextInputLayout.isVisible = action.askForReason reasonCheckBox.setOnCheckedChangeListener { _, isChecked -> reasonTextInputLayout.isEnabled = isChecked } @@ -804,10 +804,10 @@ class RoomDetailFragment @Inject constructor( .setView(layout) .setPositiveButton(R.string.remove) { _, _ -> val reason = reasonInput.text.toString() - .takeIf { askForReason } + .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() @@ -1125,7 +1125,7 @@ class RoomDetailFragment @Inject constructor( showSnackWithMessage(getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT) } is EventSharedAction.Redact -> { - promptConfirmationToRedactEvent(action.eventId, action.askForReason) + 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/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 04841266ec..8a908c842f 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,27 +168,25 @@ 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.ENCRYPTED, EventType.STICKER -> { - when (timelineEvent.root.isRedacted()) { - true -> getRedactionReason(timelineEvent) - false -> { - val messageContent: MessageContent? = timelineEvent.getLastMessageContent() - if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) { - val html = messageContent.formattedBody - ?.takeIf { it.isNotBlank() } - ?.let { htmlCompressor.compress(it) } - ?: messageContent.body + val messageContent: MessageContent? = timelineEvent.getLastMessageContent() + if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) { + val html = messageContent.formattedBody + ?.takeIf { it.isNotBlank() } + ?.let { htmlCompressor.compress(it) } + ?: messageContent.body - eventHtmlRenderer.get().render(html) - } else if (messageContent is MessageVerificationRequestContent) { - stringProvider.getString(R.string.verification_request) - } else { - messageContent?.body - } - } + eventHtmlRenderer.get().render(html) + } else if (messageContent is MessageVerificationRequestContent) { + stringProvider.getString(R.string.verification_request) + } else { + messageContent?.body } } EventType.STATE_ROOM_NAME, @@ -206,26 +204,30 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } ?: "" } - private fun getRedactionReason(timelineEvent: TimelineEvent) = - (timelineEvent + private fun getRedactionReason(timelineEvent: TimelineEvent): String { + return (timelineEvent .root .unsignedData ?.redactedEvent ?.content ?.get("reason") as? String) ?.takeIf { it.isNotBlank() } - ?.let { reason -> - when (timelineEvent.root.senderId == timelineEvent.root.unsignedData?.redactedEvent?.senderId) { - true -> stringProvider.getString(R.string.event_redacted_by_user_reason_with_reason, reason) - false -> stringProvider.getString(R.string.event_redacted_by_admin_reason_with_reason, reason) - } - } - ?: run { - when (timelineEvent.root.senderId == timelineEvent.root.unsignedData?.redactedEvent?.senderId) { - true -> stringProvider.getString(R.string.event_redacted_by_user_reason) - false -> stringProvider.getString(R.string.event_redacted_by_admin_reason) + .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() From e4577d8fef1db7ec0c0333c5a15a38a70a1f146d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 14 Feb 2020 14:34:22 +0100 Subject: [PATCH 7/7] Small cleanup before merge --- CHANGES.md | 2 +- .../home/room/detail/timeline/action/MessageActionsViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e05c52c835..ffc80c76c8 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/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 8a908c842f..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 @@ -171,7 +171,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted if (timelineEvent.root.isRedacted()) { return getRedactionReason(timelineEvent) } - + return when (timelineEvent.root.getClearType()) { EventType.MESSAGE, EventType.STICKER -> {