First Commit
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import extension.setupDependencyInjection
|
||||
import extension.testCommonDependencies
|
||||
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.features.reportroom.impl"
|
||||
testOptions {
|
||||
unitTests {
|
||||
isIncludeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupDependencyInjection()
|
||||
|
||||
dependencies {
|
||||
api(projects.features.reportroom.api)
|
||||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.matrixui)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
|
||||
testCommonDependencies(libs, true)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.reportroom.api.ReportRoomEntryPoint
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultReportRoomEntryPoint : ReportRoomEntryPoint {
|
||||
override fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
roomId: RoomId,
|
||||
): Node {
|
||||
return parentNode.createNode<ReportRoomNode>(buildContext, plugins = listOf(ReportRoomNode.Inputs(roomId)))
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
interface ReportRoom {
|
||||
suspend operator fun invoke(
|
||||
roomId: RoomId,
|
||||
shouldReport: Boolean,
|
||||
reason: String,
|
||||
shouldLeave: Boolean,
|
||||
): Result<Unit>
|
||||
|
||||
sealed class Exception : kotlin.Exception() {
|
||||
data object RoomNotFound : Exception()
|
||||
data object LeftRoomFailed : Exception()
|
||||
data object ReportRoomFailed : Exception()
|
||||
}
|
||||
}
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultReportRoom(
|
||||
private val client: MatrixClient,
|
||||
) : ReportRoom {
|
||||
override suspend operator fun invoke(
|
||||
roomId: RoomId,
|
||||
shouldReport: Boolean,
|
||||
reason: String,
|
||||
shouldLeave: Boolean
|
||||
): Result<Unit> {
|
||||
val room = client.getRoom(roomId)
|
||||
?: return Result.failure(ReportRoom.Exception.RoomNotFound)
|
||||
|
||||
if (shouldReport) {
|
||||
room
|
||||
.reportRoom(reason.takeIf { it.isNotBlank() })
|
||||
.onFailure {
|
||||
return Result.failure(ReportRoom.Exception.ReportRoomFailed)
|
||||
}
|
||||
}
|
||||
if (shouldLeave) {
|
||||
room
|
||||
.leave()
|
||||
.onFailure {
|
||||
return Result.failure(ReportRoom.Exception.LeftRoomFailed)
|
||||
}
|
||||
}
|
||||
return Result.success(Unit)
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
sealed interface ReportRoomEvents {
|
||||
data class UpdateReason(val reason: String) : ReportRoomEvents
|
||||
data object ToggleLeaveRoom : ReportRoomEvents
|
||||
data object Report : ReportRoomEvents
|
||||
data object ClearReportAction : ReportRoomEvents
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.libraries.architecture.NodeInputs
|
||||
import io.element.android.libraries.architecture.inputs
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
@AssistedInject
|
||||
class ReportRoomNode(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
presenterFactory: ReportRoomPresenter.Factory,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
data class Inputs(val roomId: RoomId) : NodeInputs
|
||||
|
||||
private val roomId = inputs<Inputs>().roomId
|
||||
private val presenter: ReportRoomPresenter = presenterFactory.create(roomId = roomId)
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
ReportRoomView(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
onBackClick = ::navigateUp,
|
||||
)
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedFactory
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runUpdatingState
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@AssistedInject
|
||||
class ReportRoomPresenter(
|
||||
@Assisted private val roomId: RoomId,
|
||||
private val reportRoom: ReportRoom,
|
||||
) : Presenter<ReportRoomState> {
|
||||
@AssistedFactory
|
||||
fun interface Factory {
|
||||
fun create(roomId: RoomId): ReportRoomPresenter
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun present(): ReportRoomState {
|
||||
var reason by rememberSaveable { mutableStateOf("") }
|
||||
var leaveRoom by rememberSaveable { mutableStateOf(false) }
|
||||
var reportAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
fun handleEvent(event: ReportRoomEvents) {
|
||||
when (event) {
|
||||
ReportRoomEvents.Report -> coroutineScope.reportRoom(reason, leaveRoom, reportAction)
|
||||
ReportRoomEvents.ToggleLeaveRoom -> {
|
||||
leaveRoom = !leaveRoom
|
||||
}
|
||||
is ReportRoomEvents.UpdateReason -> {
|
||||
reason = event.reason
|
||||
}
|
||||
ReportRoomEvents.ClearReportAction -> {
|
||||
reportAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
}
|
||||
return ReportRoomState(
|
||||
reason = reason,
|
||||
leaveRoom = leaveRoom,
|
||||
reportAction = reportAction.value,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.reportRoom(
|
||||
reason: String,
|
||||
shouldLeave: Boolean,
|
||||
action: MutableState<AsyncAction<Unit>>
|
||||
) = launch {
|
||||
val previousFailure = action.value as? AsyncAction.Failure
|
||||
val shouldReport = previousFailure?.error !is ReportRoom.Exception.LeftRoomFailed
|
||||
runUpdatingState(action) {
|
||||
reportRoom(
|
||||
roomId = roomId,
|
||||
shouldReport = shouldReport,
|
||||
reason = reason,
|
||||
shouldLeave = shouldLeave
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
|
||||
data class ReportRoomState(
|
||||
val reason: String,
|
||||
val leaveRoom: Boolean,
|
||||
val reportAction: AsyncAction<Unit>,
|
||||
val eventSink: (ReportRoomEvents) -> Unit
|
||||
) {
|
||||
val canReport: Boolean = reason.isNotBlank()
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
|
||||
open class ReportRoomStateProvider : PreviewParameterProvider<ReportRoomState> {
|
||||
companion object {
|
||||
private const val A_REPORT_ROOM_REASON = "Inappropriate content"
|
||||
}
|
||||
|
||||
override val values: Sequence<ReportRoomState>
|
||||
get() = sequenceOf(
|
||||
aReportRoomState(),
|
||||
aReportRoomState(reason = A_REPORT_ROOM_REASON),
|
||||
aReportRoomState(leaveRoom = true),
|
||||
aReportRoomState(reason = A_REPORT_ROOM_REASON, reportAction = AsyncAction.Loading),
|
||||
aReportRoomState(reason = A_REPORT_ROOM_REASON, reportAction = AsyncAction.Failure(Exception("Failed to report"))),
|
||||
)
|
||||
}
|
||||
|
||||
fun aReportRoomState(
|
||||
reason: String = "",
|
||||
leaveRoom: Boolean = false,
|
||||
reportAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
eventSink: (ReportRoomEvents) -> Unit = {}
|
||||
) = ReportRoomState(
|
||||
reason = reason,
|
||||
leaveRoom = leaveRoom,
|
||||
reportAction = reportAction,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.ListItem
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TextField
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ReportRoomView(
|
||||
state: ReportRoomState,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
val isReporting = state.reportAction is AsyncAction.Loading
|
||||
AsyncActionView(
|
||||
async = state.reportAction,
|
||||
onSuccess = { onBackClick() },
|
||||
errorTitle = { failure ->
|
||||
when (failure) {
|
||||
is ReportRoom.Exception.LeftRoomFailed -> stringResource(R.string.screen_report_room_leave_failed_alert_title)
|
||||
else -> stringResource(CommonStrings.dialog_title_error)
|
||||
}
|
||||
},
|
||||
errorMessage = { failure ->
|
||||
when (failure) {
|
||||
is ReportRoom.Exception.LeftRoomFailed -> stringResource(R.string.screen_report_room_leave_failed_alert_message)
|
||||
else -> stringResource(CommonStrings.error_unknown)
|
||||
}
|
||||
},
|
||||
onRetry = {
|
||||
state.eventSink(ReportRoomEvents.Report)
|
||||
},
|
||||
onErrorDismiss = { state.eventSink(ReportRoomEvents.ClearReportAction) }
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
titleStr = stringResource(R.string.screen_report_room_title),
|
||||
navigationIcon = {
|
||||
BackButton(onClick = onBackClick)
|
||||
}
|
||||
)
|
||||
},
|
||||
modifier = modifier
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.imePadding()
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(vertical = 16.dp)
|
||||
) {
|
||||
TextField(
|
||||
value = state.reason,
|
||||
onValueChange = { state.eventSink(ReportRoomEvents.UpdateReason(it)) },
|
||||
placeholder = stringResource(R.string.screen_report_room_reason_placeholder),
|
||||
minLines = 3,
|
||||
enabled = !isReporting,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.heightIn(min = 90.dp),
|
||||
supportingText = stringResource(R.string.screen_report_room_reason_footer),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
ListItem(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
headlineContent = {
|
||||
Text(text = stringResource(CommonStrings.action_leave_room))
|
||||
},
|
||||
onClick = {
|
||||
state.eventSink(ReportRoomEvents.ToggleLeaveRoom)
|
||||
},
|
||||
trailingContent = ListItemContent.Switch(checked = state.leaveRoom)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_report),
|
||||
enabled = state.canReport && !isReporting,
|
||||
destructive = true,
|
||||
showProgress = isReporting,
|
||||
onClick = {
|
||||
focusManager.clearFocus(force = true)
|
||||
state.eventSink(ReportRoomEvents.Report)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun ReportRoomViewPreview(
|
||||
@PreviewParameter(ReportRoomStateProvider::class) state: ReportRoomState
|
||||
) = ElementPreview {
|
||||
ReportRoomView(
|
||||
state = state,
|
||||
onBackClick = {},
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_title">"Докладване на стаята"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Vaše hlášení bylo úspěšně odesláno, ale při pokusu o opuštění místnosti jsme narazili na problém. Zkuste to prosím znovu."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Nelze opustit místnost"</string>
|
||||
<string name="screen_report_room_reason_footer">"Nahlaste tuto místnost svému administrátorovi. Pokud jsou zprávy zašifrované, váš administrátor je nebude moci číst."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Popište důvod…"</string>
|
||||
<string name="screen_report_room_title">"Nahlásit místnost"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Cyflwynwyd eich adroddiad yn llwyddiannus, ond cododd problem wrth geisio gadael yr ystafell. Ceisiwch eto."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Methu Gadael yr Ystafell"</string>
|
||||
<string name="screen_report_room_reason_footer">"Adroddwch yr ystafell hon i\'ch gweinyddwr. Os yw\'r negeseuon wedi\'u hamgryptio, fydd eich gweinyddwr ddim yn gallu eu darllen."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Disgrifiwch y rheswm…"</string>
|
||||
<string name="screen_report_room_title">"Adrodd ar ystafell"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Din anmeldelse blev indsendt med succes, men vi stødte på et problem, da vi forsøgte at forlade rummet. Prøv venligst igen."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Ude af stand til at forlade rummet"</string>
|
||||
<string name="screen_report_room_reason_footer">"Anmeld dette rum til din administrator. Hvis meddelelserne er krypteret, kan din administrator ikke læse dem."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Beskriv årsagen til anmeldelsen…"</string>
|
||||
<string name="screen_report_room_title">"Anmeld rummet"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Deine Meldung wurde erfolgreich übermittelt. Beim Versuch, den Chat zu verlassen, ist allerdings ein Problem aufgetreten. Bitte versuche es erneut."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Der Chat kann nicht verlassen werden"</string>
|
||||
<string name="screen_report_room_reason_footer">"Melde diesen Chat deinem Administrator. Wenn die Nachrichten verschlüsselt sind, kann dein Administrator sie nicht lesen."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Beschreibe den Grund für die Meldung…"</string>
|
||||
<string name="screen_report_room_title">"Chat melden"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Η αναφορά σας υποβλήθηκε με επιτυχία, αλλά αντιμετωπίσαμε ένα πρόβλημα κατά την προσπάθεια εξόδου από την αίθουσα. Παρακαλώ προσπαθήστε ξανά."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Δεν είναι δυνατή η έξοδος από την αίθουσα"</string>
|
||||
<string name="screen_report_room_reason_footer">"Αναφέρετε αυτήν την αίθουσα στον διαχειριστή σας. Εάν τα μηνύματα είναι κρυπτογραφημένα, ο διαχειριστής σας δεν θα μπορεί να τα διαβάσει."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Περιγράψτε τον λόγο αναφοράς…"</string>
|
||||
<string name="screen_report_room_title">"Αναφορά αίθουσας"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Tu denuncia se ha enviado correctamente, pero hemos encontrado un problema al intentar salir de la sala. Inténtalo de nuevo."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"No se pudo salir de la sala"</string>
|
||||
<string name="screen_report_room_reason_footer">"Denuncia esta sala a tu administrador. Si los mensajes están cifrados, tu administrador no podrá leerlos."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Describe el motivo de la denuncia…"</string>
|
||||
<string name="screen_report_room_title">"Denunciar sala"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Jututoast haldajale teatamine õnnestus, kuid jututost lahkumisel tekkis viga. Palun proovi uuesti lahkuda."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Pole võimalik lahkuda jututoast"</string>
|
||||
<string name="screen_report_room_reason_footer">"Teata sellest jututoast süsteemi haldajale. Kui sõnumid on krüptitud, ei saa haldaja neid lugeda."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Kirjelda põhjust…"</string>
|
||||
<string name="screen_report_room_title">"Teata jututoast"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_title">"Salatu gela"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_title">"ناتوان از ترک اتاق"</string>
|
||||
<string name="screen_report_room_reason_placeholder">"شرح دلیل گزارش…"</string>
|
||||
<string name="screen_report_room_title">"گزارش اتاق"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Ilmoituksesi lähetettiin onnistuneesti, mutta kohtasimme ongelman yrittäessämme poistua huoneesta. Yritä uudelleen."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Huoneesta poistuminen epäonnistui"</string>
|
||||
<string name="screen_report_room_reason_footer">"Ilmoita tästä huoneesta palvelimesi ylläpitäjälle. Jos viestit on salattu, ylläpitäjäsi ei voi lukea niitä."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Kuvaile syytä…"</string>
|
||||
<string name="screen_report_room_title">"Ilmoita huoneesta"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Votre rapport a été envoyé avec succès, mais nous avons rencontré un problème en essayant de quitter le salon. Veuillez réessayer."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Impossible de quitter le salon"</string>
|
||||
<string name="screen_report_room_reason_footer">"Signaler ce salon à votre admin. Si les messages sont chiffrés, votre admin ne pourra pas les lire."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Décrivez la raison du signalement…"</string>
|
||||
<string name="screen_report_room_title">"Signaler le salon"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"A jelentése sikeresen el lett küldve, de hibát találtunk a szoba elhagyása során. Próbálja újra."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Nem tudja elhagyni a szobát"</string>
|
||||
<string name="screen_report_room_reason_footer">"A szoba jelentése az adminisztrátoroknak. Ha az üzenetek titkosítva vannak, akkor az adminisztrátor nem fogja tudni elolvasni őket."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Írja le az okot…"</string>
|
||||
<string name="screen_report_room_title">"Szoba jelentése"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Laporan Anda berhasil dikirimkan, tetapi kami mengalami masalah saat mencoba meninggalkan ruangan. Silakan coba lagi."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Tidak Dapat Meninggalkan Ruangan"</string>
|
||||
<string name="screen_report_room_reason_footer">"Laporkan ruangan ini ke admin Anda. Jika pesan dienkripsi, admin Anda tidak akan dapat membacanya."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Jelaskan alasan untuk melaporkan…"</string>
|
||||
<string name="screen_report_room_title">"Laporkan ruangan"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"La tua segnalazione è stata inviata con successo, ma abbiamo riscontrato un problema durante il tentativo di lasciare la stanza. Per favore riprova."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Impossibile lasciare la stanza"</string>
|
||||
<string name="screen_report_room_reason_footer">"Segnala questa stanza al tuo amministratore. Se i messaggi sono cifrati, l\'amministratore non sarà in grado di leggerli."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Descrivi il motivo della segnalazione…"</string>
|
||||
<string name="screen_report_room_title">"Segnala stanza"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"신고가 성공적으로 제출되었지만, 방을 나가려고 하는 중에 문제가 발생했습니다. 다시 시도해 주세요."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"방을 나갈 수 없습니다"</string>
|
||||
<string name="screen_report_room_reason_footer">"이 방을 관리자에게 신고하세요. 메시지가 암호화되어 있는 경우, 관리자는 메시지를 읽을 수 없습니다."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"신고 사유를 설명하세요…"</string>
|
||||
<string name="screen_report_room_title">"방 신고"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Rapporten din ble sendt inn, men vi oppdaget et problem da vi prøvde å forlate rommet. Prøv igjen."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Kan ikke forlate rommet"</string>
|
||||
<string name="screen_report_room_reason_footer">"Rapporter dette rommet til administratoren din. Hvis meldingene er kryptert, vil administratoren ikke kunne lese dem."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Beskriv årsaken…"</string>
|
||||
<string name="screen_report_room_title">"Rapporter rommet"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_title">"Kamer melden"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Twoje zgłoszenie zostało wysłane pomyślnie, ale napotkaliśmy problem podczas opuszczania pokoju. Spróbuj ponownie."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Nie można wyjść z pokoju"</string>
|
||||
<string name="screen_report_room_reason_footer">"Zgłoś ten pokój swojemu administratorowi. Jeśli wiadomości są zaszyfrowane, administrator nie będzie mógł ich odczytać."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Opisz powód…"</string>
|
||||
<string name="screen_report_room_title">"Zgłoś pokój"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Sua denúncia foi enviada com sucesso, mas encontramos um problema ao tentar sair da sala. Tente novamente."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Não foi possível sair da sala"</string>
|
||||
<string name="screen_report_room_reason_footer">"Denuncie esta sala ao seu administrador. Se as mensagens estiverem criptografadas, seu administrador não poderá lê-las."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Descreva o motivo para denunciar…"</string>
|
||||
<string name="screen_report_room_title">"Denunciar sala"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"O teu relatório foi submetido com sucesso, mas houve um problema ao tentar sair da sala. Por favor, tenta novamente."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Não foi possível sair da sala"</string>
|
||||
<string name="screen_report_room_reason_footer">"Denuncia esta sala aos administradores. Se as mensagens estiverem cifradas, os administradores não as poderão ler."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Descreve a razão para denunciar…"</string>
|
||||
<string name="screen_report_room_title">"Denunciar sala"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Raportul dumneavoastră a fost trimis cu succes, dar am întâmpinat o problemă în timp ce încercam să părăsim camera. Vă rugăm să încercați din nou."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Nu s-a putut părăsi camera"</string>
|
||||
<string name="screen_report_room_reason_footer">"Raportați această cameră administratorului. Dacă mesaje sunt criptate, administratorul nu le va putea citi."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Descrieți motivul raportării…"</string>
|
||||
<string name="screen_report_room_title">"Raportați camera"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Ваш отчет был успешно отправлен, но мы столкнулись с проблемой при попытке покинуть комнату. Пожалуйста, попробуйте еще раз."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Невозможно покинуть комнату"</string>
|
||||
<string name="screen_report_room_reason_footer">"Сообщите об этой комнате своему администратору. Если сообщения зашифрованы, ваш администратор не сможет их прочитать."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Опишите причину жалобы…"</string>
|
||||
<string name="screen_report_room_title">"Комната отчетов"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Vaša správa bola úspešne odoslaná, ale pri pokuse o opustenie miestnosti sme narazili na problém. Skúste to prosím znova."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Nie je možné opustiť miestnosť"</string>
|
||||
<string name="screen_report_room_reason_footer">"Nahláste túto miestnosť svojmu správcovi. Ak sú správy zašifrované, váš správca ich nebude môcť prečítať."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Popíšte dôvod…"</string>
|
||||
<string name="screen_report_room_title">"Nahlásiť miestnosť"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Din anmälan skickades in framgångsrikt, men vi stötte på ett problem när vi försökte lämna rummet. Vänligen försök igen."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Kunde inte lämna rummet"</string>
|
||||
<string name="screen_report_room_reason_footer">"Anmäl det här rummet till din administratör. Om meddelandena är krypterade kommer din administratör inte att kunna läsa dem."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Beskriv anledningen …"</string>
|
||||
<string name="screen_report_room_title">"Anmäl rum"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Ваша скарга надіслана, але ми зіткнулися з проблемою під час спроби вийти з кімнати. Повторіть спробу."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Не вдалося вийти з кімнати"</string>
|
||||
<string name="screen_report_room_reason_footer">"Поскаржтеся на цю кімнату своєму адміністратору. Якщо повідомлення зашифровані, ваш адміністратор не зможе їх прочитати."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Опишіть причину…"</string>
|
||||
<string name="screen_report_room_title">"Поскаржитися на кімнату"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Hisobotingiz muvaffaqiyatli yuborildi, ammo xonadan chiqishda muammo yuzaga keldi. Iltimos, qaytadan urinib ko‘ring."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Xonani tark etish imkonsiz"</string>
|
||||
<string name="screen_report_room_reason_footer">"Bu xona haqida administratoringizga xabar bering. Agar xabarlar shifrlangan bo‘lsa, administratoringiz ularni o‘qiy olmaydi."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Xabar berish sababini tushuntiring…"</string>
|
||||
<string name="screen_report_room_title">"Xona ustidan shikoyat qilish"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"您的回報已成功遞交,但我們嘗試離開聊天室時遇到了問題。請再試一次。"</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"無法離開聊天室"</string>
|
||||
<string name="screen_report_room_reason_footer">"將此聊天室回報給您的管理員。若訊息已加密,您的管理員將無法讀取它們。"</string>
|
||||
<string name="screen_report_room_reason_placeholder">"說明回報的原因……"</string>
|
||||
<string name="screen_report_room_title">"回報聊天室"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"您的报告已成功提交,但在尝试离开房间时遇到了问题。请重试。"</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"无法离开房间"</string>
|
||||
<string name="screen_report_room_reason_footer">"向管理员举报此房间。如果信息已加密,管理员将无法读取。"</string>
|
||||
<string name="screen_report_room_reason_placeholder">"描述举报的原因…"</string>
|
||||
<string name="screen_report_room_title">"举报房间"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_report_room_leave_failed_alert_message">"Your report was submitted successfully, but we encountered an issue while trying to leave the room. Please try again."</string>
|
||||
<string name="screen_report_room_leave_failed_alert_title">"Unable to Leave Room"</string>
|
||||
<string name="screen_report_room_reason_footer">"Report this room to your admin. If the messages are encrypted, your admin will not be able to read them."</string>
|
||||
<string name="screen_report_room_reason_placeholder">"Describe the reason to report…"</string>
|
||||
<string name="screen_report_room_title">"Report room"</string>
|
||||
</resources>
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.tests.testutils.node.TestParentNode
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultReportRoomEntryPointTest {
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Test
|
||||
fun `test node builder`() {
|
||||
val entryPoint = DefaultReportRoomEntryPoint()
|
||||
val parentNode = TestParentNode.create { buildContext, plugins ->
|
||||
ReportRoomNode(
|
||||
buildContext = buildContext,
|
||||
plugins = plugins,
|
||||
presenterFactory = { roomId ->
|
||||
assertThat(roomId).isEqualTo(A_ROOM_ID)
|
||||
createReportRoomPresenter()
|
||||
}
|
||||
)
|
||||
}
|
||||
val result = entryPoint.createNode(
|
||||
parentNode = parentNode,
|
||||
buildContext = BuildContext.root(null),
|
||||
roomId = A_ROOM_ID,
|
||||
)
|
||||
assertThat(result).isInstanceOf(ReportRoomNode::class.java)
|
||||
assertThat(result.plugins).contains(ReportRoomNode.Inputs(A_ROOM_ID))
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
|
||||
import io.element.android.tests.testutils.lambda.assert
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultReportRoomTest {
|
||||
private val roomId = A_ROOM_ID
|
||||
private val successLeaveRoomLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
||||
private val successReportRoomLambda =
|
||||
lambdaRecorder<String?, Result<Unit>> { _ -> Result.success(Unit) }
|
||||
|
||||
private val failureLeaveRoomLambda =
|
||||
lambdaRecorder<Result<Unit>> { Result.failure(Exception("Leave room error")) }
|
||||
private val failureReportRoomLambda =
|
||||
lambdaRecorder<String?, Result<Unit>> { _ -> Result.failure(Exception("Report room error")) }
|
||||
|
||||
@Test
|
||||
fun `report room, leave=false, report=false, nothing is called`() = runTest {
|
||||
val room = FakeBaseRoom(
|
||||
roomId = roomId,
|
||||
leaveRoomLambda = successLeaveRoomLambda,
|
||||
reportRoomResult = successReportRoomLambda
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(roomId, room)
|
||||
}
|
||||
val reportRoom = DefaultReportRoom(client = client)
|
||||
|
||||
val result = reportRoom(roomId, shouldReport = false, reason = "", shouldLeave = false)
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
assert(successLeaveRoomLambda).isNeverCalled()
|
||||
assert(successReportRoomLambda).isNeverCalled()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report room, leave=false, report=true, report room success`() = runTest {
|
||||
val room = FakeBaseRoom(
|
||||
roomId = roomId,
|
||||
leaveRoomLambda = successLeaveRoomLambda,
|
||||
reportRoomResult = successReportRoomLambda
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(roomId, room)
|
||||
}
|
||||
val reportRoom = DefaultReportRoom(client = client)
|
||||
|
||||
val result = reportRoom(roomId, shouldReport = true, reason = "Spam", shouldLeave = false)
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
assert(successLeaveRoomLambda).isNeverCalled()
|
||||
assert(successReportRoomLambda)
|
||||
.isCalledOnce()
|
||||
.with(value("Spam"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report room, leave=true, report=false, leave room success`() = runTest {
|
||||
val room = FakeBaseRoom(
|
||||
roomId = roomId,
|
||||
leaveRoomLambda = successLeaveRoomLambda,
|
||||
reportRoomResult = successReportRoomLambda
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(roomId, room)
|
||||
}
|
||||
val reportRoom = DefaultReportRoom(client = client)
|
||||
|
||||
val result = reportRoom(roomId, shouldReport = false, reason = "", shouldLeave = true)
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
assert(successLeaveRoomLambda).isCalledOnce()
|
||||
assert(successReportRoomLambda).isNeverCalled()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report room, leave=true, report=true, leave room success`() = runTest {
|
||||
val room = FakeBaseRoom(
|
||||
roomId = roomId,
|
||||
leaveRoomLambda = successLeaveRoomLambda,
|
||||
reportRoomResult = successReportRoomLambda
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(roomId, room)
|
||||
}
|
||||
val reportRoom = DefaultReportRoom(client = client)
|
||||
|
||||
val result = reportRoom(roomId, shouldReport = true, reason = "Spam", shouldLeave = true)
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
assert(successLeaveRoomLambda).isCalledOnce()
|
||||
assert(successReportRoomLambda)
|
||||
.isCalledOnce()
|
||||
.with(value("Spam"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report room, leave=true, report=true, leave room failed`() = runTest {
|
||||
val room = FakeBaseRoom(
|
||||
roomId = roomId,
|
||||
leaveRoomLambda = failureLeaveRoomLambda,
|
||||
reportRoomResult = successReportRoomLambda
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(roomId, room)
|
||||
}
|
||||
val reportRoom = DefaultReportRoom(client = client)
|
||||
|
||||
val result = reportRoom(roomId, shouldReport = true, reason = "Spam", shouldLeave = true)
|
||||
|
||||
assertThat(result.isFailure).isTrue()
|
||||
assertThat(result.exceptionOrNull()).isEqualTo(ReportRoom.Exception.LeftRoomFailed)
|
||||
assert(failureLeaveRoomLambda).isCalledOnce()
|
||||
assert(successReportRoomLambda).isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report room, leave=true, report=true, report room failed`() = runTest {
|
||||
val room = FakeBaseRoom(
|
||||
roomId = roomId,
|
||||
leaveRoomLambda = successLeaveRoomLambda,
|
||||
reportRoomResult = failureReportRoomLambda
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(roomId, room)
|
||||
}
|
||||
val reportRoom = DefaultReportRoom(client = client)
|
||||
|
||||
val result = reportRoom(roomId, shouldReport = true, reason = "Spam", shouldLeave = true)
|
||||
|
||||
assertThat(result.isFailure).isTrue()
|
||||
assertThat(result.exceptionOrNull()).isEqualTo(ReportRoom.Exception.ReportRoomFailed)
|
||||
assert(successLeaveRoomLambda).isNeverCalled()
|
||||
assert(failureReportRoomLambda).isCalledOnce()
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.reportroom.impl.fakes.FakeReportRoom
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.tests.testutils.lambda.any
|
||||
import io.element.android.tests.testutils.lambda.assert
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class ReportRoomPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createReportRoomPresenter()
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reason).isEmpty()
|
||||
assertThat(state.leaveRoom).isFalse()
|
||||
assertThat(state.reportAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
assertThat(state.canReport).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - update form values`() = runTest {
|
||||
val presenter = createReportRoomPresenter()
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reason).isEmpty()
|
||||
assertThat(state.canReport).isFalse()
|
||||
assertThat(state.leaveRoom).isFalse()
|
||||
state.eventSink(ReportRoomEvents.UpdateReason("Spam"))
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reason).isEqualTo("Spam")
|
||||
assertThat(state.canReport).isTrue()
|
||||
assertThat(state.leaveRoom).isFalse()
|
||||
state.eventSink(ReportRoomEvents.ToggleLeaveRoom)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.leaveRoom).isTrue()
|
||||
assertThat(state.canReport).isTrue()
|
||||
assertThat(state.canReport).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - report room success`() = runTest {
|
||||
val roomId = A_ROOM_ID
|
||||
val reportRoomLambda = lambdaRecorder<RoomId, Boolean, String, Boolean, Result<Unit>> { _, _, _, _ -> Result.success(Unit) }
|
||||
val reportRoom = FakeReportRoom(
|
||||
lambda = reportRoomLambda
|
||||
)
|
||||
val presenter = createReportRoomPresenter(roomId = roomId, reportRoom = reportRoom)
|
||||
presenter.test {
|
||||
awaitItem().eventSink(ReportRoomEvents.ToggleLeaveRoom)
|
||||
awaitItem().eventSink(ReportRoomEvents.Report)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
}
|
||||
assert(reportRoomLambda)
|
||||
.isCalledOnce()
|
||||
.with(value(roomId), value(true), any(), value(true))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - report failed`() = runTest {
|
||||
val roomId = A_ROOM_ID
|
||||
val reportRoomLambda = lambdaRecorder<RoomId, Boolean, String, Boolean, Result<Unit>> { _, _, _, _ ->
|
||||
Result.failure(ReportRoom.Exception.ReportRoomFailed)
|
||||
}
|
||||
val reportRoom = FakeReportRoom(
|
||||
lambda = reportRoomLambda
|
||||
)
|
||||
val presenter = createReportRoomPresenter(roomId = roomId, reportRoom = reportRoom)
|
||||
presenter.test {
|
||||
awaitItem().eventSink(ReportRoomEvents.Report)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
}
|
||||
assert(reportRoomLambda)
|
||||
.isCalledOnce()
|
||||
.with(value(roomId), value(true), any(), any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - leave room failed after report room success`() = runTest {
|
||||
val roomId = A_ROOM_ID
|
||||
val reportRoomLambda = lambdaRecorder<RoomId, Boolean, String, Boolean, Result<Unit>> { _, _, _, _ ->
|
||||
Result.failure(ReportRoom.Exception.LeftRoomFailed)
|
||||
}
|
||||
val reportRoom = FakeReportRoom(
|
||||
lambda = reportRoomLambda
|
||||
)
|
||||
val presenter = createReportRoomPresenter(roomId = roomId, reportRoom = reportRoom)
|
||||
presenter.test {
|
||||
awaitItem().eventSink(ReportRoomEvents.ToggleLeaveRoom)
|
||||
awaitItem().eventSink(ReportRoomEvents.Report)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(ReportRoomEvents.Report)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
}
|
||||
assert(reportRoomLambda)
|
||||
.isCalledExactly(2)
|
||||
.withSequence(
|
||||
// The first call should report the room and try leaving it
|
||||
listOf(value(roomId), value(true), any(), value(true)),
|
||||
// The second call should not report the room again
|
||||
listOf(value(roomId), value(false), any(), value(true))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createReportRoomPresenter(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
reportRoom: ReportRoom = FakeReportRoom()
|
||||
): ReportRoomPresenter {
|
||||
return ReportRoomPresenter(roomId, reportRoom)
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
import io.element.android.tests.testutils.ensureCalledOnce
|
||||
import io.element.android.tests.testutils.pressBack
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ReportRoomViewTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `clicking on back invoke the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<ReportRoomEvents>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
rule.setReportRoomView(
|
||||
aReportRoomState(
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
onBackClick = it
|
||||
)
|
||||
rule.pressBack()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on report when enabled emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<ReportRoomEvents>()
|
||||
rule.setReportRoomView(
|
||||
aReportRoomState(
|
||||
reason = "Spam",
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_report)
|
||||
eventsRecorder.assertSingle(ReportRoomEvents.Report)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on decline when disabled does not emit event`() {
|
||||
val eventsRecorder = EventsRecorder<ReportRoomEvents>(expectEvents = false)
|
||||
rule.setReportRoomView(
|
||||
aReportRoomState(eventSink = eventsRecorder),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_report)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on leave room option emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<ReportRoomEvents>()
|
||||
rule.setReportRoomView(
|
||||
aReportRoomState(eventSink = eventsRecorder),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_leave_room)
|
||||
eventsRecorder.assertSingle(ReportRoomEvents.ToggleLeaveRoom)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `typing text in the reason field emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<ReportRoomEvents>()
|
||||
rule.setReportRoomView(
|
||||
aReportRoomState(
|
||||
eventSink = eventsRecorder,
|
||||
reason = ""
|
||||
),
|
||||
)
|
||||
rule.onNodeWithText("").performTextInput("Spam!")
|
||||
eventsRecorder.assertSingle(ReportRoomEvents.UpdateReason("Spam!"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setReportRoomView(
|
||||
state: ReportRoomState,
|
||||
onBackClick: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
setContent {
|
||||
ReportRoomView(
|
||||
state = state,
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.reportroom.impl.fakes
|
||||
|
||||
import io.element.android.features.reportroom.impl.ReportRoom
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakeReportRoom(
|
||||
var lambda: (RoomId, Boolean, String, Boolean) -> Result<Unit> = { _, _, _, _ -> lambdaError() }
|
||||
) : ReportRoom {
|
||||
override suspend fun invoke(
|
||||
roomId: RoomId,
|
||||
shouldReport: Boolean,
|
||||
reason: String,
|
||||
shouldLeave: Boolean
|
||||
): Result<Unit> = simulateLongTask {
|
||||
lambda(roomId, shouldReport, reason, shouldLeave)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user