diff --git a/changelog.d/8796.misc b/changelog.d/8796.misc new file mode 100644 index 0000000000..ea630f0aa3 --- /dev/null +++ b/changelog.d/8796.misc @@ -0,0 +1 @@ + Add a report user action in the message bottom sheet and on the user profile page. diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 577101b0d3..2a98069c2e 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -1953,8 +1953,11 @@ "This content was reported as spam.\n\nIf you don't want to see any more content from this user, you can ignore them to hide their messages." "Reported as inappropriate" "This content was reported as inappropriate.\n\nIf you don't want to see any more content from this user, you can ignore them to hide their messages." + "Reported user" + "The user has been reported.\n\nIf you don't want to see any more content from this user, you can ignore them to hide their messages." Ignore user + Report user "All messages (noisy)" "All messages" 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 478ed4a58d..30bcf7f8eb 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 @@ -61,7 +61,8 @@ sealed class RoomDetailAction : VectorViewModelAction { val senderId: String?, val reason: String, val spam: Boolean = false, - val inappropriate: Boolean = false + val inappropriate: Boolean = false, + val user: Boolean = false, ) : RoomDetailAction() data class IgnoreUser(val userId: String?) : RoomDetailAction() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index feaad386cb..f80855663f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -1345,6 +1345,16 @@ class TimelineFragment : } .show() } + data.user -> { + MaterialAlertDialogBuilder(requireActivity(), R.style.ThemeOverlay_Vector_MaterialAlertDialog_NegativeDestructive) + .setTitle(R.string.user_reported_as_inappropriate_title) + .setMessage(R.string.user_reported_as_inappropriate_content) + .setPositiveButton(R.string.ok, null) + .setNegativeButton(R.string.block_user) { _, _ -> + timelineViewModel.handle(RoomDetailAction.IgnoreUser(data.senderId)) + } + .show() + } else -> { MaterialAlertDialogBuilder(requireActivity(), R.style.ThemeOverlay_Vector_MaterialAlertDialog_NegativeDestructive) .setTitle(R.string.content_reported_title) @@ -1857,6 +1867,13 @@ class TimelineFragment : is EventSharedAction.IgnoreUser -> { action.senderId?.let { askConfirmationToIgnoreUser(it) } } + is EventSharedAction.ReportUser -> { + timelineViewModel.handle( + RoomDetailAction.ReportContent( + action.eventId, action.senderId, "Reporting user ${action.senderId}", user = true + ) + ) + } is EventSharedAction.OnUrlClicked -> { onUrlClicked(action.url, action.title) } 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 7bf9f536f2..18ff638390 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 @@ -98,6 +98,9 @@ sealed class EventSharedAction( data class IgnoreUser(val senderId: String?) : EventSharedAction(R.string.message_ignore_user, R.drawable.ic_alert_triangle, true) + data class ReportUser(val eventId: String, val senderId: String?) : + EventSharedAction(R.string.message_report_user, R.drawable.ic_flag, true) + data class QuickReact(val eventId: String, val clickedOn: String, val add: Boolean) : EventSharedAction(0, 0) 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 62aed5c3c6..8809c4f0bf 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 @@ -430,6 +430,12 @@ class MessageActionsViewModel @AssistedInject constructor( add(EventSharedAction.Separator) add(EventSharedAction.IgnoreUser(timelineEvent.root.senderId)) + add( + EventSharedAction.ReportUser( + eventId = eventId, + senderId = timelineEvent.root.senderId, + ) + ) } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt index e2298d9b53..874f3c73b8 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt @@ -22,6 +22,7 @@ import im.vector.app.core.platform.VectorViewModelAction sealed class RoomMemberProfileAction : VectorViewModelAction { object RetryFetchingInfo : RoomMemberProfileAction() object IgnoreUser : RoomMemberProfileAction() + object ReportUser : RoomMemberProfileAction() data class BanOrUnbanUser(val reason: String?) : RoomMemberProfileAction() data class KickUser(val reason: String?) : RoomMemberProfileAction() object InviteUser : RoomMemberProfileAction() diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index 9585e6aaa1..e74bad1acb 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -39,6 +39,7 @@ class RoomMemberProfileController @Inject constructor( interface Callback { fun onIgnoreClicked() + fun onReportClicked() fun onTapVerify() fun onShowDeviceList() fun onShowDeviceListNoCrossSigning() @@ -225,7 +226,7 @@ class RoomMemberProfileController @Inject constructor( title = stringProvider.getString(R.string.room_participants_action_invite), destructive = false, editable = false, - divider = ignoreActionTitle != null, + divider = true, action = { callback?.onInviteClicked() } ) } @@ -235,10 +236,18 @@ class RoomMemberProfileController @Inject constructor( title = ignoreActionTitle, destructive = true, editable = false, - divider = false, + divider = true, action = { callback?.onIgnoreClicked() } ) } + buildProfileAction( + id = "report", + title = stringProvider.getString(R.string.message_report_user), + destructive = true, + editable = false, + divider = false, + action = { callback?.onReportClicked() } + ) } } @@ -314,9 +323,9 @@ class RoomMemberProfileController @Inject constructor( private fun RoomMemberProfileViewState.buildIgnoreActionTitle(): String? { val isIgnored = isIgnored() ?: return null return if (isIgnored) { - stringProvider.getString(R.string.unignore) + stringProvider.getString(R.string.room_participants_action_unignore_title) } else { - stringProvider.getString(R.string.action_ignore) + stringProvider.getString(R.string.room_participants_action_ignore_title) } } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 020512af36..7ac5bfea0c 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -140,11 +140,20 @@ class RoomMemberProfileFragment : is RoomMemberProfileViewEvents.OnIgnoreActionSuccess -> Unit is RoomMemberProfileViewEvents.OnInviteActionSuccess -> Unit RoomMemberProfileViewEvents.GoBack -> handleGoBack() + RoomMemberProfileViewEvents.OnReportActionSuccess -> handleReportSuccess() } } setupLongClicks() } + private fun handleReportSuccess() { + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.user_reported_as_inappropriate_title) + .setMessage(R.string.user_reported_as_inappropriate_content) + .setPositiveButton(R.string.ok, null) + .show() + } + private fun setupLongClicks() { headerViews.memberProfileNameView.copyOnLongClick() headerViews.memberProfileIdView.copyOnLongClick() @@ -301,6 +310,10 @@ class RoomMemberProfileFragment : } } + override fun onReportClicked() { + viewModel.handle(RoomMemberProfileAction.ReportUser) + } + override fun onTapVerify() { viewModel.handle(RoomMemberProfileAction.VerifyUser) } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewEvents.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewEvents.kt index d04de8b936..0bf8ef1b6e 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewEvents.kt @@ -26,6 +26,7 @@ sealed class RoomMemberProfileViewEvents : VectorViewEvents { data class Failure(val throwable: Throwable) : RoomMemberProfileViewEvents() object OnIgnoreActionSuccess : RoomMemberProfileViewEvents() + object OnReportActionSuccess : RoomMemberProfileViewEvents() object OnSetPowerLevelSuccess : RoomMemberProfileViewEvents() object OnInviteActionSuccess : RoomMemberProfileViewEvents() object OnKickActionSuccess : RoomMemberProfileViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index d38b2a0a69..f688793f4b 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -161,6 +161,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor( when (action) { is RoomMemberProfileAction.RetryFetchingInfo -> handleRetryFetchProfileInfo() is RoomMemberProfileAction.IgnoreUser -> handleIgnoreAction() + is RoomMemberProfileAction.ReportUser -> handleReportAction() is RoomMemberProfileAction.VerifyUser -> prepareVerification() is RoomMemberProfileAction.ShareRoomMemberProfile -> handleShareRoomMemberProfile() is RoomMemberProfileAction.SetPowerLevel -> handleSetPowerLevel(action) @@ -172,6 +173,25 @@ class RoomMemberProfileViewModel @AssistedInject constructor( } } + private fun handleReportAction() { + viewModelScope.launch { + val event = try { + // The API need an Event, use the latest Event. + val latestEventId = room?.roomSummary()?.latestPreviewableEvent?.eventId ?: return@launch + room.reportingService() + .reportContent( + eventId = latestEventId, + score = -100, + reason = "Reporting user ${initialState.userId} (eventId is not relevant)" + ) + RoomMemberProfileViewEvents.OnReportActionSuccess + } catch (failure: Throwable) { + RoomMemberProfileViewEvents.Failure(failure) + } + _viewEvents.post(event) + } + } + private fun handleOpenOrCreateDm(action: RoomMemberProfileAction.OpenOrCreateDm) { viewModelScope.launch { _viewEvents.post(RoomMemberProfileViewEvents.Loading())