Skip to content

Commit 3c3eb49

Browse files
authored
Merge pull request #9000 from element-hq/feature/bma/reportRoom
Add action to report room.
2 parents 4a43097 + b204d27 commit 3c3eb49

File tree

13 files changed

+166
-1
lines changed

13 files changed

+166
-1
lines changed

changelog.d/8998.misc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add action to report room.

library/ui-strings/src/main/res/values/strings.xml

+2
Original file line numberDiff line numberDiff line change
@@ -2369,6 +2369,8 @@
23692369
</plurals>
23702370
<string name="room_profile_section_more_polls">Poll history</string>
23712371
<string name="room_profile_section_more_uploads">Uploads</string>
2372+
<string name="room_profile_section_more_report">Report Room</string>
2373+
<string name="room_profile_section_more_report_success_content">The room has been successfully reported.</string>
23722374
<string name="room_profile_section_more_leave">Leave Room</string>
23732375
<string name="direct_room_profile_section_more_leave">Leave</string>
23742376
<string name="room_profile_leaving_room">"Leaving the room…"</string>

matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/reporting/ReportingService.kt

+5
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,9 @@ interface ReportingService {
2626
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-rooms-roomid-report-eventid
2727
*/
2828
suspend fun reportContent(eventId: String, score: Int, reason: String)
29+
30+
/**
31+
* Report a room.
32+
*/
33+
suspend fun reportRoom(reason: String)
2934
}

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt

+13
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import org.matrix.android.sdk.internal.session.room.read.ReadBody
3636
import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
3737
import org.matrix.android.sdk.internal.session.room.relation.threads.ThreadSummariesResponse
3838
import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody
39+
import org.matrix.android.sdk.internal.session.room.reporting.ReportRoomBody
3940
import org.matrix.android.sdk.internal.session.room.send.SendResponse
4041
import org.matrix.android.sdk.internal.session.room.send.model.EventRedactBody
4142
import org.matrix.android.sdk.internal.session.room.tags.TagBody
@@ -375,6 +376,18 @@ internal interface RoomAPI {
375376
@Body body: ReportContentBody,
376377
)
377378

379+
/**
380+
* Reports a room as inappropriate to the server, which may then notify the appropriate people.
381+
*
382+
* @param roomId the room id
383+
* @param body body containing the reason
384+
*/
385+
@POST(NetworkConstants.URI_API_PREFIX_PATH_V3 + "rooms/{roomId}/report")
386+
suspend fun reportRoom(
387+
@Path("roomId") roomId: String,
388+
@Body body: ReportRoomBody,
389+
)
390+
378391
/**
379392
* Get a list of aliases maintained by the local server for the given room.
380393
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-aliases

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt

+5
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ import org.matrix.android.sdk.internal.session.room.relation.threads.DefaultFetc
114114
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadSummariesTask
115115
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
116116
import org.matrix.android.sdk.internal.session.room.reporting.DefaultReportContentTask
117+
import org.matrix.android.sdk.internal.session.room.reporting.DefaultReportRoomTask
117118
import org.matrix.android.sdk.internal.session.room.reporting.ReportContentTask
119+
import org.matrix.android.sdk.internal.session.room.reporting.ReportRoomTask
118120
import org.matrix.android.sdk.internal.session.room.state.DefaultSendStateTask
119121
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
120122
import org.matrix.android.sdk.internal.session.room.tags.AddTagToRoomTask
@@ -281,6 +283,9 @@ internal abstract class RoomModule {
281283
@Binds
282284
abstract fun bindReportContentTask(task: DefaultReportContentTask): ReportContentTask
283285

286+
@Binds
287+
abstract fun bindReportRoomTask(task: DefaultReportRoomTask): ReportRoomTask
288+
284289
@Binds
285290
abstract fun bindGetContextOfEventTask(task: DefaultGetContextOfEventTask): GetContextOfEventTask
286291

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import org.matrix.android.sdk.api.session.room.reporting.ReportingService
2323

2424
internal class DefaultReportingService @AssistedInject constructor(
2525
@Assisted private val roomId: String,
26-
private val reportContentTask: ReportContentTask
26+
private val reportContentTask: ReportContentTask,
27+
private val reportRoomTask: ReportRoomTask,
2728
) : ReportingService {
2829

2930
@AssistedFactory
@@ -35,4 +36,9 @@ internal class DefaultReportingService @AssistedInject constructor(
3536
val params = ReportContentTask.Params(roomId, eventId, score, reason)
3637
reportContentTask.execute(params)
3738
}
39+
40+
override suspend fun reportRoom(reason: String) {
41+
val params = ReportRoomTask.Params(roomId, reason)
42+
reportRoomTask.execute(params)
43+
}
3844
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2025 The Matrix.org Foundation C.I.C.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.matrix.android.sdk.internal.session.room.reporting
18+
19+
import com.squareup.moshi.Json
20+
import com.squareup.moshi.JsonClass
21+
22+
@JsonClass(generateAdapter = true)
23+
internal data class ReportRoomBody(
24+
/**
25+
* Required. The reason the content is being reported. May be blank.
26+
*/
27+
@Json(name = "reason") val reason: String
28+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2025 The Matrix.org Foundation C.I.C.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.matrix.android.sdk.internal.session.room.reporting
18+
19+
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
20+
import org.matrix.android.sdk.internal.network.executeRequest
21+
import org.matrix.android.sdk.internal.session.room.RoomAPI
22+
import org.matrix.android.sdk.internal.task.Task
23+
import javax.inject.Inject
24+
25+
internal interface ReportRoomTask : Task<ReportRoomTask.Params, Unit> {
26+
data class Params(
27+
val roomId: String,
28+
val reason: String,
29+
)
30+
}
31+
32+
internal class DefaultReportRoomTask @Inject constructor(
33+
private val roomAPI: RoomAPI,
34+
private val globalErrorReceiver: GlobalErrorReceiver
35+
) : ReportRoomTask {
36+
37+
override suspend fun execute(params: ReportRoomTask.Params) {
38+
return executeRequest(globalErrorReceiver) {
39+
roomAPI.reportRoom(params.roomId, ReportRoomBody(params.reason))
40+
}
41+
}
42+
}

vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt

+1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ sealed class RoomProfileAction : VectorViewModelAction {
1818
object CreateShortcut : RoomProfileAction()
1919
object RestoreEncryptionState : RoomProfileAction()
2020
data class SetEncryptToVerifiedDeviceOnly(val enabled: Boolean) : RoomProfileAction()
21+
data class ReportRoom(val reason: String) : RoomProfileAction()
2122
}

vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt

+8
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class RoomProfileController @Inject constructor(
5252
fun onUploadsClicked()
5353
fun createShortcut()
5454
fun onSettingsClicked()
55+
fun onReportRoomClicked()
5556
fun onLeaveRoomClicked()
5657
fun onRoomAliasesClicked()
5758
fun onRoomPermissionsClicked()
@@ -279,6 +280,13 @@ class RoomProfileController @Inject constructor(
279280
action = { callback?.createShortcut() }
280281
)
281282
}
283+
buildProfileAction(
284+
id = "Report",
285+
title = stringProvider.getString(CommonStrings.room_profile_section_more_report),
286+
icon = R.drawable.ic_report_spam,
287+
editable = false,
288+
action = { callback?.onReportRoomClicked() }
289+
)
282290
buildProfileAction(
283291
id = "leave",
284292
title = stringProvider.getString(

vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt

+33
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import im.vector.app.core.platform.VectorBaseFragment
3333
import im.vector.app.core.platform.VectorMenuProvider
3434
import im.vector.app.core.utils.copyToClipboard
3535
import im.vector.app.core.utils.startSharePlainTextIntent
36+
import im.vector.app.databinding.DialogReportContentBinding
3637
import im.vector.app.databinding.FragmentMatrixProfileBinding
3738
import im.vector.app.databinding.ViewStubRoomProfileHeaderBinding
3839
import im.vector.app.features.analytics.plan.Interaction
@@ -123,6 +124,7 @@ class RoomProfileFragment :
123124
is RoomProfileViewEvents.ShareRoomProfile -> onShareRoomProfile(it.permalink)
124125
is RoomProfileViewEvents.OnShortcutReady -> addShortcut(it)
125126
RoomProfileViewEvents.DismissLoading -> dismissLoadingDialog()
127+
is RoomProfileViewEvents.Success -> dismissSuccessDialog(it.message)
126128
}
127129
}
128130
roomListQuickActionsSharedActionViewModel
@@ -133,6 +135,17 @@ class RoomProfileFragment :
133135
setupLongClicks()
134136
}
135137

