Merge pull request #8797 from element-hq/feature/bma/reportUser

Report user
This commit is contained in:
Benoit Marty 2024-04-02 18:08:44 +02:00 committed by GitHub
commit 9aaf29d4cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 80 additions and 5 deletions

1
changelog.d/8796.misc Normal file
View file

@ -0,0 +1 @@
Add a report user action in the message bottom sheet and on the user profile page.

View file

@ -1953,8 +1953,11 @@
<string name="content_reported_as_spam_content">"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."</string> <string name="content_reported_as_spam_content">"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."</string>
<string name="content_reported_as_inappropriate_title">"Reported as inappropriate"</string> <string name="content_reported_as_inappropriate_title">"Reported as inappropriate"</string>
<string name="content_reported_as_inappropriate_content">"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."</string> <string name="content_reported_as_inappropriate_content">"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."</string>
<string name="user_reported_as_inappropriate_title">"Reported user"</string>
<string name="user_reported_as_inappropriate_content">"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."</string>
<string name="message_ignore_user">Ignore user</string> <string name="message_ignore_user">Ignore user</string>
<string name="message_report_user">Report user</string>
<string name="room_list_quick_actions_notifications_all_noisy">"All messages (noisy)"</string> <string name="room_list_quick_actions_notifications_all_noisy">"All messages (noisy)"</string>
<string name="room_list_quick_actions_notifications_all">"All messages"</string> <string name="room_list_quick_actions_notifications_all">"All messages"</string>

View file

@ -61,7 +61,8 @@ sealed class RoomDetailAction : VectorViewModelAction {
val senderId: String?, val senderId: String?,
val reason: String, val reason: String,
val spam: Boolean = false, val spam: Boolean = false,
val inappropriate: Boolean = false val inappropriate: Boolean = false,
val user: Boolean = false,
) : RoomDetailAction() ) : RoomDetailAction()
data class IgnoreUser(val userId: String?) : RoomDetailAction() data class IgnoreUser(val userId: String?) : RoomDetailAction()

View file

@ -1345,6 +1345,16 @@ class TimelineFragment :
} }
.show() .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 -> { else -> {
MaterialAlertDialogBuilder(requireActivity(), R.style.ThemeOverlay_Vector_MaterialAlertDialog_NegativeDestructive) MaterialAlertDialogBuilder(requireActivity(), R.style.ThemeOverlay_Vector_MaterialAlertDialog_NegativeDestructive)
.setTitle(R.string.content_reported_title) .setTitle(R.string.content_reported_title)
@ -1857,6 +1867,13 @@ class TimelineFragment :
is EventSharedAction.IgnoreUser -> { is EventSharedAction.IgnoreUser -> {
action.senderId?.let { askConfirmationToIgnoreUser(it) } 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 -> { is EventSharedAction.OnUrlClicked -> {
onUrlClicked(action.url, action.title) onUrlClicked(action.url, action.title)
} }

View file

@ -98,6 +98,9 @@ sealed class EventSharedAction(
data class IgnoreUser(val senderId: String?) : data class IgnoreUser(val senderId: String?) :
EventSharedAction(R.string.message_ignore_user, R.drawable.ic_alert_triangle, true) 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) : data class QuickReact(val eventId: String, val clickedOn: String, val add: Boolean) :
EventSharedAction(0, 0) EventSharedAction(0, 0)

View file

@ -430,6 +430,12 @@ class MessageActionsViewModel @AssistedInject constructor(
add(EventSharedAction.Separator) add(EventSharedAction.Separator)
add(EventSharedAction.IgnoreUser(timelineEvent.root.senderId)) add(EventSharedAction.IgnoreUser(timelineEvent.root.senderId))
add(
EventSharedAction.ReportUser(
eventId = eventId,
senderId = timelineEvent.root.senderId,
)
)
} }
} }

View file

@ -22,6 +22,7 @@ import im.vector.app.core.platform.VectorViewModelAction
sealed class RoomMemberProfileAction : VectorViewModelAction { sealed class RoomMemberProfileAction : VectorViewModelAction {
object RetryFetchingInfo : RoomMemberProfileAction() object RetryFetchingInfo : RoomMemberProfileAction()
object IgnoreUser : RoomMemberProfileAction() object IgnoreUser : RoomMemberProfileAction()
object ReportUser : RoomMemberProfileAction()
data class BanOrUnbanUser(val reason: String?) : RoomMemberProfileAction() data class BanOrUnbanUser(val reason: String?) : RoomMemberProfileAction()
data class KickUser(val reason: String?) : RoomMemberProfileAction() data class KickUser(val reason: String?) : RoomMemberProfileAction()
object InviteUser : RoomMemberProfileAction() object InviteUser : RoomMemberProfileAction()

View file

@ -39,6 +39,7 @@ class RoomMemberProfileController @Inject constructor(
interface Callback { interface Callback {
fun onIgnoreClicked() fun onIgnoreClicked()
fun onReportClicked()
fun onTapVerify() fun onTapVerify()
fun onShowDeviceList() fun onShowDeviceList()
fun onShowDeviceListNoCrossSigning() fun onShowDeviceListNoCrossSigning()
@ -225,7 +226,7 @@ class RoomMemberProfileController @Inject constructor(
title = stringProvider.getString(R.string.room_participants_action_invite), title = stringProvider.getString(R.string.room_participants_action_invite),
destructive = false, destructive = false,
editable = false, editable = false,
divider = ignoreActionTitle != null, divider = true,
action = { callback?.onInviteClicked() } action = { callback?.onInviteClicked() }
) )
} }
@ -235,10 +236,18 @@ class RoomMemberProfileController @Inject constructor(
title = ignoreActionTitle, title = ignoreActionTitle,
destructive = true, destructive = true,
editable = false, editable = false,
divider = false, divider = true,
action = { callback?.onIgnoreClicked() } 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? { private fun RoomMemberProfileViewState.buildIgnoreActionTitle(): String? {
val isIgnored = isIgnored() ?: return null val isIgnored = isIgnored() ?: return null
return if (isIgnored) { return if (isIgnored) {
stringProvider.getString(R.string.unignore) stringProvider.getString(R.string.room_participants_action_unignore_title)
} else { } else {
stringProvider.getString(R.string.action_ignore) stringProvider.getString(R.string.room_participants_action_ignore_title)
} }
} }
} }

View file

@ -140,11 +140,20 @@ class RoomMemberProfileFragment :
is RoomMemberProfileViewEvents.OnIgnoreActionSuccess -> Unit is RoomMemberProfileViewEvents.OnIgnoreActionSuccess -> Unit
is RoomMemberProfileViewEvents.OnInviteActionSuccess -> Unit is RoomMemberProfileViewEvents.OnInviteActionSuccess -> Unit
RoomMemberProfileViewEvents.GoBack -> handleGoBack() RoomMemberProfileViewEvents.GoBack -> handleGoBack()
RoomMemberProfileViewEvents.OnReportActionSuccess -> handleReportSuccess()
} }
} }
setupLongClicks() 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() { private fun setupLongClicks() {
headerViews.memberProfileNameView.copyOnLongClick() headerViews.memberProfileNameView.copyOnLongClick()
headerViews.memberProfileIdView.copyOnLongClick() headerViews.memberProfileIdView.copyOnLongClick()
@ -301,6 +310,10 @@ class RoomMemberProfileFragment :
} }
} }
override fun onReportClicked() {
viewModel.handle(RoomMemberProfileAction.ReportUser)
}
override fun onTapVerify() { override fun onTapVerify() {
viewModel.handle(RoomMemberProfileAction.VerifyUser) viewModel.handle(RoomMemberProfileAction.VerifyUser)
} }

