First Commit
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import extension.setupDependencyInjection
|
||||
import extension.testCommonDependencies
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023, 2024 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.features.invite.impl"
|
||||
testOptions {
|
||||
unitTests {
|
||||
isIncludeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupDependencyInjection()
|
||||
|
||||
dependencies {
|
||||
api(projects.features.invite.api)
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
implementation(projects.libraries.androidutils)
|
||||
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)
|
||||
implementation(projects.services.analytics.api)
|
||||
implementation(projects.libraries.push.api)
|
||||
|
||||
testCommonDependencies(libs, true)
|
||||
testImplementation(projects.features.invite.test)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.push.test)
|
||||
testImplementation(projects.services.analytics.test)
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.invite.impl
|
||||
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.libraries.core.extensions.mapFailure
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.exception.ClientException
|
||||
import io.element.android.libraries.matrix.api.exception.ErrorKind
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRoom
|
||||
import io.element.android.libraries.push.api.notifications.NotificationCleaner
|
||||
|
||||
interface AcceptInvite {
|
||||
suspend operator fun invoke(roomId: RoomId): Result<RoomId>
|
||||
|
||||
sealed class Failures : Exception() {
|
||||
data object InvalidInvite : Failures()
|
||||
}
|
||||
}
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultAcceptInvite(
|
||||
private val client: MatrixClient,
|
||||
private val joinRoom: JoinRoom,
|
||||
private val notificationCleaner: NotificationCleaner,
|
||||
private val seenInvitesStore: SeenInvitesStore,
|
||||
) : AcceptInvite {
|
||||
override suspend fun invoke(roomId: RoomId): Result<RoomId> {
|
||||
return joinRoom(
|
||||
roomIdOrAlias = roomId.toRoomIdOrAlias(),
|
||||
serverNames = emptyList(),
|
||||
trigger = JoinedRoom.Trigger.Invite,
|
||||
).onSuccess {
|
||||
notificationCleaner.clearMembershipNotificationForRoom(client.sessionId, roomId)
|
||||
seenInvitesStore.markAsUnSeen(roomId)
|
||||
}.mapFailure {
|
||||
if (it is ClientException.MatrixApi) {
|
||||
when (it.kind) {
|
||||
ErrorKind.Unknown -> AcceptInvite.Failures.InvalidInvite
|
||||
else -> it
|
||||
}
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}.map { roomId }
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.invite.impl
|
||||
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.push.api.notifications.NotificationCleaner
|
||||
|
||||
interface DeclineInvite {
|
||||
suspend operator fun invoke(
|
||||
roomId: RoomId,
|
||||
blockUser: Boolean,
|
||||
reportRoom: Boolean,
|
||||
reportReason: String?
|
||||
): Result<RoomId>
|
||||
|
||||
sealed class Exception : kotlin.Exception() {
|
||||
data object RoomNotFound : Exception()
|
||||
data object DeclineInviteFailed : Exception()
|
||||
data object ReportRoomFailed : Exception()
|
||||
data object BlockUserFailed : Exception()
|
||||
}
|
||||
}
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultDeclineInvite(
|
||||
private val client: MatrixClient,
|
||||
private val notificationCleaner: NotificationCleaner,
|
||||
private val seenInvitesStore: SeenInvitesStore,
|
||||
) : DeclineInvite {
|
||||
override suspend fun invoke(
|
||||
roomId: RoomId,
|
||||
blockUser: Boolean,
|
||||
reportRoom: Boolean,
|
||||
reportReason: String?
|
||||
): Result<RoomId> {
|
||||
val room = client.getRoom(roomId) ?: return Result.failure(DeclineInvite.Exception.RoomNotFound)
|
||||
room.use {
|
||||
room.leave()
|
||||
.onFailure { return Result.failure(DeclineInvite.Exception.DeclineInviteFailed) }
|
||||
.onSuccess {
|
||||
notificationCleaner.clearMembershipNotificationForRoom(
|
||||
sessionId = client.sessionId,
|
||||
roomId = roomId
|
||||
)
|
||||
seenInvitesStore.markAsUnSeen(roomId)
|
||||
}
|
||||
|
||||
if (blockUser) {
|
||||
val userIdToBlock = room.info().inviter?.userId
|
||||
if (userIdToBlock != null) {
|
||||
client
|
||||
.ignoreUser(userIdToBlock)
|
||||
.onFailure { return Result.failure(DeclineInvite.Exception.BlockUserFailed) }
|
||||
}
|
||||
}
|
||||
if (reportRoom) {
|
||||
room
|
||||
.reportRoom(reportReason)
|
||||
.onFailure { return Result.failure(DeclineInvite.Exception.ReportRoomFailed) }
|
||||
}
|
||||
}
|
||||
return Result.success(roomId)
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.invite.impl
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringSetPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStoreFile
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.libraries.androidutils.file.safeDelete
|
||||
import io.element.android.libraries.androidutils.hash.hash
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.sessionstorage.api.observer.SessionListener
|
||||
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
private val seenInvitesKey = stringSetPreferencesKey("seenInvites")
|
||||
|
||||
class DefaultSeenInvitesStore(
|
||||
context: Context,
|
||||
sessionId: SessionId,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
sessionObserver: SessionObserver,
|
||||
) : SeenInvitesStore {
|
||||
init {
|
||||
sessionObserver.addListener(object : SessionListener {
|
||||
override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {
|
||||
if (sessionId.value == userId) {
|
||||
clear()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private val dataStoreFile = sessionId.value.hash().take(16).let { hashedUserId ->
|
||||
context.preferencesDataStoreFile("session_${hashedUserId}_seen-invites")
|
||||
}
|
||||
|
||||
private val store = PreferenceDataStoreFactory.create(
|
||||
scope = sessionCoroutineScope,
|
||||
migrations = emptyList(),
|
||||
) {
|
||||
dataStoreFile
|
||||
}
|
||||
|
||||
override fun seenRoomIds(): Flow<Set<RoomId>> =
|
||||
store.data.map { prefs ->
|
||||
prefs[seenInvitesKey]
|
||||
.orEmpty()
|
||||
.map { RoomId(it) }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override suspend fun markAsSeen(roomId: RoomId) {
|
||||
store.edit { prefs ->
|
||||
prefs[seenInvitesKey] = prefs[seenInvitesKey].orEmpty() + roomId.value
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun markAsUnSeen(roomId: RoomId) {
|
||||
store.edit { prefs ->
|
||||
prefs[seenInvitesKey] = prefs[seenInvitesKey].orEmpty() - roomId.value
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun clear() {
|
||||
dataStoreFile.safeDelete()
|
||||
}
|
||||
}
|
||||
+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.invite.impl
|
||||
|
||||
import android.content.Context
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
@SingleIn(AppScope::class)
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultSeenInvitesStoreFactory(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val sessionObserver: SessionObserver,
|
||||
) : SeenInvitesStoreFactory {
|
||||
// We can have only one class accessing a single data store, so keep a cache of them.
|
||||
private val cache = ConcurrentHashMap<SessionId, SeenInvitesStore>()
|
||||
|
||||
override fun getOrCreate(
|
||||
sessionId: SessionId,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
): SeenInvitesStore {
|
||||
return cache.getOrPut(sessionId) {
|
||||
DefaultSeenInvitesStore(
|
||||
context = context,
|
||||
sessionId = sessionId,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
sessionObserver = sessionObserver,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+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.invite.impl
|
||||
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
interface SeenInvitesStoreFactory {
|
||||
fun getOrCreate(
|
||||
sessionId: SessionId,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
): SeenInvitesStore
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 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.invite.impl.acceptdecline
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.ConfirmingDeclineInvite
|
||||
import io.element.android.features.invite.impl.AcceptInvite
|
||||
import io.element.android.features.invite.impl.DeclineInvite
|
||||
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
|
||||
|
||||
@Inject
|
||||
class AcceptDeclineInvitePresenter(
|
||||
private val acceptInvite: AcceptInvite,
|
||||
private val declineInvite: DeclineInvite,
|
||||
) : Presenter<AcceptDeclineInviteState> {
|
||||
@Composable
|
||||
override fun present(): AcceptDeclineInviteState {
|
||||
val localCoroutineScope = rememberCoroutineScope()
|
||||
val acceptedAction: MutableState<AsyncAction<RoomId>> =
|
||||
remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
val declinedAction: MutableState<AsyncAction<RoomId>> =
|
||||
remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
|
||||
fun handleEvent(event: AcceptDeclineInviteEvents) {
|
||||
when (event) {
|
||||
is AcceptDeclineInviteEvents.AcceptInvite -> {
|
||||
localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction)
|
||||
}
|
||||
|
||||
is AcceptDeclineInviteEvents.DeclineInvite -> {
|
||||
val inviteData = event.invite
|
||||
if (event.shouldConfirm) {
|
||||
declinedAction.value = ConfirmingDeclineInvite(inviteData, event.blockUser)
|
||||
} else {
|
||||
localCoroutineScope.declineInvite(
|
||||
inviteData = inviteData,
|
||||
blockUser = event.blockUser,
|
||||
declinedAction = declinedAction,
|
||||
)
|
||||
}
|
||||
}
|
||||
is InternalAcceptDeclineInviteEvents.ClearAcceptActionState -> {
|
||||
acceptedAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
|
||||
is InternalAcceptDeclineInviteEvents.ClearDeclineActionState -> {
|
||||
declinedAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AcceptDeclineInviteState(
|
||||
acceptAction = acceptedAction.value,
|
||||
declineAction = declinedAction.value,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.acceptInvite(
|
||||
roomId: RoomId,
|
||||
acceptedAction: MutableState<AsyncAction<RoomId>>,
|
||||
) = launch {
|
||||
acceptedAction.runUpdatingState {
|
||||
acceptInvite(roomId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.declineInvite(
|
||||
inviteData: InviteData,
|
||||
blockUser: Boolean,
|
||||
declinedAction: MutableState<AsyncAction<RoomId>>,
|
||||
) = launch {
|
||||
declinedAction.runUpdatingState {
|
||||
declineInvite(
|
||||
roomId = inviteData.roomId,
|
||||
blockUser = blockUser,
|
||||
reportRoom = false,
|
||||
reportReason = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.invite.impl.acceptdecline
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.ConfirmingDeclineInvite
|
||||
import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState
|
||||
import io.element.android.features.invite.impl.AcceptInvite
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
open class AcceptDeclineInviteStateProvider : PreviewParameterProvider<AcceptDeclineInviteState> {
|
||||
override val values: Sequence<AcceptDeclineInviteState>
|
||||
get() = sequenceOf(
|
||||
anAcceptDeclineInviteState(),
|
||||
anAcceptDeclineInviteState(
|
||||
declineAction = ConfirmingDeclineInvite(
|
||||
InviteData(
|
||||
roomId = RoomId("!room:matrix.org"),
|
||||
isDm = true,
|
||||
roomName = "Alice"
|
||||
),
|
||||
blockUser = false,
|
||||
),
|
||||
),
|
||||
anAcceptDeclineInviteState(
|
||||
declineAction = ConfirmingDeclineInvite(
|
||||
InviteData(
|
||||
roomId = RoomId("!room:matrix.org"),
|
||||
isDm = true,
|
||||
roomName = "Alice"
|
||||
),
|
||||
blockUser = true,
|
||||
),
|
||||
),
|
||||
anAcceptDeclineInviteState(
|
||||
acceptAction = AsyncAction.Failure(RuntimeException("Error while accepting invite")),
|
||||
),
|
||||
anAcceptDeclineInviteState(
|
||||
acceptAction = AsyncAction.Failure(AcceptInvite.Failures.InvalidInvite),
|
||||
),
|
||||
anAcceptDeclineInviteState(
|
||||
declineAction = AsyncAction.Failure(RuntimeException("Error while declining invite")),
|
||||
),
|
||||
)
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 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.invite.impl.acceptdecline
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.ConfirmingDeclineInvite
|
||||
import io.element.android.features.invite.impl.AcceptInvite
|
||||
import io.element.android.features.invite.impl.R
|
||||
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun AcceptDeclineInviteView(
|
||||
state: AcceptDeclineInviteState,
|
||||
onAcceptInviteSuccess: (RoomId) -> Unit,
|
||||
onDeclineInviteSuccess: (RoomId) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(modifier = modifier) {
|
||||
AsyncActionView(
|
||||
async = state.acceptAction,
|
||||
onSuccess = { roomId ->
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.ClearAcceptActionState)
|
||||
onAcceptInviteSuccess(roomId)
|
||||
},
|
||||
onErrorDismiss = {
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.ClearAcceptActionState)
|
||||
},
|
||||
errorTitle = {
|
||||
stringResource(CommonStrings.common_something_went_wrong)
|
||||
},
|
||||
errorMessage = { error ->
|
||||
if (error is AcceptInvite.Failures.InvalidInvite) {
|
||||
stringResource(CommonStrings.error_invalid_invite)
|
||||
} else {
|
||||
stringResource(CommonStrings.error_network_or_server_issue)
|
||||
}
|
||||
}
|
||||
)
|
||||
AsyncActionView(
|
||||
async = state.declineAction,
|
||||
onSuccess = { roomId ->
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.ClearDeclineActionState)
|
||||
onDeclineInviteSuccess(roomId)
|
||||
},
|
||||
onErrorDismiss = {
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.ClearDeclineActionState)
|
||||
},
|
||||
errorTitle = {
|
||||
stringResource(CommonStrings.common_something_went_wrong)
|
||||
},
|
||||
errorMessage = {
|
||||
stringResource(CommonStrings.error_network_or_server_issue)
|
||||
},
|
||||
confirmationDialog = { confirming ->
|
||||
// Note: confirming will always be of type ConfirmingDeclineInvite.
|
||||
if (confirming is ConfirmingDeclineInvite) {
|
||||
DeclineConfirmationDialog(
|
||||
invite = confirming.inviteData,
|
||||
blockUser = confirming.blockUser,
|
||||
onConfirmClick = {
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(
|
||||
confirming.inviteData,
|
||||
blockUser = confirming.blockUser,
|
||||
shouldConfirm = false
|
||||
)
|
||||
)
|
||||
},
|
||||
onDismissClick = {
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.ClearDeclineActionState)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DeclineConfirmationDialog(
|
||||
invite: InviteData,
|
||||
blockUser: Boolean,
|
||||
onConfirmClick: () -> Unit,
|
||||
onDismissClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
ConfirmationDialog(
|
||||
modifier = modifier,
|
||||
content = stringResource(R.string.screen_invites_decline_chat_message, invite.roomName),
|
||||
title = if (blockUser) {
|
||||
stringResource(R.string.screen_join_room_decline_and_block_alert_title)
|
||||
} else {
|
||||
stringResource(R.string.screen_invites_decline_chat_title)
|
||||
},
|
||||
submitText = if (blockUser) {
|
||||
stringResource(R.string.screen_join_room_decline_and_block_alert_confirmation)
|
||||
} else {
|
||||
stringResource(CommonStrings.action_decline)
|
||||
},
|
||||
cancelText = stringResource(CommonStrings.action_cancel),
|
||||
onSubmitClick = onConfirmClick,
|
||||
onDismiss = onDismissClick,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun AcceptDeclineInviteViewPreview(@PreviewParameter(AcceptDeclineInviteStateProvider::class) state: AcceptDeclineInviteState) =
|
||||
ElementPreview {
|
||||
AcceptDeclineInviteView(
|
||||
state = state,
|
||||
onAcceptInviteSuccess = {},
|
||||
onDeclineInviteSuccess = {},
|
||||
)
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 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.invite.impl.acceptdecline
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteView
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultAcceptDeclineInviteView : AcceptDeclineInviteView {
|
||||
@Composable
|
||||
override fun Render(
|
||||
state: AcceptDeclineInviteState,
|
||||
onAcceptInviteSuccess: (RoomId) -> Unit,
|
||||
onDeclineInviteSuccess: (RoomId) -> Unit,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
AcceptDeclineInviteView(
|
||||
state = state,
|
||||
onAcceptInviteSuccess = onAcceptInviteSuccess,
|
||||
onDeclineInviteSuccess = onDeclineInviteSuccess,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 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.invite.impl.acceptdecline
|
||||
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
|
||||
sealed interface InternalAcceptDeclineInviteEvents : AcceptDeclineInviteEvents {
|
||||
data object ClearAcceptActionState : InternalAcceptDeclineInviteEvents
|
||||
data object ClearDeclineActionState : InternalAcceptDeclineInviteEvents
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.invite.impl.declineandblock
|
||||
|
||||
sealed interface DeclineAndBlockEvents {
|
||||
data class UpdateReportReason(val reason: String) : DeclineAndBlockEvents
|
||||
data object ToggleReportRoom : DeclineAndBlockEvents
|
||||
data object ToggleBlockUser : DeclineAndBlockEvents
|
||||
data object Decline : DeclineAndBlockEvents
|
||||
data object ClearDeclineAction : DeclineAndBlockEvents
|
||||
}
|
||||
+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.invite.impl.declineandblock
|
||||
|
||||
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.features.invite.api.InviteData
|
||||
import io.element.android.libraries.architecture.NodeInputs
|
||||
import io.element.android.libraries.architecture.inputs
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
@AssistedInject
|
||||
class DeclineAndBlockNode(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
presenterFactory: DeclineAndBlockPresenter.Factory,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
data class Inputs(val inviteData: InviteData) : NodeInputs
|
||||
|
||||
private val inviteData = inputs<Inputs>().inviteData
|
||||
private val presenter = presenterFactory.create(inviteData)
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
DeclineAndBlockView(
|
||||
state = state,
|
||||
onBackClick = ::navigateUp,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.invite.impl.declineandblock
|
||||
|
||||
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.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.impl.DeclineInvite
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@AssistedInject
|
||||
class DeclineAndBlockPresenter(
|
||||
@Assisted private val inviteData: InviteData,
|
||||
private val declineInvite: DeclineInvite,
|
||||
private val snackbarDispatcher: SnackbarDispatcher,
|
||||
) : Presenter<DeclineAndBlockState> {
|
||||
@AssistedFactory
|
||||
fun interface Factory {
|
||||
fun create(inviteData: InviteData): DeclineAndBlockPresenter
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun present(): DeclineAndBlockState {
|
||||
var reportReason by rememberSaveable { mutableStateOf("") }
|
||||
var blockUser by rememberSaveable { mutableStateOf(true) }
|
||||
var reportRoom by rememberSaveable { mutableStateOf(false) }
|
||||
val declineAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
fun handleEvent(event: DeclineAndBlockEvents) {
|
||||
when (event) {
|
||||
DeclineAndBlockEvents.ClearDeclineAction -> declineAction.value = AsyncAction.Uninitialized
|
||||
DeclineAndBlockEvents.Decline -> coroutineScope.decline(reportReason, blockUser, reportRoom, declineAction)
|
||||
DeclineAndBlockEvents.ToggleBlockUser -> blockUser = !blockUser
|
||||
DeclineAndBlockEvents.ToggleReportRoom -> reportRoom = !reportRoom
|
||||
is DeclineAndBlockEvents.UpdateReportReason -> reportReason = event.reason
|
||||
}
|
||||
}
|
||||
|
||||
return DeclineAndBlockState(
|
||||
reportRoom = reportRoom,
|
||||
reportReason = reportReason,
|
||||
blockUser = blockUser,
|
||||
declineAction = declineAction.value,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.decline(
|
||||
reason: String,
|
||||
blockUser: Boolean,
|
||||
reportRoom: Boolean,
|
||||
action: MutableState<AsyncAction<Unit>>
|
||||
) = launch {
|
||||
action.value = AsyncAction.Loading
|
||||
declineInvite(
|
||||
roomId = inviteData.roomId,
|
||||
blockUser = blockUser,
|
||||
reportRoom = reportRoom,
|
||||
reportReason = reason
|
||||
).onSuccess {
|
||||
action.value = AsyncAction.Success(Unit)
|
||||
}.onFailure { error ->
|
||||
if (error is DeclineInvite.Exception.DeclineInviteFailed) {
|
||||
action.value = AsyncAction.Failure(error)
|
||||
} else {
|
||||
action.value = AsyncAction.Uninitialized
|
||||
snackbarDispatcher.post(SnackbarMessage(CommonStrings.error_unknown))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.invite.impl.declineandblock
|
||||
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
|
||||
data class DeclineAndBlockState(
|
||||
val reportRoom: Boolean,
|
||||
val reportReason: String,
|
||||
val blockUser: Boolean,
|
||||
val declineAction: AsyncAction<Unit>,
|
||||
val eventSink: (DeclineAndBlockEvents) -> Unit
|
||||
) {
|
||||
val canDecline = blockUser || reportRoom && reportReason.isNotEmpty()
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.invite.impl.declineandblock
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
|
||||
open class DeclineAndBlockStateProvider : PreviewParameterProvider<DeclineAndBlockState> {
|
||||
override val values: Sequence<DeclineAndBlockState>
|
||||
get() = sequenceOf(
|
||||
aDeclineAndBlockState(),
|
||||
aDeclineAndBlockState(
|
||||
reportRoom = true,
|
||||
reportReason = "Inappropriate content",
|
||||
),
|
||||
aDeclineAndBlockState(
|
||||
blockUser = true,
|
||||
),
|
||||
aDeclineAndBlockState(
|
||||
declineAction = AsyncAction.Loading,
|
||||
),
|
||||
aDeclineAndBlockState(
|
||||
declineAction = AsyncAction.Failure(Exception("Failed to decline")),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun aDeclineAndBlockState(
|
||||
reportRoom: Boolean = false,
|
||||
reportReason: String = "",
|
||||
blockUser: Boolean = false,
|
||||
declineAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
eventSink: (DeclineAndBlockEvents) -> Unit = {},
|
||||
) = DeclineAndBlockState(
|
||||
reportRoom = reportRoom,
|
||||
reportReason = reportReason,
|
||||
blockUser = blockUser,
|
||||
declineAction = declineAction,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.invite.impl.declineandblock
|
||||
|
||||
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.features.invite.impl.R
|
||||
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 DeclineAndBlockView(
|
||||
state: DeclineAndBlockState,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
val isDeclining = state.declineAction is AsyncAction.Loading
|
||||
AsyncActionView(
|
||||
async = state.declineAction,
|
||||
onSuccess = { onBackClick() },
|
||||
errorMessage = { stringResource(CommonStrings.error_unknown) },
|
||||
onRetry = { state.eventSink(DeclineAndBlockEvents.Decline) },
|
||||
onErrorDismiss = { state.eventSink(DeclineAndBlockEvents.ClearDeclineAction) }
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
titleStr = stringResource(R.string.screen_decline_and_block_title),
|
||||
navigationIcon = {
|
||||
BackButton(onClick = onBackClick)
|
||||
}
|
||||
)
|
||||
},
|
||||
modifier = modifier
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.imePadding()
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(vertical = 16.dp)
|
||||
) {
|
||||
ListItem(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
headlineContent = {
|
||||
Text(text = stringResource(R.string.screen_decline_and_block_block_user_option_title))
|
||||
},
|
||||
supportingContent = {
|
||||
Text(text = stringResource(R.string.screen_decline_and_block_block_user_option_description))
|
||||
},
|
||||
onClick = {
|
||||
state.eventSink(DeclineAndBlockEvents.ToggleBlockUser)
|
||||
},
|
||||
trailingContent = ListItemContent.Switch(checked = state.blockUser)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
ListItem(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
headlineContent = {
|
||||
Text(text = stringResource(CommonStrings.action_report_room))
|
||||
},
|
||||
supportingContent = {
|
||||
Text(text = stringResource(R.string.screen_decline_and_block_report_user_option_description))
|
||||
},
|
||||
onClick = {
|
||||
state.eventSink(DeclineAndBlockEvents.ToggleReportRoom)
|
||||
},
|
||||
trailingContent = ListItemContent.Switch(checked = state.reportRoom)
|
||||
)
|
||||
|
||||
if (state.reportRoom) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
TextField(
|
||||
value = state.reportReason,
|
||||
onValueChange = { state.eventSink(DeclineAndBlockEvents.UpdateReportReason(it)) },
|
||||
placeholder = stringResource(R.string.screen_decline_and_block_report_user_reason_placeholder),
|
||||
minLines = 3,
|
||||
enabled = !isDeclining,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.heightIn(min = 90.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_decline),
|
||||
destructive = true,
|
||||
showProgress = isDeclining,
|
||||
enabled = !isDeclining && state.canDecline,
|
||||
onClick = {
|
||||
focusManager.clearFocus(force = true)
|
||||
state.eventSink(DeclineAndBlockEvents.Decline)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun DeclineAndBlockViewPreview(
|
||||
@PreviewParameter(DeclineAndBlockStateProvider::class) state: DeclineAndBlockState
|
||||
) = ElementPreview {
|
||||
DeclineAndBlockView(
|
||||
state = state,
|
||||
onBackClick = {},
|
||||
)
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.invite.impl.declineandblock
|
||||
|
||||
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.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.declineandblock.DeclineInviteAndBlockEntryPoint
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultDeclineAndBlockEntryPoint : DeclineInviteAndBlockEntryPoint {
|
||||
override fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
inviteData: InviteData,
|
||||
): Node {
|
||||
val inputs = DeclineAndBlockNode.Inputs(inviteData)
|
||||
return parentNode.createNode<DeclineAndBlockNode>(buildContext, plugins = listOf(inputs))
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 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.invite.impl.di
|
||||
|
||||
import dev.zacsweers.metro.BindingContainer
|
||||
import dev.zacsweers.metro.Binds
|
||||
import dev.zacsweers.metro.ContributesTo
|
||||
import dev.zacsweers.metro.Provides
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.impl.SeenInvitesStoreFactory
|
||||
import io.element.android.features.invite.impl.acceptdecline.AcceptDeclineInvitePresenter
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
|
||||
@ContributesTo(SessionScope::class)
|
||||
@BindingContainer
|
||||
interface InviteModule {
|
||||
@Binds
|
||||
fun bindAcceptDeclinePresenter(presenter: AcceptDeclineInvitePresenter): Presenter<AcceptDeclineInviteState>
|
||||
|
||||
companion object {
|
||||
@Provides
|
||||
fun providesSeenInvitesStore(
|
||||
factory: SeenInvitesStoreFactory,
|
||||
matrixClient: MatrixClient,
|
||||
): SeenInvitesStore {
|
||||
return factory.getOrCreate(
|
||||
matrixClient.sessionId,
|
||||
matrixClient.sessionCoroutineScope,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Заблакіраваць карыстальніка"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Вы ўпэўненыя, што хочаце адхіліць запрашэнне ў %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Адхіліць запрашэнне"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Вы ўпэўненыя, што хочаце адмовіцца ад прыватных зносін з %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Адхіліць чат"</string>
|
||||
<string name="screen_invites_empty_list">"Няма запрашэнняў"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) запрасіў(-ла) вас"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Блокиране на потребителя"</string>
|
||||
<string name="screen_decline_and_block_title">"Отхвърляне и блокиране"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Сигурни ли сте, че искате да отхвърлите поканата за присъединяване в %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Отказване на покана"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Сигурни ли сте, че искате да откажете този личен чат с %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Отказване на чат"</string>
|
||||
<string name="screen_invites_empty_list">"Няма покани"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) ви покани"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Отхвърляне и блокиране"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Od tohoto uživatele neuvidíte žádné zprávy ani pozvánky do místnosti"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Zablokovat uživatele"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Nahlaste tuto místnost svému poskytovateli účtu."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Popište důvod…"</string>
|
||||
<string name="screen_decline_and_block_title">"Odmítnout a zablokovat"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Opravdu chcete odmítnout pozvánku do %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Odmítnout pozvání"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Opravdu chcete odmítnout tuto soukromou konverzaci s %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Odmítnout chat"</string>
|
||||
<string name="screen_invites_empty_list">"Žádné pozvánky"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) vás pozval(a)"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ano, odmítnout a zablokovat"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Opravdu chcete odmítnout pozvánku do této místnosti? Tím také zabráníte tomu, aby vás %1$s kontaktoval(a) nebo pozval(a) do místností."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Odmítnout pozvání a zablokovat"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Odmítnout a zablokovat"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Fyddwch chi ddim yn gweld unrhyw negeseuon neu wahoddiadau ystafell gan y defnyddiwr hwn"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Rhwystro defnyddiwr"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Adrodd am yr ystafell hon i ddarparwr eich cyfrif."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Disgrifiwch y rheswm…"</string>
|
||||
<string name="screen_decline_and_block_title">"Gwrthod a rhwystro"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Ydych chi\'n siŵr eich bod am wrthod y gwahoddiad i ymuno â %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Gwrthod y gwahoddiad"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Ydych chi\'n siŵr eich bod am wrthod y sgwrs breifat hon gyda %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Gwrthod sgwrs"</string>
|
||||
<string name="screen_invites_empty_list">"Dim Gwahoddiadau"</string>
|
||||
<string name="screen_invites_invited_you">"Mae %1$s (%2$s) wedi eich gwahodd"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Iawn, gwrthod a rhwystro"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Ydych chi\'n siŵr eich bod am wrthod y gwahoddiad i ymuno â\'r ystafell hon? Bydd hyn hefyd yn atal %1$s rhag cysylltu â chi neu eich gwahodd i ystafelloedd."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Gwrthod gwahoddiad a rhwystro"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Gwrthod a rhwystro"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Du vil ikke se nogen beskeder eller rum-invitationer fra denne bruger"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Bloker bruger"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Anmeld dette rum til din kontoudbyder."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Beskriv årsagen til anmeldelsen…"</string>
|
||||
<string name="screen_decline_and_block_title">"Afvis og blokér"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Er du sikker på, at du vil afvise invitationen til at deltage i %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Afvis invitation"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Er du sikker på, at du vil afvise denne private samtale med %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Afvis samtale"</string>
|
||||
<string name="screen_invites_empty_list">"Ingen invitationer"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s(%2$s ) inviterede dig"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ja, afvis og blokér"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Er du sikker på, at du vil afvise invitationen til at deltage i dette rum? Dette forhindrer også %1$s i at kontakte dig eller invitere dig til andre rum."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Afvis invitation og blokér"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Afvis og blokér"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Du wirst keine Nachrichten oder Chat-Einladungen von diesem Nutzer sehen."</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Nutzer blockieren"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Melde diesen Chat deinem Konto-Anbieter."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Beschreibe den Grund für die Meldung…"</string>
|
||||
<string name="screen_decline_and_block_title">"Ablehnen und blockieren"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Möchtest du die Einladung zum Betreten von %1$s wirklich ablehnen?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Einladung ablehnen"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Bist du sicher, dass du diese Direktnachricht von %1$s ablehnen möchtest?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Einladung ablehnen"</string>
|
||||
<string name="screen_invites_empty_list">"Keine Einladungen"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) hat dich eingeladen"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ja, ablehnen & blockieren"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Bist du sicher, dass du die Einladung zu diesem Chat ablehnen möchtest? Dadurch wird auch jede weitere Kontaktaufnahme oder Chat Einladung von %1$s blockiert."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Einladung ablehnen & Nutzer blockieren"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Ablehnen und blockieren"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Δεν θα βλέπετε μηνύματα ή προσκλήσεις αίθουσας από αυτόν τον χρήστη"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Αποκλεισμός χρήστη"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Αναφέρετε αυτή την αίθουσα στον πάροχο του λογαριασμού σας."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Περιγράψτε τον λόγο αναφοράς…"</string>
|
||||
<string name="screen_decline_and_block_title">"Απόρριψη και αποκλεισμός"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Σίγουρα θες να απορρίψεις την πρόσκληση συμμετοχής στο %1$s;"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Απόρριψη πρόσκλησης"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Σίγουρα θες να απορρίψεις την ιδιωτική συνομιλία με τον χρήστη %1$s;"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Απόρριψη συνομιλίας"</string>
|
||||
<string name="screen_invites_empty_list">"Χωρίς προσκλήσεις"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) σέ προσκάλεσε"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ναι, απόρριψη και αποκλεισμός"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Είστε βέβαιοι ότι θέλετε να απορρίψετε την πρόσκληση συμμετοχής σε αυτήν την αίθουσα; Αυτό θα εμποδίσει επίσης τον χρήστη %1$s να επικοινωνήσει μαζί σας ή να σας προσκαλέσει σε αίθουσες."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Απόρριψη πρόσκλησης και αποκλεισμός"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Απόρριψη και αποκλεισμός"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"No verás ningún mensaje ni invitación a sala que provenga de este usuario"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Bloquear usuario"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Denunciar esta sala a tu proveedor de cuentas."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Describe el motivo de la denuncia…"</string>
|
||||
<string name="screen_decline_and_block_title">"Rechazar y bloquear"</string>
|
||||
<string name="screen_invites_decline_chat_message">"¿Estás seguro de que quieres rechazar la invitación a unirte a %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Rechazar la invitación"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"¿Estás seguro de que quieres rechazar este chat privado con %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Rechazar el chat"</string>
|
||||
<string name="screen_invites_empty_list">"Sin invitaciones"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) te invitó"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Sí, rechazar y bloquear"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"¿Estás seguro de que deseas rechazar la invitación para unirte a esta sala? Esto también impedirá que %1$s pueda contactar contigo o invitarte a salas."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Rechazar invitación y bloquear"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Rechazar y bloquear"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Sa ei näe enam selle kasutaja saadetud sõnumeid ja jututubade kutseid"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Blokeeri kasutaja"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Teata sellest jututoast oma teenusepakkujale."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Kirjelda põhjust…"</string>
|
||||
<string name="screen_decline_and_block_title">"Keeldu ja blokeeri"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Kas sa oled kindel, et soovid keelduda liitumiskutsest: %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Lükka kutse tagasi"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Kas sa oled kindel, et soovid keelduda privaatsest vestlusest kasutajaga %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Keeldu vestlusest"</string>
|
||||
<string name="screen_invites_empty_list">"Kutseid pole"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) saatis sulle kutse"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Jah, keeldu ja blokeeri"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Kas sa oled kindel, et soovid keelduda kutsest sellesse jututuppa? Samaga kaob kasutajal %1$s võimalus sinuga suhelda ja saata sulle jututubade kutseid."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Keeldu kutsest ja blokeeri"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Keeldu ja blokeeri"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Blokeatu erabiltzailea"</string>
|
||||
<string name="screen_decline_and_block_title">"Baztertu eta blokeatu"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Ziur %1$s(e)ra batzeko gonbidapena baztertu nahi duzula?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Baztertu gonbidapena"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Ziur %1$s(r)en txat pribatua baztertu nahi duzula?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Baztertu txata"</string>
|
||||
<string name="screen_invites_empty_list">"Ez dago gonbidapenik"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s(e)k (%2$s) gonbidatu zaitu"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Eman gonbidapenari ezetza eta blokeatu"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Baztertu eta blokeatu"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_title">"انسداد کاربر"</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"شرح دلیل گزارش…"</string>
|
||||
<string name="screen_decline_and_block_title">"رد و انسداد"</string>
|
||||
<string name="screen_invites_decline_chat_message">"مطمئنید که میخواهید دعوت پیوستن به %1$s را رد کنید؟"</string>
|
||||
<string name="screen_invites_decline_chat_title">"رد دعوت"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"مطمئنید که میخواهید این گپ خصوصی با %1$s را رد کنید؟"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"رد گپ"</string>
|
||||
<string name="screen_invites_empty_list">"بدون دعوت"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) دعوتتان کرد"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"بله. رد و انسداد"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"رد دعوت و انسداد"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"رد و انسداد"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Et tule näkemään viestejä tai kutsuja tältä käyttäjältä"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Estä käyttäjä"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Ilmoita tästä huoneesta palveluntarjoajallesi."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Kuvaile syytä…"</string>
|
||||
<string name="screen_decline_and_block_title">"Hylkää ja estä"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Haluatko varmasti hylätä kutsun liittyä %1$s -huoneeseen?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Hylkää kutsu"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Haluatko varmasti hylätä kutsun yksityiseen keskusteluun käyttäjän %1$s kanssa?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Hylkää keskustelu"</string>
|
||||
<string name="screen_invites_empty_list">"Ei kutsuja"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) kutsui sinut"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Kyllä, hylkää ja estä"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Oletko varma, että haluat kieltäytyä kutsusta liittyä tähän huoneeseen? Tämä estää myös käyttäjää %1$s ottamasta sinuun yhteyttä tai kutsumasta sinua huoneisiin."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Hylkää kutsu ja estä"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Hylkää ja estä"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Vous ne verrez aucun messages ou invitation à un salon de la part de cet utilisateur"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Bloquer l’utilisateur"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Signalez ce salon à votre fournisseur de compte."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Décrivez la raison du signalement…"</string>
|
||||
<string name="screen_decline_and_block_title">"Refuser et bloquer"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Êtes-vous sûr de vouloir décliner l’invitation à rejoindre %1$s ?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Refuser l’invitation"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Êtes-vous sûr de vouloir refuser cette discussion privée avec %1$s ?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Refuser l’invitation"</string>
|
||||
<string name="screen_invites_empty_list">"Aucune invitation"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) vous a invité(e)"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Oui, refuser et bloquer"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Êtes-vous sûr de vouloir refuser l’invitation à rejoindre ce salon ? Cela empêchera également %1$s de vous contacter ou de vous inviter dans les salons."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Refuser l’invitation et bloquer"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Refuser et bloquer"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Ettől a felhasználótól nem fog többé üzeneteket vagy meghívásokat látni."</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Felhasználó letiltása"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"A szoba jelentése a fiókszolgáltatójának."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Írja le az okot…"</string>
|
||||
<string name="screen_decline_and_block_title">"Elutasítás és letiltás"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Biztos, hogy elutasítja a meghívást, hogy csatlakozzon ehhez: %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Meghívás elutasítása"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Biztos, hogy elutasítja ezt a privát csevegést vele: %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Csevegés elutasítása"</string>
|
||||
<string name="screen_invites_empty_list">"Nincsenek meghívások"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) meghívta"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Igen, elutasítás és blokkolás"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Biztos, hogy elutasítja a meghívást, hogy csatlakozzon ehhez a szobához? Ez azt is megakadályozza, hogy %1$s kapcsolatba lépjen Önnel, vagy szobákba hívja."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Meghívó elutasítása és blokkolás"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Elutasítás és letiltás"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Anda tidak akan melihat pesan atau undangan ruangan dari pengguna ini"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Blokir pengguna"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Laporkan ruangan ini ke penyedia akun Anda."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Jelaskan alasan untuk melaporkan…"</string>
|
||||
<string name="screen_decline_and_block_title">"Tolak dan blokir"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Apakah Anda yakin ingin menolak undangan untuk bergabung ke %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Tolak undangan"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Apakah Anda yakin ingin menolak obrolan pribadi dengan %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Tolak obrolan"</string>
|
||||
<string name="screen_invites_empty_list">"Tidak ada undangan"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) mengundang Anda"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ya, tolak & blokir"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Apakah Anda yakin ingin menolak undangan untuk bergabung dengan ruangan ini? Ini juga akan mencegah %1$s menghubungi Anda atau mengundang Anda ke ruangan."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Tolak undangan & blokir"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Tolak dan blokir"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Non vedrai alcun messaggio o invito ad una stanza da parte di questo utente"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Blocca utente"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Segnala questa stanza al fornitore del tuo account."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Descrivi il motivo della segnalazione…"</string>
|
||||
<string name="screen_decline_and_block_title">"Rifiuta e blocca"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Vuoi davvero rifiutare l\'invito ad entrare in %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Rifiuta l\'invito"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Vuoi davvero rifiutare questa conversazione privata con %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Rifiuta l\'invito alla conversazione"</string>
|
||||
<string name="screen_invites_empty_list">"Nessun invito"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) ti ha invitato"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Sì, rifiuta e blocca"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Sei sicuro di voler rifiutare l\'invito a entrare in questa stanza? Ciò impedirà a %1$s di contattarti o invitarti nuovamente in una stanza."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Rifiuta invito e blocca"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Rifiuta e blocca"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_title">"მომხმარებლის დაბლოკვა"</string>
|
||||
<string name="screen_invites_decline_chat_message">"დარწმუნებული ხართ, რომ გსურთ, უარი თქვათ მოწვევაზე %1$s-ში?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"მოწვევაზე უარის თქმა"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"დარწმუნებული ხართ, რომ გსურთ, უარი თქვათ ჩატზე %1$s-თან?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"ჩატზე უარის თქვა"</string>
|
||||
<string name="screen_invites_empty_list">"მოწვევები არ არის"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) მოგიწვიათ"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"이 사용자로부터 메시지나 방 초대장이 표시되지 않습니다."</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"사용자 차단하기"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"이 room 계정 제공자에게 신고하세요."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"신고 사유를 설명하세요…"</string>
|
||||
<string name="screen_decline_and_block_title">"거부 및 차단"</string>
|
||||
<string name="screen_invites_decline_chat_message">"정말로 %1$s 에 참가하지 않고 초대를 거절하시겠어요?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"초대 거절"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"%1$s 와의 비공개 채팅을 정말 거부하시겠습니까?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"채팅 거절"</string>
|
||||
<string name="screen_invites_empty_list">"초대 없음"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) 당신을 초대했습니다"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"예, 거부 및 차단"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"이 방에 대한 초대 거부를 정말로 확인하시겠습니까? 이 경우 %1$s 에서 귀하에게 연락하거나 방에 초대할 수 없게 됩니다."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"초대 거부 및 차단"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"거부 및 차단"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Blokuoti vartotoją"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Ar tikrai norite atmesti kvietimą prisijungti prie %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Atmesti kvietimą"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Ar tikrai norite atmesti šį privatų pokalbį su %1$s ?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Atmesti pokalbį"</string>
|
||||
<string name="screen_invites_empty_list">"Jokių kvietimų"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s(%2$s) pakvietė Jus"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Du vil ikke se noen meldinger eller rominvitasjoner fra denne brukeren"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Blokker bruker"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Rapporter dette rommet til din kontoleverandør."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Beskriv årsaken…"</string>
|
||||
<string name="screen_decline_and_block_title">"Avslå og blokker"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Er du sikker på at du vil takke nei til invitasjonen til å bli med i %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Avvis invitasjon"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Er du sikker på at du vil avslå denne private chatten med %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Avslå chat"</string>
|
||||
<string name="screen_invites_empty_list">"Ingen invitasjoner"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s(%2$s) inviterte deg"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ja, avslå og blokker"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Er du sikker på at du vil avslå invitasjonen til å bli med i dette rommet? Dette vil også forhindre %1$s fra å kontakte deg eller invitere deg til rom."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Avslå invitasjon og blokker"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Avslå og blokker"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Gebruiker blokkeren"</string>
|
||||
<string name="screen_decline_and_block_title">"Weigeren en blokkeren"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Weet je zeker dat je de uitnodiging om toe te treden tot %1$s wilt weigeren?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Uitnodiging weigeren"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Weet je zeker dat je deze privéchat met %1$s wilt weigeren?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Chat weigeren"</string>
|
||||
<string name="screen_invites_empty_list">"Geen uitnodigingen"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) heeft je uitgenodigd"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Weigeren en blokkeren"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Nie zobaczysz żadnych wiadomości ani zaproszeń od tego użytkownika"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Zablokuj użytkownika"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Zgłoś pokój dostawcy swojego konta."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Opisz powód…"</string>
|
||||
<string name="screen_decline_and_block_title">"Odrzuć i zablokuj"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Czy na pewno chcesz odrzucić zaproszenie dołączenia do %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Odrzuć zaproszenie"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Czy na pewno chcesz odrzucić rozmowę prywatną z %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Odrzuć czat"</string>
|
||||
<string name="screen_invites_empty_list">"Brak zaproszeń"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) zaprosił Cię"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Tak, odrzuć i zablokuj"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Czy na pewno chcesz odrzucić zaproszenie dołączenia do tego pokoju? %1$s nie będzie mógł się również z Tobą skontaktować, ani zaprosić Cię do pokoju."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Odrzuć zaproszenie i zablokuj"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Odrzuć i zablokuj"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Você não verá nenhuma mensagem ou convite de sala deste usuário"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Bloquear usuário"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Denuncie esta sala ao fornecedor da sua conta."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Descreva o motivo para denunciar…"</string>
|
||||
<string name="screen_decline_and_block_title">"Recusar e bloquear"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Tem certeza de que deseja recusar o convite para entrar em %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Recusar convite"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Tem certeza de que deseja recusar esse conversa privada com %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Recusar chat"</string>
|
||||
<string name="screen_invites_empty_list">"Não há convites"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s(%2$s) convidou você"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Sim, recusar e bloquear"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Tem certeza de que quer recusar o convite para entrar nesta sala? Isso também impedirá que %1$s entre em contato com você ou o convide para salas."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Recusar convite e bloquear"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Recusar e bloquear"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Não vais ver quaisquer mensagens ou convites para sala deste utilizador"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Bloquear utilizador"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Denunciar esta sala ao fornecedor da tua conta."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Descreve a razão para denunciar…"</string>
|
||||
<string name="screen_decline_and_block_title">"Recusar e bloquear"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Tens a certeza que queres rejeitar o convite para entra em %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Rejeitar convite"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Tens a certeza que queres rejeitar esta conversa privada com %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Rejeitar conversa"</string>
|
||||
<string name="screen_invites_empty_list">"Sem convites"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) convidou-te"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Sim, recusar & bloquear"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Tens a certeza de que queres recusar o convite para entrar nesta sala? Isto também evitará que %1$s te contacte ou te convide para salas."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Recusar convite & bloquear"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Recusar e bloquear"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Nu veți vedea niciun mesaj sau invitație de la acest utilizator."</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Blocați utilizatorul"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Raportați această cameră furnizorului contului dumneavoastră"</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Descrieți motivul raportării…"</string>
|
||||
<string name="screen_decline_and_block_title">"Refuzați și blocați"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Sigur doriți să refuzați alăturarea la %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Refuzați invitația"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Sigur doriți să refuzați conversațiile cu %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Refuzați conversația"</string>
|
||||
<string name="screen_invites_empty_list">"Nicio invitație"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) v-a invitat."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Da, refuzați și blocați"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Sunteți sigur că doriți să refuzați invitația de a vă alătura acestei camere? Acest lucru va împiedica, de asemenea, %1$s să vă contacteze sau să vă invite în camere."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Refuzați invitația și blocați"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Refuzați și blocați"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Вы не увидите сообщений или приглашений в комнату от этого пользователя"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Заблокировать пользователя"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Сообщите об этой комнате своему поставщику учетной записи."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Опишите причину жалобы…"</string>
|
||||
<string name="screen_decline_and_block_title">"Отклонить и заблокировать"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Вы уверены, что хотите отклонить приглашение в %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Отклонить приглашение"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Вы уверены, что хотите отказаться от личного общения с %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Отклонить чат"</string>
|
||||
<string name="screen_invites_empty_list">"Нет приглашений"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) пригласил вас"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Да, отклонить и заблокировать"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Вы действительно хотите отклонить приглашение в эту комнате? Это также предотвратит %1$s возможность связываться с вами или приглашать вас в комнаты."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Отклонить приглашение и заблокировать"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Отклонить и заблокировать"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Od tohto používateľa sa vám nezobrazia žiadne správy ani pozvánky do miestnosti"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Zablokovať používateľa"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Nahlásiť túto miestnosť poskytovateľovi účtu."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Popíšte dôvod…"</string>
|
||||
<string name="screen_decline_and_block_title">"Odmietnuť a zablokovať"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Naozaj chcete odmietnuť pozvánku na pripojenie do %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Odmietnuť pozvanie"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Naozaj chcete odmietnuť túto súkromnú konverzáciu s %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Odmietnuť konverzáciu"</string>
|
||||
<string name="screen_invites_empty_list">"Žiadne pozvánky"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) vás pozval/a"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Áno, odmietnuť a zablokovať"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Ste si istí, že chcete odmietnuť pozvanie na vstup do tejto miestnosti? To tiež zabráni tomu, aby vás %1$s kontaktoval/a alebo vás pozval/a do miestností."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Odmietnuť pozvánku a zablokovať"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Odmietnuť a zablokovať"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Du kommer inte att se några meddelanden eller rumsinbjudningar från den här användaren"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Blockera användare"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Rapportera det här rummet till din kontoleverantör."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Beskriv anledningen …"</string>
|
||||
<string name="screen_decline_and_block_title">"Avvisa och blockera"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Är du säker på att du vill tacka nej till inbjudan att gå med%1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Avböj inbjudan"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Är du säker på att du vill avböja denna privata chatt med %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Avböj chatt"</string>
|
||||
<string name="screen_invites_empty_list">"Inga inbjudningar"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) bjöd in dig"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ja, avvisa och blockera"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Är du säker på att du vill avvisa inbjudan att gå med i det här rummet? Detta kommer också att hindra %1$s från att kontakta dig eller bjuda in dig till rum."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Avvisa inbjudan och blockera"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Avvisa och blockera"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Kullanıcıyı engelle"</string>
|
||||
<string name="screen_invites_decline_chat_message">"%1$s katılma davetini reddetmek istediğinizden emin misiniz?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Daveti reddet"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"%1$s ile bu özel sohbeti reddetmek istediğinizden emin misiniz?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Sohbeti reddet"</string>
|
||||
<string name="screen_invites_empty_list">"Davet Yok"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) sizi davet etti"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Evet, reddet ve engelle"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Bu odaya katılma davetini reddetmek istediğinizden emin misiniz? Bu aynı zamanda %1$s sizinle iletişim kurmasını veya sizi odalara davet etmesini de engeller."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Daveti reddet ve engelle"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Ви не бачитимете повідомлень або запрошень у кімнату від цього користувача"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Заблокувати користувача"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Поскаржитися на цю кімнату постачальнику облікового запису."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Опишіть причину…"</string>
|
||||
<string name="screen_decline_and_block_title">"Відхилити та заблокувати"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Ви впевнені, що хочете відхилити запрошення приєднатися до %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Відхилити запрошення"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Ви дійсно хочете відмовитися від приватної бесіди з %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Відхилити бесіду"</string>
|
||||
<string name="screen_invites_empty_list">"Немає запрошень"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) запрошує вас"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Так, відхилити та заблокувати"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Ви впевнені, що хочете відхилити запрошення приєднатися до цієї кімнати? Це також завадить %1$s зв\'язатися з вами або запрошувати вас в кімнати."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Відхилити запрошення та заблокувати"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Відхилити та заблокувати"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_title">"صارف کو مسدود کریں"</string>
|
||||
<string name="screen_invites_decline_chat_message">"کیا آپکو یقین ہے کہ آپ %1$s میں شامل ہونے کی درخواست مسترد کرنا چاہتے ہیں؟"</string>
|
||||
<string name="screen_invites_decline_chat_title">"دعوت مسترد کریں"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"کیا آپکو یقین ہے کہ آپ %1$s کیساتھ نجی گفتگو مسترد کرنا چاہتے ہیں؟"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"گفتگو مسترد کریں"</string>
|
||||
<string name="screen_invites_empty_list">"کوئی دعوت نامے نہیں"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) نے آپ کو مدعو کیا"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"Siz bu foydalanuvchidan hech qanday xabar yoki xonaga taklif ko‘rmaysiz"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Foydalanuvchini bloklash"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Bu xona haqida hisobingiz provayderiga xabar bering."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Xabar berish sababini tushuntiring…"</string>
|
||||
<string name="screen_decline_and_block_title">"Rad etish va bloklash"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Haqiqatan ham qo\'shilish taklifini rad qilmoqchimisiz%1$s ?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Taklifni rad etish"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Haqiqatan ham bu shaxsiy chatni rad qilmoqchimisiz%1$s ?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Chatni rad etish"</string>
|
||||
<string name="screen_invites_empty_list">"Takliflar yo\'q"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s(%2$s ) sizni taklif qildi"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ha, rad etish va bloklash"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Ushbu xonaga qo‘shilish taklifini rad etishga ishonchingiz komilmi? Bu %1$sning siz bilan bog‘lanishiga yoki sizni xonalarga taklif qilishiga ham to‘sqinlik qiladi."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Taklifni rad etish va bloklash"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Rad etish va bloklash"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"您將不會看到來自此使用者的任何訊息或聊天室邀請"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"封鎖使用者"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"向您的帳號提供者回報此聊天室。"</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"說明回報的原因……"</string>
|
||||
<string name="screen_decline_and_block_title">"拒絕並封鎖"</string>
|
||||
<string name="screen_invites_decline_chat_message">"您確定您想要拒絕加入 %1$s 的邀請嗎?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"拒絕邀請"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"您確定您要拒絕此與 %1$s 的私人聊天嗎?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"拒絕聊天"</string>
|
||||
<string name="screen_invites_empty_list">"沒有邀請"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s(%2$s)邀請您"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"是的,拒絕並封鎖"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"您確定要拒絕加入此聊天室的邀請嗎?這也會防止 %1$s 聯絡您或邀請您加入聊天室。"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"拒絕邀請並封鎖"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"拒絕並封鎖"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"您不会看到来自该用户的任何信息或房间邀请"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"封禁用户"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"向您的帐户提供商举报此房间。"</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"描述举报的原因…"</string>
|
||||
<string name="screen_decline_and_block_title">"拒绝并屏蔽"</string>
|
||||
<string name="screen_invites_decline_chat_message">"您确定要拒绝加入 %1$s 的邀请吗?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"拒绝邀请"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"您确定要拒绝与 %1$s 开始私聊吗?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"拒绝聊天"</string>
|
||||
<string name="screen_invites_empty_list">"没有邀请"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s)邀请了你"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"是的,拒绝并屏蔽"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"您确定要拒绝加入此房间的邀请吗?这也将阻止%1$s 与您联系或邀请您加入房间。"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"拒绝邀请并屏蔽"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"拒绝并屏蔽"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_decline_and_block_block_user_option_description">"You will not see any messages or room invites from this user"</string>
|
||||
<string name="screen_decline_and_block_block_user_option_title">"Block user"</string>
|
||||
<string name="screen_decline_and_block_report_user_option_description">"Report this room to your account provider."</string>
|
||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Describe the reason to report…"</string>
|
||||
<string name="screen_decline_and_block_title">"Decline and block"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Are you sure you want to decline the invitation to join %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Decline invite"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Are you sure you want to decline this private chat with %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Decline chat"</string>
|
||||
<string name="screen_invites_empty_list">"No Invites"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) invited you"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Yes, decline & block"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Are you sure you want to decline the invite to join this room? This will also prevent %1$s from contacting you or inviting you to rooms."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Decline invite & block"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Decline and block"</string>
|
||||
</resources>
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.invite.impl
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.features.invite.test.InMemorySeenInvitesStore
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
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.join.FakeJoinRoom
|
||||
import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner
|
||||
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 kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultAcceptInviteTest {
|
||||
private val roomId = A_ROOM_ID
|
||||
private val client = FakeMatrixClient()
|
||||
private val seenInvitesStore = InMemorySeenInvitesStore(initialRoomIds = setOf(roomId))
|
||||
|
||||
private val clearMembershipNotificationForRoomLambda =
|
||||
lambdaRecorder<SessionId, RoomId, Unit> { _, _ -> }
|
||||
private val notificationCleaner =
|
||||
FakeNotificationCleaner(clearMembershipNotificationForRoomLambda = clearMembershipNotificationForRoomLambda)
|
||||
|
||||
@Test
|
||||
fun `accept invite success scenario`() = runTest {
|
||||
val joinRoomLambda =
|
||||
lambdaRecorder<RoomIdOrAlias, List<String>, JoinedRoom.Trigger, Result<Unit>> { _, _, _ ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
|
||||
val acceptInvite = DefaultAcceptInvite(
|
||||
client = client,
|
||||
notificationCleaner = notificationCleaner,
|
||||
joinRoom = FakeJoinRoom(lambda = joinRoomLambda),
|
||||
seenInvitesStore = seenInvitesStore
|
||||
)
|
||||
|
||||
val result = acceptInvite(roomId)
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
|
||||
assert(joinRoomLambda)
|
||||
.isCalledOnce()
|
||||
.with(value(roomId.toRoomIdOrAlias()), any(), any())
|
||||
|
||||
assert(clearMembershipNotificationForRoomLambda)
|
||||
.isCalledOnce()
|
||||
.with(value(client.sessionId), value(roomId))
|
||||
|
||||
assertThat(seenInvitesStore.seenRoomIds().first()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `accept invite failure scenario`() = runTest {
|
||||
val joinRoomLambda =
|
||||
lambdaRecorder<RoomIdOrAlias, List<String>, JoinedRoom.Trigger, Result<Unit>> { _, _, _ ->
|
||||
Result.failure(RuntimeException("Join room failed"))
|
||||
}
|
||||
|
||||
val acceptInvite = DefaultAcceptInvite(
|
||||
client = client,
|
||||
notificationCleaner = notificationCleaner,
|
||||
joinRoom = FakeJoinRoom(lambda = joinRoomLambda),
|
||||
seenInvitesStore = seenInvitesStore
|
||||
)
|
||||
|
||||
val result = acceptInvite(roomId)
|
||||
|
||||
assertThat(result.isFailure).isTrue()
|
||||
|
||||
assert(joinRoomLambda)
|
||||
.isCalledOnce()
|
||||
.with(value(roomId.toRoomIdOrAlias()), any(), any())
|
||||
|
||||
assert(clearMembershipNotificationForRoomLambda).isNeverCalled()
|
||||
|
||||
assertThat(seenInvitesStore.seenRoomIds().first()).containsExactly(roomId)
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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.invite.impl
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.invite.test.InMemorySeenInvitesStore
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
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.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner
|
||||
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.flow.first
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultDeclineInviteTest {
|
||||
private val roomId = A_ROOM_ID
|
||||
private val inviter = aRoomMember()
|
||||
private val seenInvitesStore = InMemorySeenInvitesStore(initialRoomIds = setOf(roomId))
|
||||
private val clearMembershipNotificationForRoomLambda =
|
||||
lambdaRecorder<SessionId, RoomId, Unit> { _, _ -> }
|
||||
private val notificationCleaner =
|
||||
FakeNotificationCleaner(clearMembershipNotificationForRoomLambda = clearMembershipNotificationForRoomLambda)
|
||||
|
||||
private val successLeaveRoomLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
||||
private val successIgnoreUserLambda =
|
||||
lambdaRecorder<UserId, 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 failureIgnoreUserLambda =
|
||||
lambdaRecorder<UserId, Result<Unit>> { _ -> Result.failure(Exception("Ignore user error")) }
|
||||
private val failureReportRoomLambda =
|
||||
lambdaRecorder<String?, Result<Unit>> { _ -> Result.failure(Exception("Report room error")) }
|
||||
|
||||
@Test
|
||||
fun `decline invite, block=false, report=false, all success`() = runTest {
|
||||
val room = FakeBaseRoom(
|
||||
roomId = roomId,
|
||||
leaveRoomLambda = successLeaveRoomLambda,
|
||||
reportRoomResult = successReportRoomLambda
|
||||
)
|
||||
val client = FakeMatrixClient(ignoreUserResult = successIgnoreUserLambda).apply {
|
||||
givenGetRoomResult(roomId, room)
|
||||
}
|
||||
|
||||
val declineInvite = DefaultDeclineInvite(
|
||||
client = client,
|
||||
notificationCleaner = notificationCleaner,
|
||||
seenInvitesStore = seenInvitesStore
|
||||
)
|
||||
|
||||
val result =
|
||||
declineInvite(roomId, blockUser = false, reportRoom = false, reportReason = null)
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
|
||||
assert(clearMembershipNotificationForRoomLambda)
|
||||
.isCalledOnce()
|
||||
.with(value(client.sessionId), value(roomId))
|
||||
|
||||
assertThat(seenInvitesStore.seenRoomIds().first()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decline invite, block=true, report=true, all success`() = runTest {
|
||||
val room = FakeBaseRoom(
|
||||
roomId = roomId,
|
||||
leaveRoomLambda = successLeaveRoomLambda,
|
||||
reportRoomResult = successReportRoomLambda,
|
||||
initialRoomInfo = aRoomInfo(inviter = inviter)
|
||||
)
|
||||
val client = FakeMatrixClient(ignoreUserResult = successIgnoreUserLambda).apply {
|
||||
givenGetRoomResult(roomId, room)
|
||||
}
|
||||
val declineInvite = DefaultDeclineInvite(
|
||||
client = client,
|
||||
notificationCleaner = notificationCleaner,
|
||||
seenInvitesStore = seenInvitesStore
|
||||
)
|
||||
val result = declineInvite(roomId, blockUser = true, reportRoom = true, reportReason = null)
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
|
||||
assert(clearMembershipNotificationForRoomLambda)
|
||||
.isCalledOnce()
|
||||
.with(value(client.sessionId), value(roomId))
|
||||
|
||||
assertThat(seenInvitesStore.seenRoomIds().first()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decline invite, block=true, report=true, decline invite failed`() = runTest {
|
||||
val room = FakeBaseRoom(
|
||||
roomId = roomId,
|
||||
leaveRoomLambda = failureLeaveRoomLambda,
|
||||
reportRoomResult = successReportRoomLambda
|
||||
)
|
||||
val client = FakeMatrixClient(ignoreUserResult = successIgnoreUserLambda).apply {
|
||||
givenGetRoomResult(roomId, room)
|
||||
}
|
||||
val declineInvite = DefaultDeclineInvite(
|
||||
client = client,
|
||||
notificationCleaner = notificationCleaner,
|
||||
seenInvitesStore = seenInvitesStore
|
||||
)
|
||||
val result = declineInvite(roomId, blockUser = true, reportRoom = true, reportReason = null)
|
||||
|
||||
assertThat(result.exceptionOrNull()).isEqualTo(DeclineInvite.Exception.DeclineInviteFailed)
|
||||
|
||||
assert(clearMembershipNotificationForRoomLambda)
|
||||
.isNeverCalled()
|
||||
|
||||
assertThat(seenInvitesStore.seenRoomIds().first()).isNotEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decline invite, block=true, report=true, ignore user failed`() = runTest {
|
||||
val room = FakeBaseRoom(
|
||||
roomId = roomId,
|
||||
leaveRoomLambda = successLeaveRoomLambda,
|
||||
reportRoomResult = successReportRoomLambda,
|
||||
initialRoomInfo = aRoomInfo(inviter = inviter)
|
||||
)
|
||||
val client = FakeMatrixClient(ignoreUserResult = failureIgnoreUserLambda).apply {
|
||||
givenGetRoomResult(roomId, room)
|
||||
}
|
||||
val declineInvite = DefaultDeclineInvite(
|
||||
client = client,
|
||||
notificationCleaner = notificationCleaner,
|
||||
seenInvitesStore = seenInvitesStore
|
||||
)
|
||||
val result = declineInvite(roomId, blockUser = true, reportRoom = true, reportReason = null)
|
||||
|
||||
assertThat(result.exceptionOrNull()).isEqualTo(DeclineInvite.Exception.BlockUserFailed)
|
||||
|
||||
assert(clearMembershipNotificationForRoomLambda).isCalledOnce()
|
||||
assertThat(seenInvitesStore.seenRoomIds().first()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decline invite, block=true, report=true, report room failed`() = runTest {
|
||||
val room = FakeBaseRoom(
|
||||
roomId = roomId,
|
||||
leaveRoomLambda = successLeaveRoomLambda,
|
||||
reportRoomResult = failureReportRoomLambda,
|
||||
initialRoomInfo = aRoomInfo(inviter = inviter)
|
||||
)
|
||||
val client = FakeMatrixClient(ignoreUserResult = successIgnoreUserLambda).apply {
|
||||
givenGetRoomResult(roomId, room)
|
||||
}
|
||||
val declineInvite = DefaultDeclineInvite(
|
||||
client = client,
|
||||
notificationCleaner = notificationCleaner,
|
||||
seenInvitesStore = seenInvitesStore
|
||||
)
|
||||
val result = declineInvite(roomId, blockUser = true, reportRoom = true, reportReason = null)
|
||||
|
||||
assertThat(result.exceptionOrNull()).isEqualTo(DeclineInvite.Exception.ReportRoomFailed)
|
||||
|
||||
assert(clearMembershipNotificationForRoomLambda).isCalledOnce()
|
||||
assertThat(seenInvitesStore.seenRoomIds().first()).isEmpty()
|
||||
}
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 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.invite.impl.acceptdecline
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.ConfirmingDeclineInvite
|
||||
import io.element.android.features.invite.impl.AcceptInvite
|
||||
import io.element.android.features.invite.impl.DeclineInvite
|
||||
import io.element.android.features.invite.impl.fake.FakeAcceptInvite
|
||||
import io.element.android.features.invite.impl.fake.FakeDeclineInvite
|
||||
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.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
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.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class AcceptDeclineInvitePresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createAcceptDeclineInvitePresenter()
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.acceptAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - declining invite cancel flow`() = runTest {
|
||||
val presenter = createAcceptDeclineInvitePresenter()
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true)
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData, false))
|
||||
state.eventSink(
|
||||
InternalAcceptDeclineInviteEvents.ClearDeclineActionState
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - declining invite error flow`() = runTest {
|
||||
val declineInviteFailure = lambdaRecorder<RoomId, Boolean, Boolean, String?, Result<RoomId>> { _, _, _, _ ->
|
||||
Result.failure(DeclineInvite.Exception.DeclineInviteFailed)
|
||||
}
|
||||
val presenter = createAcceptDeclineInvitePresenter(
|
||||
declineInvite = FakeDeclineInvite(lambda = declineInviteFailure)
|
||||
)
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true)
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData, false))
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false)
|
||||
)
|
||||
}
|
||||
assertThat(awaitItem().declineAction.isLoading()).isTrue()
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(
|
||||
InternalAcceptDeclineInviteEvents.ClearDeclineActionState
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
assert(declineInviteFailure)
|
||||
.isCalledOnce()
|
||||
.with(value(A_ROOM_ID), value(false), value(false), value(null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - declining invite success flow`() = runTest {
|
||||
val declineInviteSuccess = lambdaRecorder<RoomId, Boolean, Boolean, String?, Result<RoomId>> { roomId, _, _, _ -> Result.success(roomId) }
|
||||
val presenter = createAcceptDeclineInvitePresenter(
|
||||
declineInvite = FakeDeclineInvite(lambda = declineInviteSuccess)
|
||||
)
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true)
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData, blockUser = false))
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false)
|
||||
)
|
||||
}
|
||||
assertThat(awaitItem().declineAction.isLoading()).isTrue()
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
assert(declineInviteSuccess)
|
||||
.isCalledOnce()
|
||||
.with(value(A_ROOM_ID), value(false), value(false), value(null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - accepting invite error flow`() = runTest {
|
||||
val acceptInviteFailure = lambdaRecorder<RoomId, Result<RoomId>> { roomId: RoomId ->
|
||||
Result.failure(RuntimeException("Failed to accept invite"))
|
||||
}
|
||||
val presenter = createAcceptDeclineInvitePresenter(
|
||||
acceptInvite = FakeAcceptInvite(lambda = acceptInviteFailure),
|
||||
)
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.AcceptInvite(inviteData)
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.acceptAction).isEqualTo(AsyncAction.Loading)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.acceptAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(
|
||||
InternalAcceptDeclineInviteEvents.ClearAcceptActionState
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.acceptAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
assert(acceptInviteFailure)
|
||||
.isCalledOnce()
|
||||
.with(value(A_ROOM_ID))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - accepting invite success flow`() = runTest {
|
||||
val acceptInviteSuccess = lambdaRecorder<RoomId, Result<RoomId>> { roomId: RoomId -> Result.success(roomId) }
|
||||
val presenter = createAcceptDeclineInvitePresenter(
|
||||
acceptInvite = FakeAcceptInvite(lambda = acceptInviteSuccess)
|
||||
)
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.AcceptInvite(inviteData)
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.acceptAction).isEqualTo(AsyncAction.Loading)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.acceptAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
acceptInviteSuccess.assertions()
|
||||
.isCalledOnce()
|
||||
.with(value(A_ROOM_ID))
|
||||
}
|
||||
|
||||
private fun anInviteData(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
name: String = A_ROOM_NAME,
|
||||
isDm: Boolean = false,
|
||||
): InviteData {
|
||||
return InviteData(
|
||||
roomId = roomId,
|
||||
roomName = name,
|
||||
isDm = isDm,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createAcceptDeclineInvitePresenter(
|
||||
acceptInvite: AcceptInvite = FakeAcceptInvite(),
|
||||
declineInvite: DeclineInvite = FakeDeclineInvite(),
|
||||
): AcceptDeclineInvitePresenter {
|
||||
return AcceptDeclineInvitePresenter(
|
||||
acceptInvite = acceptInvite,
|
||||
declineInvite = declineInvite,
|
||||
)
|
||||
}
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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.invite.impl.declineandblock
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.impl.DeclineInvite
|
||||
import io.element.android.features.invite.impl.fake.FakeDeclineInvite
|
||||
import io.element.android.features.invite.test.anInviteData
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
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.WarmUpRule
|
||||
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.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class DeclineAndBlockPresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createDeclineAndBlockPresenter()
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.blockUser).isTrue()
|
||||
assertThat(state.reportRoom).isFalse()
|
||||
assertThat(state.reportReason).isEmpty()
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
assertThat(state.canDecline).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - update form values`() = runTest {
|
||||
val presenter = createDeclineAndBlockPresenter()
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportRoom).isFalse()
|
||||
assertThat(state.blockUser).isTrue()
|
||||
assertThat(state.reportReason).isEmpty()
|
||||
assertThat(state.canDecline).isTrue()
|
||||
state.eventSink(DeclineAndBlockEvents.ToggleBlockUser)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportRoom).isFalse()
|
||||
assertThat(state.blockUser).isFalse()
|
||||
assertThat(state.reportReason).isEmpty()
|
||||
assertThat(state.canDecline).isFalse()
|
||||
state.eventSink(DeclineAndBlockEvents.ToggleReportRoom)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportRoom).isTrue()
|
||||
assertThat(state.blockUser).isFalse()
|
||||
assertThat(state.reportReason).isEmpty()
|
||||
assertThat(state.canDecline).isFalse()
|
||||
state.eventSink(DeclineAndBlockEvents.UpdateReportReason("Spam"))
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportRoom).isTrue()
|
||||
assertThat(state.blockUser).isFalse()
|
||||
assertThat(state.reportReason).isEqualTo("Spam")
|
||||
assertThat(state.canDecline).isTrue()
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - declining invite success flow`() = runTest {
|
||||
val declineInviteSuccess = lambdaRecorder<RoomId, Boolean, Boolean, String?, Result<RoomId>> { roomId, _, _, _ -> Result.success(roomId) }
|
||||
val presenter = createDeclineAndBlockPresenter(
|
||||
declineInvite = FakeDeclineInvite(lambda = declineInviteSuccess)
|
||||
)
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(DeclineAndBlockEvents.Decline)
|
||||
}
|
||||
assertThat(awaitItem().declineAction.isLoading()).isTrue()
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
assert(declineInviteSuccess)
|
||||
.isCalledOnce()
|
||||
.with(value(A_ROOM_ID), value(true), value(false), value(""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - declining invite error flow`() = runTest {
|
||||
val declineInviteFailure = lambdaRecorder<RoomId, Boolean, Boolean, String?, Result<RoomId>> { _, _, _, _ ->
|
||||
Result.failure(DeclineInvite.Exception.DeclineInviteFailed)
|
||||
}
|
||||
val presenter = createDeclineAndBlockPresenter(
|
||||
declineInvite = FakeDeclineInvite(lambda = declineInviteFailure)
|
||||
)
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(DeclineAndBlockEvents.Decline)
|
||||
}
|
||||
assertThat(awaitItem().declineAction.isLoading()).isTrue()
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(DeclineAndBlockEvents.ClearDeclineAction)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
assert(declineInviteFailure)
|
||||
.isCalledOnce()
|
||||
.with(value(A_ROOM_ID), value(true), value(false), value(""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - block user error flow`() = runTest {
|
||||
val declineInviteFailure = lambdaRecorder<RoomId, Boolean, Boolean, String?, Result<RoomId>> { _, _, _, _ ->
|
||||
Result.failure(DeclineInvite.Exception.BlockUserFailed)
|
||||
}
|
||||
val presenter = createDeclineAndBlockPresenter(
|
||||
declineInvite = FakeDeclineInvite(lambda = declineInviteFailure)
|
||||
)
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(DeclineAndBlockEvents.Decline)
|
||||
}
|
||||
assertThat(awaitItem().declineAction.isLoading()).isTrue()
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
assert(declineInviteFailure)
|
||||
.isCalledOnce()
|
||||
.with(value(A_ROOM_ID), value(true), value(false), value(""))
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createDeclineAndBlockPresenter(
|
||||
inviteData: InviteData = anInviteData(),
|
||||
declineInvite: DeclineInvite = FakeDeclineInvite(),
|
||||
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
|
||||
): DeclineAndBlockPresenter {
|
||||
return DeclineAndBlockPresenter(
|
||||
inviteData = inviteData,
|
||||
declineInvite = declineInvite,
|
||||
snackbarDispatcher = snackbarDispatcher,
|
||||
)
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.invite.impl.declineandblock
|
||||
|
||||
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.features.invite.impl.R
|
||||
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 DeclineAndBlockViewTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `clicking on back invoke the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
rule.setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
onBackClick = it
|
||||
)
|
||||
rule.pressBack()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on decline when enabled emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>()
|
||||
rule.setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
blockUser = true,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_decline)
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvents.Decline)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on decline when disabled does not emit event`() {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>(expectEvents = false)
|
||||
rule.setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
blockUser = false,
|
||||
reportRoom = false,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_decline)
|
||||
eventsRecorder.assertEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on block option emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>()
|
||||
rule.setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
blockUser = true,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(R.string.screen_decline_and_block_block_user_option_title)
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvents.ToggleBlockUser)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on report room option emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>()
|
||||
rule.setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
reportRoom = true,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_report_room)
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvents.ToggleReportRoom)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `typing text in the reason field emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>()
|
||||
rule.setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
reportRoom = true,
|
||||
reportReason = "",
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.onNodeWithText("").performTextInput("Spam!")
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvents.UpdateReportReason("Spam!"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setDeclineAndBlockView(
|
||||
state: DeclineAndBlockState,
|
||||
onBackClick: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
setContent {
|
||||
DeclineAndBlockView(
|
||||
state = state,
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.invite.impl.declineandblock
|
||||
|
||||
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.features.invite.test.anInviteData
|
||||
import io.element.android.tests.testutils.node.TestParentNode
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultDeclineAndBlockEntryPointTest {
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Test
|
||||
fun `test node builder`() {
|
||||
val entryPoint = DefaultDeclineAndBlockEntryPoint()
|
||||
val parentNode = TestParentNode.create { buildContext, plugins ->
|
||||
DeclineAndBlockNode(
|
||||
buildContext = buildContext,
|
||||
plugins = plugins,
|
||||
presenterFactory = { inviteData -> createDeclineAndBlockPresenter() }
|
||||
)
|
||||
}
|
||||
val inviteData = anInviteData()
|
||||
val result = entryPoint.createNode(
|
||||
parentNode = parentNode,
|
||||
buildContext = BuildContext.root(null),
|
||||
inviteData = inviteData
|
||||
)
|
||||
assertThat(result).isInstanceOf(DeclineAndBlockNode::class.java)
|
||||
assertThat(result.plugins).contains(DeclineAndBlockNode.Inputs(inviteData))
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.invite.impl.fake
|
||||
|
||||
import io.element.android.features.invite.impl.AcceptInvite
|
||||
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 FakeAcceptInvite(
|
||||
private val lambda: (RoomId) -> Result<RoomId> = { lambdaError() },
|
||||
) : AcceptInvite {
|
||||
override suspend fun invoke(roomId: RoomId) = simulateLongTask {
|
||||
lambda(roomId)
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.invite.impl.fake
|
||||
|
||||
import io.element.android.features.invite.impl.DeclineInvite
|
||||
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 FakeDeclineInvite(
|
||||
private val lambda: (RoomId, Boolean, Boolean, String?) -> Result<RoomId> = { _, _, _, _ -> lambdaError() },
|
||||
) : DeclineInvite {
|
||||
override suspend fun invoke(roomId: RoomId, blockUser: Boolean, reportRoom: Boolean, reportReason: String?): Result<RoomId> = simulateLongTask {
|
||||
lambda(roomId, blockUser, reportRoom, reportReason)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user