138+
private fun dismissSuccessDialog(message: CharSequence) {
139+
MaterialAlertDialogBuilder(
140+
requireActivity(),
141+
im.vector.lib.ui.styles.R.style.ThemeOverlay_Vector_MaterialAlertDialog_NegativeDestructive
142+
)
143+
.setTitle(CommonStrings.room_profile_section_more_report)
144+
.setMessage(message)
145+
.setPositiveButton(CommonStrings.ok, null)
146+
.show()
147+
}
148+
136149
private fun setupWaitingView() {
137150
views.waitingView.waitingStatusText.setText(CommonStrings.please_wait)
138151
views.waitingView.waitingStatusText.isVisible = true
@@ -286,6 +299,26 @@ class RoomProfileFragment :
286299
ShortcutManagerCompat.requestPinShortcut(requireContext(), onShortcutReady.shortcutInfo, null)
287300
}
288301

302+
override fun onReportRoomClicked() {
303+
promptReasonToReportRoom()
304+
}
305+
306+
private fun promptReasonToReportRoom() {
307+
val inflater = requireActivity().layoutInflater
308+
val layout = inflater.inflate(R.layout.dialog_report_content, null)
309+
val views = DialogReportContentBinding.bind(layout)
310+
311+
MaterialAlertDialogBuilder(requireActivity())
312+
.setTitle(CommonStrings.room_profile_section_more_report)
313+
.setView(layout)
314+
.setPositiveButton(CommonStrings.report_content_custom_submit) { _, _ ->
315+
val reason = views.dialogReportContentInput.text.toString()
316+
roomProfileViewModel.handle(RoomProfileAction.ReportRoom(reason))
317+
}
318+
.setNegativeButton(CommonStrings.action_cancel, null)
319+
.show()
320+
}
321+
289322
override fun onLeaveRoomClicked() {
290323
val isPublicRoom = roomProfileViewModel.isPublicRoom()
291324
val message = buildString {

vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewEvents.kt

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ sealed class RoomProfileViewEvents : VectorViewEvents {
1717
data class Loading(val message: CharSequence? = null) : RoomProfileViewEvents()
1818
object DismissLoading : RoomProfileViewEvents()
1919
data class Failure(val throwable: Throwable) : RoomProfileViewEvents()
20+
data class Success(val message: CharSequence) : RoomProfileViewEvents()
2021

2122
data class ShareRoomProfile(val permalink: String) : RoomProfileViewEvents()
2223
data class OnShortcutReady(val shortcutInfo: ShortcutInfoCompat) : RoomProfileViewEvents()

vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt

+20
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,26 @@ class RoomProfileViewModel @AssistedInject constructor(
176176
RoomProfileAction.CreateShortcut -> handleCreateShortcut()
177177
RoomProfileAction.RestoreEncryptionState -> restoreEncryptionState()
178178
is RoomProfileAction.SetEncryptToVerifiedDeviceOnly -> setEncryptToVerifiedDeviceOnly(action.enabled)
179+
is RoomProfileAction.ReportRoom -> handleReportRoom(action.reason)
180+
}
181+
}
182+
183+
private fun handleReportRoom(reason: String) {
184+
_viewEvents.post(RoomProfileViewEvents.Loading())
185+
session.coroutineScope.launch {
186+
try {
187+
room.reportingService().reportRoom(reason = reason)
188+
_viewEvents.post(
189+
RoomProfileViewEvents.Success(
190+
stringProvider.getString(CommonStrings.room_profile_section_more_report_success_content)
191+
)
192+
)
193+
} catch (failure: Throwable) {
194+
Timber.e(failure, "Failed to report room ${room.roomId}")
195+
_viewEvents.post(RoomProfileViewEvents.Failure(failure))
196+
} finally {
197+
_viewEvents.post(RoomProfileViewEvents.DismissLoading)
198+
}
179199
}
180200
}
181201

0 commit comments

Comments
 (0)