View file

@ -26,6 +26,7 @@ sealed class RoomMemberProfileViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : RoomMemberProfileViewEvents() data class Failure(val throwable: Throwable) : RoomMemberProfileViewEvents()
object OnIgnoreActionSuccess : RoomMemberProfileViewEvents() object OnIgnoreActionSuccess : RoomMemberProfileViewEvents()
object OnReportActionSuccess : RoomMemberProfileViewEvents()
object OnSetPowerLevelSuccess : RoomMemberProfileViewEvents() object OnSetPowerLevelSuccess : RoomMemberProfileViewEvents()
object OnInviteActionSuccess : RoomMemberProfileViewEvents() object OnInviteActionSuccess : RoomMemberProfileViewEvents()
object OnKickActionSuccess : RoomMemberProfileViewEvents() object OnKickActionSuccess : RoomMemberProfileViewEvents()

View file

@ -161,6 +161,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(
when (action) { when (action) {
is RoomMemberProfileAction.RetryFetchingInfo -> handleRetryFetchProfileInfo() is RoomMemberProfileAction.RetryFetchingInfo -> handleRetryFetchProfileInfo()
is RoomMemberProfileAction.IgnoreUser -> handleIgnoreAction() is RoomMemberProfileAction.IgnoreUser -> handleIgnoreAction()
is RoomMemberProfileAction.ReportUser -> handleReportAction()
is RoomMemberProfileAction.VerifyUser -> prepareVerification() is RoomMemberProfileAction.VerifyUser -> prepareVerification()
is RoomMemberProfileAction.ShareRoomMemberProfile -> handleShareRoomMemberProfile() is RoomMemberProfileAction.ShareRoomMemberProfile -> handleShareRoomMemberProfile()
is RoomMemberProfileAction.SetPowerLevel -> handleSetPowerLevel(action) 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) { private fun handleOpenOrCreateDm(action: RoomMemberProfileAction.OpenOrCreateDm) {
viewModelScope.launch { viewModelScope.launch {
_viewEvents.post(RoomMemberProfileViewEvents.Loading()) _viewEvents.post(RoomMemberProfileViewEvents.Loading())