Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/8998.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add action to report room.
2 changes: 2 additions & 0 deletions library/ui-strings/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2369,6 +2369,8 @@
</plurals>
<string name="room_profile_section_more_polls">Poll history</string>
<string name="room_profile_section_more_uploads">Uploads</string>
<string name="room_profile_section_more_report">Report Room</string>
<string name="room_profile_section_more_report_success_content">The room has been successfully reported.</string>
<string name="room_profile_section_more_leave">Leave Room</string>
<string name="direct_room_profile_section_more_leave">Leave</string>
<string name="room_profile_leaving_room">"Leaving the room…"</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,9 @@ interface ReportingService {
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-rooms-roomid-report-eventid
*/
suspend fun reportContent(eventId: String, score: Int, reason: String)

/**
* Report a room.
*/
suspend fun reportRoom(reason: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import org.matrix.android.sdk.internal.session.room.read.ReadBody
import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
import org.matrix.android.sdk.internal.session.room.relation.threads.ThreadSummariesResponse
import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody
import org.matrix.android.sdk.internal.session.room.reporting.ReportRoomBody
import org.matrix.android.sdk.internal.session.room.send.SendResponse
import org.matrix.android.sdk.internal.session.room.send.model.EventRedactBody
import org.matrix.android.sdk.internal.session.room.tags.TagBody
Expand Down Expand Up @@ -375,6 +376,18 @@ internal interface RoomAPI {
@Body body: ReportContentBody,
)

/**
* Reports a room as inappropriate to the server, which may then notify the appropriate people.
*
* @param roomId the room id
* @param body body containing the reason
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_V3 + "rooms/{roomId}/report")
suspend fun reportRoom(
@Path("roomId") roomId: String,
@Body body: ReportRoomBody,
)

/**
* Get a list of aliases maintained by the local server for the given room.
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-aliases
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ import org.matrix.android.sdk.internal.session.room.relation.threads.DefaultFetc
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadSummariesTask
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
import org.matrix.android.sdk.internal.session.room.reporting.DefaultReportContentTask
import org.matrix.android.sdk.internal.session.room.reporting.DefaultReportRoomTask
import org.matrix.android.sdk.internal.session.room.reporting.ReportContentTask
import org.matrix.android.sdk.internal.session.room.reporting.ReportRoomTask
import org.matrix.android.sdk.internal.session.room.state.DefaultSendStateTask
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
import org.matrix.android.sdk.internal.session.room.tags.AddTagToRoomTask
Expand Down Expand Up @@ -281,6 +283,9 @@ internal abstract class RoomModule {
@Binds
abstract fun bindReportContentTask(task: DefaultReportContentTask): ReportContentTask

@Binds
abstract fun bindReportRoomTask(task: DefaultReportRoomTask): ReportRoomTask

@Binds
abstract fun bindGetContextOfEventTask(task: DefaultGetContextOfEventTask): GetContextOfEventTask

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import org.matrix.android.sdk.api.session.room.reporting.ReportingService

internal class DefaultReportingService @AssistedInject constructor(
@Assisted private val roomId: String,
private val reportContentTask: ReportContentTask
private val reportContentTask: ReportContentTask,
private val reportRoomTask: ReportRoomTask,
) : ReportingService {

@AssistedFactory
Expand All @@ -35,4 +36,9 @@ internal class DefaultReportingService @AssistedInject constructor(
val params = ReportContentTask.Params(roomId, eventId, score, reason)
reportContentTask.execute(params)
}

override suspend fun reportRoom(reason: String) {
val params = ReportRoomTask.Params(roomId, reason)
reportRoomTask.execute(params)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2025 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.matrix.android.sdk.internal.session.room.reporting

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
internal data class ReportRoomBody(
/**
* Required. The reason the content is being reported. May be blank.
*/
@Json(name = "reason") val reason: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2025 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.matrix.android.sdk.internal.session.room.reporting

import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.room.RoomAPI
import org.matrix.android.sdk.internal.task.Task
import javax.inject.Inject

internal interface ReportRoomTask : Task<ReportRoomTask.Params, Unit> {
data class Params(
val roomId: String,
val reason: String,
)
}

internal class DefaultReportRoomTask @Inject constructor(
private val roomAPI: RoomAPI,
private val globalErrorReceiver: GlobalErrorReceiver
) : ReportRoomTask {

override suspend fun execute(params: ReportRoomTask.Params) {
return executeRequest(globalErrorReceiver) {
roomAPI.reportRoom(params.roomId, ReportRoomBody(params.reason))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ sealed class RoomProfileAction : VectorViewModelAction {
object CreateShortcut : RoomProfileAction()
object RestoreEncryptionState : RoomProfileAction()
data class SetEncryptToVerifiedDeviceOnly(val enabled: Boolean) : RoomProfileAction()
data class ReportRoom(val reason: String) : RoomProfileAction()
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class RoomProfileController @Inject constructor(
fun onUploadsClicked()
fun createShortcut()
fun onSettingsClicked()
fun onReportRoomClicked()
fun onLeaveRoomClicked()
fun onRoomAliasesClicked()
fun onRoomPermissionsClicked()
Expand Down Expand Up @@ -279,6 +280,13 @@ class RoomProfileController @Inject constructor(
action = { callback?.createShortcut() }
)
}
buildProfileAction(
id = "Report",
title = stringProvider.getString(CommonStrings.room_profile_section_more_report),
icon = R.drawable.ic_report_spam,
editable = false,
action = { callback?.onReportRoomClicked() }
)
buildProfileAction(
id = "leave",
title = stringProvider.getString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.DialogReportContentBinding
import im.vector.app.databinding.FragmentMatrixProfileBinding
import im.vector.app.databinding.ViewStubRoomProfileHeaderBinding
import im.vector.app.features.analytics.plan.Interaction
Expand Down Expand Up @@ -123,6 +124,7 @@ class RoomProfileFragment :
is RoomProfileViewEvents.ShareRoomProfile -> onShareRoomProfile(it.permalink)
is RoomProfileViewEvents.OnShortcutReady -> addShortcut(it)
RoomProfileViewEvents.DismissLoading -> dismissLoadingDialog()
is RoomProfileViewEvents.Success -> dismissSuccessDialog(it.message)
}
}
roomListQuickActionsSharedActionViewModel
Expand All @@ -133,6 +135,17 @@ class RoomProfileFragment :
setupLongClicks()
}

private fun dismissSuccessDialog(message: CharSequence) {
MaterialAlertDialogBuilder(
requireActivity(),
im.vector.lib.ui.styles.R.style.ThemeOverlay_Vector_MaterialAlertDialog_NegativeDestructive
)
.setTitle(CommonStrings.room_profile_section_more_report)
.setMessage(message)
.setPositiveButton(CommonStrings.ok, null)
.show()
}

private fun setupWaitingView() {
views.waitingView.waitingStatusText.setText(CommonStrings.please_wait)
views.waitingView.waitingStatusText.isVisible = true
Expand Down Expand Up @@ -286,6 +299,26 @@ class RoomProfileFragment :
ShortcutManagerCompat.requestPinShortcut(requireContext(), onShortcutReady.shortcutInfo, null)
}

override fun onReportRoomClicked() {
promptReasonToReportRoom()
}

private fun promptReasonToReportRoom() {
val inflater = requireActivity().layoutInflater
val layout = inflater.inflate(R.layout.dialog_report_content, null)
val views = DialogReportContentBinding.bind(layout)

MaterialAlertDialogBuilder(requireActivity())
.setTitle(CommonStrings.room_profile_section_more_report)
.setView(layout)
.setPositiveButton(CommonStrings.report_content_custom_submit) { _, _ ->
val reason = views.dialogReportContentInput.text.toString()
roomProfileViewModel.handle(RoomProfileAction.ReportRoom(reason))
}
.setNegativeButton(CommonStrings.action_cancel, null)
.show()
}

override fun onLeaveRoomClicked() {
val isPublicRoom = roomProfileViewModel.isPublicRoom()
val message = buildString {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ sealed class RoomProfileViewEvents : VectorViewEvents {
data class Loading(val message: CharSequence? = null) : RoomProfileViewEvents()
object DismissLoading : RoomProfileViewEvents()
data class Failure(val throwable: Throwable) : RoomProfileViewEvents()
data class Success(val message: CharSequence) : RoomProfileViewEvents()

data class ShareRoomProfile(val permalink: String) : RoomProfileViewEvents()
data class OnShortcutReady(val shortcutInfo: ShortcutInfoCompat) : RoomProfileViewEvents()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,26 @@ class RoomProfileViewModel @AssistedInject constructor(
RoomProfileAction.CreateShortcut -> handleCreateShortcut()
RoomProfileAction.RestoreEncryptionState -> restoreEncryptionState()
is RoomProfileAction.SetEncryptToVerifiedDeviceOnly -> setEncryptToVerifiedDeviceOnly(action.enabled)
is RoomProfileAction.ReportRoom -> handleReportRoom(action.reason)
}
}

private fun handleReportRoom(reason: String) {
_viewEvents.post(RoomProfileViewEvents.Loading())
session.coroutineScope.launch {
try {
room.reportingService().reportRoom(reason = reason)
_viewEvents.post(
RoomProfileViewEvents.Success(
stringProvider.getString(CommonStrings.room_profile_section_more_report_success_content)
)
)
} catch (failure: Throwable) {
Timber.e(failure, "Failed to report room ${room.roomId}")
_viewEvents.post(RoomProfileViewEvents.Failure(failure))
} finally {
_viewEvents.post(RoomProfileViewEvents.DismissLoading)
}
}
}

Expand Down
Loading