First Commit
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import extension.setupDependencyInjection
|
||||
import extension.testCommonDependencies
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2022-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")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.features.logout.impl"
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
isIncludeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupDependencyInjection()
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.androidutils)
|
||||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.featureflag.api)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.testtags)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.libraries.dateformatter.api)
|
||||
implementation(projects.libraries.workmanager.api)
|
||||
api(projects.features.logout.api)
|
||||
|
||||
testCommonDependencies(libs, true)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.featureflag.test)
|
||||
testImplementation(projects.libraries.sessionStorage.test)
|
||||
testImplementation(projects.libraries.workmanager.test)
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl
|
||||
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.logout.api.LogoutEntryPoint
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultLogoutEntryPoint : LogoutEntryPoint {
|
||||
override fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: LogoutEntryPoint.Callback,
|
||||
): Node {
|
||||
return parentNode.createNode<LogoutNode>(buildContext, listOf(callback))
|
||||
}
|
||||
}
|
||||
+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.logout.impl
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.logout.api.LogoutUseCase
|
||||
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import timber.log.Timber
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultLogoutUseCase(
|
||||
private val sessionStore: SessionStore,
|
||||
private val matrixClientProvider: MatrixClientProvider,
|
||||
) : LogoutUseCase {
|
||||
override suspend fun logoutAll(ignoreSdkError: Boolean) {
|
||||
sessionStore.getAllSessions()
|
||||
.map { sessionData ->
|
||||
SessionId(sessionData.userId)
|
||||
}
|
||||
.forEach { sessionId ->
|
||||
Timber.d("Logging out sessionId: $sessionId")
|
||||
matrixClientProvider.getOrRestore(sessionId).fold(
|
||||
onSuccess = { client ->
|
||||
client.logout(userInitiated = true, ignoreSdkError = ignoreSdkError)
|
||||
},
|
||||
onFailure = { error ->
|
||||
Timber.e(error, "Failed to get or restore MatrixClient for sessionId: $sessionId")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl
|
||||
|
||||
sealed interface LogoutEvents {
|
||||
data class Logout(val ignoreSdkError: Boolean) : LogoutEvents
|
||||
data object CloseDialogs : LogoutEvents
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.features.logout.api.LogoutEntryPoint
|
||||
import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
@AssistedInject
|
||||
class LogoutNode(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: LogoutPresenter,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
private val callback: LogoutEntryPoint.Callback = callback()
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
LogoutView(
|
||||
state = state,
|
||||
onChangeRecoveryKeyClick = callback::navigateToSecureBackup,
|
||||
onBackClick = ::navigateUp,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||
import io.element.android.libraries.core.bool.orTrue
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Inject
|
||||
class LogoutPresenter(
|
||||
private val matrixClient: MatrixClient,
|
||||
private val encryptionService: EncryptionService,
|
||||
private val workManagerScheduler: WorkManagerScheduler,
|
||||
) : Presenter<LogoutState> {
|
||||
@Composable
|
||||
override fun present(): LogoutState {
|
||||
val localCoroutineScope = rememberCoroutineScope()
|
||||
val logoutAction: MutableState<AsyncAction<Unit>> = remember {
|
||||
mutableStateOf(AsyncAction.Uninitialized)
|
||||
}
|
||||
|
||||
val backupUploadState: BackupUploadState by remember {
|
||||
encryptionService.waitForBackupUploadSteadyState()
|
||||
}
|
||||
.collectAsState(initial = BackupUploadState.Unknown)
|
||||
|
||||
var waitingForALongTime by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(backupUploadState) {
|
||||
if (backupUploadState is BackupUploadState.Waiting) {
|
||||
delay(2_000)
|
||||
waitingForALongTime = true
|
||||
} else {
|
||||
waitingForALongTime = false
|
||||
}
|
||||
}
|
||||
|
||||
val isLastDevice by encryptionService.isLastDevice.collectAsState()
|
||||
val backupState by encryptionService.backupStateStateFlow.collectAsState()
|
||||
val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState()
|
||||
|
||||
val doesBackupExistOnServerAction: MutableState<AsyncData<Boolean>> = remember {
|
||||
mutableStateOf(AsyncData.Uninitialized)
|
||||
}
|
||||
|
||||
LaunchedEffect(backupState) {
|
||||
if (backupState == BackupState.UNKNOWN) {
|
||||
getKeyBackupStatus(doesBackupExistOnServerAction)
|
||||
}
|
||||
}
|
||||
|
||||
fun handleEvent(event: LogoutEvents) {
|
||||
when (event) {
|
||||
is LogoutEvents.Logout -> {
|
||||
if (logoutAction.value.isConfirming() || event.ignoreSdkError) {
|
||||
localCoroutineScope.logout(logoutAction, event.ignoreSdkError)
|
||||
} else {
|
||||
logoutAction.value = AsyncAction.ConfirmingNoParams
|
||||
}
|
||||
}
|
||||
LogoutEvents.CloseDialogs -> {
|
||||
logoutAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LogoutState(
|
||||
isLastDevice = isLastDevice,
|
||||
backupState = backupState,
|
||||
doesBackupExistOnServer = doesBackupExistOnServerAction.value.dataOrNull().orTrue(),
|
||||
recoveryState = recoveryState,
|
||||
backupUploadState = backupUploadState,
|
||||
waitingForALongTime = waitingForALongTime,
|
||||
logoutAction = logoutAction.value,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.getKeyBackupStatus(action: MutableState<AsyncData<Boolean>>) = launch {
|
||||
suspend {
|
||||
encryptionService.doesBackupExistOnServer().getOrThrow()
|
||||
}.runCatchingUpdatingState(action)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.logout(
|
||||
logoutAction: MutableState<AsyncAction<Unit>>,
|
||||
ignoreSdkError: Boolean,
|
||||
) = launch {
|
||||
suspend {
|
||||
// Cancel any pending work (e.g. notification sync)
|
||||
workManagerScheduler.cancel(matrixClient.sessionId)
|
||||
|
||||
matrixClient.logout(userInitiated = true, ignoreSdkError)
|
||||
}.runCatchingUpdatingState(logoutAction)
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl
|
||||
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
|
||||
data class LogoutState(
|
||||
val isLastDevice: Boolean,
|
||||
val backupState: BackupState,
|
||||
val doesBackupExistOnServer: Boolean,
|
||||
val recoveryState: RecoveryState,
|
||||
val backupUploadState: BackupUploadState,
|
||||
val waitingForALongTime: Boolean,
|
||||
val logoutAction: AsyncAction<Unit>,
|
||||
val eventSink: (LogoutEvents) -> Unit,
|
||||
)
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.encryption.SteadyStateException
|
||||
|
||||
open class LogoutStateProvider : PreviewParameterProvider<LogoutState> {
|
||||
override val values: Sequence<LogoutState>
|
||||
get() = sequenceOf(
|
||||
aLogoutState(),
|
||||
aLogoutState(isLastDevice = true),
|
||||
aLogoutState(isLastDevice = false, backupUploadState = BackupUploadState.Uploading(66, 200)),
|
||||
aLogoutState(isLastDevice = true, backupUploadState = BackupUploadState.Done),
|
||||
aLogoutState(logoutAction = AsyncAction.ConfirmingNoParams),
|
||||
aLogoutState(logoutAction = AsyncAction.Loading),
|
||||
aLogoutState(logoutAction = AsyncAction.Failure(Exception("Failed to logout"))),
|
||||
aLogoutState(backupUploadState = BackupUploadState.SteadyException(SteadyStateException.Connection("No network"))),
|
||||
// Last session no recovery
|
||||
aLogoutState(isLastDevice = true, recoveryState = RecoveryState.DISABLED),
|
||||
// Last session no backup
|
||||
aLogoutState(isLastDevice = true, backupState = BackupState.UNKNOWN, doesBackupExistOnServer = false),
|
||||
aLogoutState(
|
||||
isLastDevice = false,
|
||||
backupUploadState = BackupUploadState.Waiting,
|
||||
),
|
||||
aLogoutState(
|
||||
isLastDevice = false,
|
||||
backupUploadState = BackupUploadState.Waiting,
|
||||
waitingForALongTime = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun aLogoutState(
|
||||
isLastDevice: Boolean = false,
|
||||
backupState: BackupState = BackupState.ENABLED,
|
||||
doesBackupExistOnServer: Boolean = true,
|
||||
recoveryState: RecoveryState = RecoveryState.ENABLED,
|
||||
backupUploadState: BackupUploadState = BackupUploadState.Unknown,
|
||||
waitingForALongTime: Boolean = false,
|
||||
logoutAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
eventSink: (LogoutEvents) -> Unit = {},
|
||||
) = LogoutState(
|
||||
isLastDevice = isLastDevice,
|
||||
backupState = backupState,
|
||||
doesBackupExistOnServer = doesBackupExistOnServer,
|
||||
recoveryState = recoveryState,
|
||||
backupUploadState = backupUploadState,
|
||||
waitingForALongTime = waitingForALongTime,
|
||||
logoutAction = logoutAction,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.logout.impl.tools.isBackingUp
|
||||
import io.element.android.features.logout.impl.ui.LogoutActionDialog
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage
|
||||
import io.element.android.libraries.designsystem.components.BigIcon
|
||||
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.LinearProgressIndicator
|
||||
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.encryption.SteadyStateException
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.testtags.testTag
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun LogoutView(
|
||||
state: LogoutState,
|
||||
onChangeRecoveryKeyClick: () -> Unit,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val eventSink = state.eventSink
|
||||
|
||||
FlowStepPage(
|
||||
onBackClick = onBackClick,
|
||||
title = title(state),
|
||||
subTitle = subtitle(state),
|
||||
iconStyle = BigIcon.Style.Default(CompoundIcons.KeySolid()),
|
||||
modifier = modifier,
|
||||
buttons = {
|
||||
Buttons(
|
||||
state = state,
|
||||
onChangeRecoveryKeyClick = onChangeRecoveryKeyClick,
|
||||
onLogoutClick = {
|
||||
eventSink(LogoutEvents.Logout(ignoreSdkError = false))
|
||||
}
|
||||
)
|
||||
},
|
||||
) {
|
||||
Content(state)
|
||||
}
|
||||
|
||||
LogoutActionDialog(
|
||||
state.logoutAction,
|
||||
onConfirmClick = {
|
||||
eventSink(LogoutEvents.Logout(ignoreSdkError = false))
|
||||
},
|
||||
onForceLogoutClick = {
|
||||
eventSink(LogoutEvents.Logout(ignoreSdkError = true))
|
||||
},
|
||||
onDismissDialog = {
|
||||
eventSink(LogoutEvents.CloseDialogs)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun title(state: LogoutState): String {
|
||||
return when {
|
||||
state.backupUploadState.isBackingUp() -> stringResource(id = R.string.screen_signout_key_backup_ongoing_title)
|
||||
state.isLastDevice -> {
|
||||
if (state.recoveryState != RecoveryState.ENABLED) {
|
||||
stringResource(id = R.string.screen_signout_recovery_disabled_title)
|
||||
} else if (state.backupState == BackupState.UNKNOWN && state.doesBackupExistOnServer.not()) {
|
||||
stringResource(id = R.string.screen_signout_key_backup_disabled_title)
|
||||
} else {
|
||||
stringResource(id = R.string.screen_signout_save_recovery_key_title)
|
||||
}
|
||||
}
|
||||
else -> stringResource(CommonStrings.action_signout)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun subtitle(state: LogoutState): String? {
|
||||
return when {
|
||||
(state.backupUploadState as? BackupUploadState.SteadyException)?.exception is SteadyStateException.Connection ->
|
||||
stringResource(id = R.string.screen_signout_key_backup_offline_subtitle)
|
||||
state.backupUploadState.isBackingUp() -> stringResource(id = R.string.screen_signout_key_backup_ongoing_subtitle)
|
||||
state.isLastDevice -> stringResource(id = R.string.screen_signout_key_backup_disabled_subtitle)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.Buttons(
|
||||
state: LogoutState,
|
||||
onLogoutClick: () -> Unit,
|
||||
onChangeRecoveryKeyClick: () -> Unit,
|
||||
) {
|
||||
val logoutAction = state.logoutAction
|
||||
if (state.isLastDevice) {
|
||||
OutlinedButton(
|
||||
text = stringResource(id = CommonStrings.common_settings),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = onChangeRecoveryKeyClick,
|
||||
)
|
||||
}
|
||||
val signOutSubmitRes = when {
|
||||
logoutAction is AsyncAction.Loading -> R.string.screen_signout_in_progress_dialog_content
|
||||
state.backupUploadState.isBackingUp() -> CommonStrings.action_signout_anyway
|
||||
else -> CommonStrings.action_signout
|
||||
}
|
||||
Button(
|
||||
text = stringResource(id = signOutSubmitRes),
|
||||
showProgress = logoutAction is AsyncAction.Loading,
|
||||
destructive = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(TestTags.signOut),
|
||||
onClick = onLogoutClick,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(
|
||||
state: LogoutState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 60.dp, start = 20.dp, end = 20.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
when (state.backupUploadState) {
|
||||
is BackupUploadState.Uploading -> {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
progress = { state.backupUploadState.backedUpCount.toFloat() / state.backupUploadState.totalCount.toFloat() },
|
||||
trackColor = ElementTheme.colors.progressIndicatorTrackColor,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
text = "${state.backupUploadState.backedUpCount} / ${state.backupUploadState.totalCount}",
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
)
|
||||
}
|
||||
BackupUploadState.Waiting -> {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
trackColor = ElementTheme.colors.progressIndicatorTrackColor,
|
||||
)
|
||||
if (state.waitingForALongTime) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
text = stringResource(CommonStrings.common_please_check_internet_connection),
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun LogoutViewPreview(
|
||||
@PreviewParameter(LogoutStateProvider::class) state: LogoutState,
|
||||
) = ElementPreview {
|
||||
LogoutView(
|
||||
state,
|
||||
onChangeRecoveryKeyClick = {},
|
||||
onBackClick = {},
|
||||
)
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.logout.impl.di
|
||||
|
||||
import dev.zacsweers.metro.BindingContainer
|
||||
import dev.zacsweers.metro.Binds
|
||||
import dev.zacsweers.metro.ContributesTo
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.logout.impl.direct.DirectLogoutPresenter
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
|
||||
@ContributesTo(SessionScope::class)
|
||||
@BindingContainer
|
||||
interface LogoutModule {
|
||||
@Binds
|
||||
fun bindDirectLogoutPresenter(presenter: DirectLogoutPresenter): Presenter<DirectLogoutState>
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl.direct
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvents
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutStateProvider
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutView
|
||||
import io.element.android.features.logout.impl.ui.LogoutActionDialog
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultDirectLogoutView : DirectLogoutView {
|
||||
@Composable
|
||||
override fun Render(state: DirectLogoutState) {
|
||||
val eventSink = state.eventSink
|
||||
LogoutActionDialog(
|
||||
state.logoutAction,
|
||||
onConfirmClick = {
|
||||
eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false))
|
||||
},
|
||||
onForceLogoutClick = {
|
||||
eventSink(DirectLogoutEvents.Logout(ignoreSdkError = true))
|
||||
},
|
||||
onDismissDialog = {
|
||||
eventSink(DirectLogoutEvents.CloseDialogs)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun DefaultDirectLogoutViewPreview(
|
||||
@PreviewParameter(DirectLogoutStateProvider::class) state: DirectLogoutState,
|
||||
) = ElementPreview {
|
||||
DefaultDirectLogoutView().Render(state = state)
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl.direct
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
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.logout.api.direct.DirectLogoutEvents
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.logout.impl.tools.isBackingUp
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Inject
|
||||
class DirectLogoutPresenter(
|
||||
private val matrixClient: MatrixClient,
|
||||
private val encryptionService: EncryptionService,
|
||||
) : Presenter<DirectLogoutState> {
|
||||
@Composable
|
||||
override fun present(): DirectLogoutState {
|
||||
val localCoroutineScope = rememberCoroutineScope()
|
||||
|
||||
val logoutAction: MutableState<AsyncAction<Unit>> = remember {
|
||||
mutableStateOf(AsyncAction.Uninitialized)
|
||||
}
|
||||
|
||||
val backupUploadState: BackupUploadState by remember {
|
||||
encryptionService.waitForBackupUploadSteadyState()
|
||||
}
|
||||
.collectAsState(initial = BackupUploadState.Unknown)
|
||||
|
||||
val isLastDevice by encryptionService.isLastDevice.collectAsState()
|
||||
|
||||
fun handleEvent(event: DirectLogoutEvents) {
|
||||
when (event) {
|
||||
is DirectLogoutEvents.Logout -> {
|
||||
if (logoutAction.value.isConfirming() || event.ignoreSdkError) {
|
||||
localCoroutineScope.logout(logoutAction, event.ignoreSdkError)
|
||||
} else {
|
||||
logoutAction.value = AsyncAction.ConfirmingNoParams
|
||||
}
|
||||
}
|
||||
DirectLogoutEvents.CloseDialogs -> {
|
||||
logoutAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DirectLogoutState(
|
||||
canDoDirectSignOut = !isLastDevice &&
|
||||
!backupUploadState.isBackingUp(),
|
||||
logoutAction = logoutAction.value,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.logout(
|
||||
logoutAction: MutableState<AsyncAction<Unit>>,
|
||||
ignoreSdkError: Boolean,
|
||||
) = launch {
|
||||
suspend {
|
||||
matrixClient.logout(userInitiated = true, ignoreSdkError)
|
||||
}.runCatchingUpdatingState(logoutAction)
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl.tools
|
||||
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.SteadyStateException
|
||||
|
||||
internal fun BackupUploadState.isBackingUp(): Boolean {
|
||||
return when (this) {
|
||||
BackupUploadState.Waiting,
|
||||
is BackupUploadState.Uploading -> true
|
||||
// The backup is in progress, but there have been a network issue, so we have to warn the user.
|
||||
is BackupUploadState.SteadyException -> exception is SteadyStateException.Connection
|
||||
BackupUploadState.Unknown,
|
||||
BackupUploadState.Done,
|
||||
BackupUploadState.Error -> false
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.element.android.features.logout.impl.R
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun LogoutActionDialog(
|
||||
state: AsyncAction<Unit>,
|
||||
onConfirmClick: () -> Unit,
|
||||
onForceLogoutClick: () -> Unit,
|
||||
onDismissDialog: () -> Unit,
|
||||
) {
|
||||
when (state) {
|
||||
AsyncAction.Uninitialized ->
|
||||
Unit
|
||||
is AsyncAction.Confirming ->
|
||||
LogoutConfirmationDialog(
|
||||
onSubmitClick = onConfirmClick,
|
||||
onDismiss = onDismissDialog
|
||||
)
|
||||
is AsyncAction.Loading ->
|
||||
ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content))
|
||||
is AsyncAction.Failure ->
|
||||
RetryDialog(
|
||||
title = stringResource(id = CommonStrings.dialog_title_error),
|
||||
content = stringResource(id = CommonStrings.error_unknown),
|
||||
retryText = stringResource(id = CommonStrings.action_signout_anyway),
|
||||
onRetry = onForceLogoutClick,
|
||||
onDismiss = onDismissDialog,
|
||||
)
|
||||
is AsyncAction.Success -> Unit
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.element.android.features.logout.impl.R
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun LogoutConfirmationDialog(
|
||||
onSubmitClick: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
ConfirmationDialog(
|
||||
title = stringResource(id = CommonStrings.action_signout),
|
||||
content = stringResource(id = R.string.screen_signout_confirmation_dialog_content),
|
||||
submitText = stringResource(id = CommonStrings.action_signout),
|
||||
onSubmitClick = onSubmitClick,
|
||||
onDismiss = onDismiss,
|
||||
)
|
||||
}
|
||||
@@ -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_signout_confirmation_dialog_content">"Вы ўпэўнены, што хочаце выйсці?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Выйсці"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Выйсці"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Выхад…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Вы збіраецеся выйсці з апошняга сеанса. Калі вы выйдзеце з сістэмы зараз, вы страціце доступ да зашыфраваных паведамленняў."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Вы адключылі рэзервовае капіраванне"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Вашы ключы ўсё яшчэ захоўваліся, калі вы выйшлі з сеткі. Паўторна падключыцеся, каб можна было стварыць рэзервовую копію вашых ключоў перад выхадам."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Вашы ключы ўсё яшчэ ствараюцца"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Калі ласка, дачакайцеся завяршэння працэсу, перш чым выходзіць з сістэмы."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Вашы ключы ўсё яшчэ ствараюцца"</string>
|
||||
<string name="screen_signout_preference_item">"Выйсці"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Вы збіраецеся выйсці з апошняга сеанса. Калі вы выйдзеце з сістэмы зараз, вы страціце доступ да зашыфраваных паведамленняў."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Аднаўленне не наладжана"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Вы збіраецеся выйсці з апошняга сеанса. Калі вы выйдзеце з сістэмы зараз, вы страціце доступ да зашыфраваных паведамленняў."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Вы захавалі свой ключ аднаўлення?"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_signout_confirmation_dialog_content">"Сигурни ли сте, че искате да излезете?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Изход"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Изход"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Излизане…"</string>
|
||||
<string name="screen_signout_preference_item">"Изход"</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_signout_confirmation_dialog_content">"Opravdu se chcete odhlásit?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Odhlásit se"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Odhlásit se"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Odhlašování…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Chystáte se odhlásit z poslední relace. Pokud se nyní odhlásíte, ztratíte přístup ke svým šifrovaným zprávám."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Vypnuli jste zálohování"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Když jste přešli do režimu offline, vaše klíče se ještě stále zálohovaly. Znovu se připojte, aby bylo možné před odhlášením zálohovat vaše klíče."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Vaše klíče jsou stále zálohovány"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Před odhlášením prosím počkejte na dokončení."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Vaše klíče jsou stále zálohovány"</string>
|
||||
<string name="screen_signout_preference_item">"Odhlásit se"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Chystáte se odhlásit z poslední relace. Pokud se nyní odhlásíte, ztratíte přístup ke svým šifrovaným zprávám."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Obnovení není nastaveno"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Chystáte se odhlásit z poslední relace. Pokud se nyní odhlásíte, můžete ztratit přístup k šifrovaným zprávám."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Uložili jste si klíč pro obnovení?"</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_signout_confirmation_dialog_content">"Ydych chi\'n siŵr eich bod am allgofnodi?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Allgofnodi"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Allgofnodi"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Yn allgofnodi…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Rydych chi ar fin allgofnodi o\'ch sesiwn ddiwethaf. Os byddwch yn allgofnodi nawr, byddwch yn colli mynediad i\'ch negeseuon wedi\'u hamgryptio."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Rydych chi wedi diffodd copïo wrth gefn"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Roedd eich allweddi yn dal i gael eu copïo wrth gefn pan aethoch all-lein. Ailgysylltwch fel bod modd gwneud copi wrth gefn o\'ch allweddi cyn allgofnodi."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Mae eich allweddi yn dal i gael eu copïo wrth gefn"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Arhoswch iddo gael ei gwblhau cyn allgofnodi."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Mae eich allweddi yn dal i gael eu copïo wrth gefn"</string>
|
||||
<string name="screen_signout_preference_item">"Allgofnodi"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Rydych chi ar fin allgofnodi o\'ch sesiwn ddiwethaf. Os byddwch yn allgofnodi nawr, byddwch yn colli mynediad i\'ch negeseuon wedi\'u hamgryptio."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Adfer heb ei osod"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Rydych chi ar fin allgofnodi o\'ch sesiwn ddiwethaf. Os ydych chi\'n allgofnodi nawr, efallai y byddwch chi\'n colli mynediad i\'ch negeseuon wedi\'u hamgryptio."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Ydych chi wedi cadw\'ch allwedd adfer?"</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_signout_confirmation_dialog_content">"Er du sikker på, at du vil logge ud?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Log ud"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Log ud"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Logger ud…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Du er ved at logge ud af din sidste session. Hvis du logger ud nu, mister du adgangen til dine krypterede meddelelser."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Du har slået sikkerhedskopiering fra"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Dine nøgler blev stadig sikkerhedskopieret, da du gik offline. Opret forbindelse igen, så dine nøgler kan sikkerhedskopieres, før du logger ud."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Dine nøgler bliver stadig sikkerhedskopieret"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Vent på, at dette er fuldført, før du logger ud."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Dine nøgler bliver stadig sikkerhedskopieret"</string>
|
||||
<string name="screen_signout_preference_item">"Log ud"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Du er ved at logge ud af din sidste session. Hvis du logger af nu, mister du adgangen til dine krypterede meddelelser."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Gendannelse er ikke konfigureret"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Du er ved at logge ud af din sidste session. Hvis du logger af nu, kan du miste adgangen til dine krypterede meddelelser."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Har du gemt din gendannelsesnøgle?"</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_signout_confirmation_dialog_content">"Möchtest du dich wirklich abmelden?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Abmelden"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Abmelden"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Abmelden…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Du hast das Backup deaktiviert"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Das Backup deiner Schlüssel lief noch, als du offline gegangen bist. Verbinde dich erneut, damit deine Schlüssel vor dem Abmelden gesichert werden können."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Deine Schlüssel werden noch gesichert"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Bitte warte, bis dieser Vorgang abgeschlossen ist, bevor du dich abmeldest."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Deine Schlüssel werden noch gesichert"</string>
|
||||
<string name="screen_signout_preference_item">"Abmelden"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Wiederherstellung nicht eingerichtet"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du möglicherweise den Zugriff auf deine verschlüsselten Nachrichten."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Hast du deinen Wiederherstellungsschlüssel gespeichert?"</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_signout_confirmation_dialog_content">"Σίγουρα θες να αποσυνδεθείς;"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Αποσύνδεση"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Αποσύνδεση"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Αποσύνδεση…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Πρόκειται να αποσυνδεθείς από την τελευταία σου συνεδρία. Εάν αποσυνδεθείς τώρα, θα χάσεις την πρόσβαση στα κρυπτογραφημένα μηνύματά σου."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Έχεις απενεργοποιήσει τη δημιουργία αντιγράφων ασφαλείας"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Εξακολουθούσε να δημιουργείται αντίγραφο ασφαλείας των κλειδιών σου όταν βρέθηκες εκτός σύνδεσης. Επανασυνδέσου, ώστε να είναι δυνατή η δημιουργία αντιγράφων ασφαλείας των κλειδιών σου πριν αποσυνδεθείς."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Εξακολουθούν να δημιουργούνται αντίγραφα ασφαλείας των κλειδιών σου"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Περίμενε να ολοκληρωθεί πριν αποσυνδεθείς."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Εξακολουθούν να δημιουργούνται αντίγραφα ασφαλείας των κλειδιών σου"</string>
|
||||
<string name="screen_signout_preference_item">"Αποσύνδεση"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Πρόκειται να αποσυνδεθείς από την τελευταία σου συνεδρία. Εάν αποσυνδεθείς τώρα, θα χάσεις την πρόσβαση στα κρυπτογραφημένα μηνύματά σου."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Η ανάκτηση δεν έχει ρυθμιστεί"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Πρόκειται να αποσυνδεθείς από την τελευταία σας συνεδρία. Εάν αποσυνδεθείς τώρα, ενδέχεται να χάσεις την πρόσβαση στα κρυπτογραφημένα μηνύματά σου."</string>
|
||||
<string name="screen_signout_save_recovery_key_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_signout_confirmation_dialog_content">"¿Estás seguro de que quieres cerrar sesión?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Cerrar sesión"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Cerrar sesión"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Cerrando sesión…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Estás a punto de cerrar tu última sesión. Si cierras sesión ahora, perderás el acceso a tus mensajes cifrados."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Has desactivado la copia de seguridad"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Se estaba haciendo una copia de seguridad de tus claves cuando te desconectaste. Vuelve a conectarte para que se haga una copia de seguridad de tus claves antes de desconectarte."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Se sigue guardando una copia de seguridad de tus claves"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Espera a que se complete antes de cerrar sesión."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Se sigue guardando una copia de seguridad de tus claves"</string>
|
||||
<string name="screen_signout_preference_item">"Cerrar sesión"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Estás a punto de cerrar tu última sesión. Si cierras sesión ahora, perderás el acceso a tus mensajes cifrados."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"La recuperación no está configurada"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Estás a punto de cerrar tu última sesión. Si cierras la sesión ahora, podrías perder el acceso a tus mensajes cifrados."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"¿Has guardado tu clave de recuperación?"</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_signout_confirmation_dialog_content">"Kas sa oled kindel, et soovid välja logida?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Logi välja"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Logi välja"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Logime välja…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Oled oma viimasest seansist välja logimas. Kui logid nüüd välja, kaotad ligipääsu oma krüptitud sõnumitele."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Sa oled varukoopiate tegemise välja lülitanud"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Kui su võrguühendus katkes, siis sinu krüptovõtmed oli parasjagu varundamisel. Loo võrguühendus uuesti, oota kuni krüptovõtmete varundamine lõppeb ja alles siis logi rakendusest välja."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Sinu krüptovõtmed on veel varundamisel"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Enne väljalogimist palun oota, et pooleliolev toiming lõppeb."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Sinu krüptovõtmed on veel varundamisel"</string>
|
||||
<string name="screen_signout_preference_item">"Logi välja"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Sa oled logimas välja oma viimasest sessioonist. Kui teed seda nüüd, siis kaotad ligipääsu oma krüptitud sõnumitele."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Andmete taastamine on seadistamata"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Sa oled logimas välja oma viimasest sessioonist. Kui teed seda nüüd, siis ilmselt kaotad ligipääsu oma krüptitud sõnumitele."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Kas sa oled oma taastevõtme talletanud?"</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_signout_confirmation_dialog_content">"Ziur saioa amaitu nahi duzula?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Amaitu saioa"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Amaitu saioa"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Saioa amaitzen…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Babeskopia desaktibatu duzu"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Itxaron eragiketa amaitu arte saioa amaitu baino lehen."</string>
|
||||
<string name="screen_signout_preference_item">"Amaitu saioa"</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Berreskuratzea ez da konfiguratu"</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Gorde al duzu berreskuratze-gakoa?"</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_signout_confirmation_dialog_content">"مطمئنید که میخواهید از حسابتان خارج شوید؟"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"خروج"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"خروج"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"خارج شدن…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"دارید از واپسین نشستتان خارج میشوید. اگر اکنون خارج شوید پیامهای رمزنگاشتهتان را از دست خواهید داد."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"پشتیبان را خاموش کردهاید"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"در هنگامی که آفلاین شدید، کلیدهای شما هنوز در حال پشتیبانگیری بودند. دوباره متصل شوید ، تا قبل از خروج از کلیدهایتان نسخه پشتیبان گرفته شود."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"کلیدهایتان هنوز در حال پشتیبان گیریند"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"لطفاً پیش از خروج منتظر پایانش شوید."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"کلیدهایتان هنوز در حال پشتیبان گیریند"</string>
|
||||
<string name="screen_signout_preference_item">"خروج"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"شما در آستانه خروج از آخرین جلسه خود هستید. اگر اکنون از سیستم خارج شوید، دسترسی به پیام های رمزگذاری شده تان را از دست خواهید داد."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"بازگردانی برپا نشده"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"دارید از واپسین نشستتان خارج میشوید. اگر اکنون خارج شوید ممکن است پیامهای رمزنگاشتهتان را از دست بدهید."</string>
|
||||
<string name="screen_signout_save_recovery_key_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_signout_confirmation_dialog_content">"Haluatko varmasti kirjautua ulos?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Kirjaudu ulos"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Kirjaudu ulos"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Kirjaudutaan ulos…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Olet kirjautumassa ulos viimeisestä istunnostasi. Jos kirjaudut ulos nyt, menetät pääsyn salattuihin viesteihisi."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Olet poistanut varmuuskopioinnin käytöstä"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Avaimiasi varmuuskopioitiin vielä, kun menit offline-tilaan. Muodosta yhteys uudelleen, jotta avaimesi voidaan varmuuskopioida ennen uloskirjautumista."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Avaimiasi varmuuskopioidaan vielä"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Odota, että tämä on valmis ennen uloskirjautumista."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Avaimiasi varmuuskopioidaan vielä"</string>
|
||||
<string name="screen_signout_preference_item">"Kirjaudu ulos"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Olet kirjautumassa ulos viimeisestä istunnostasi. Jos kirjaudut ulos nyt, menetät pääsyn salattuihin viesteihisi."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Palautus ei ole käytössä"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Olet kirjautumassa ulos viimeisestä istunnostasi. Jos kirjaudut ulos nyt, saatat menettää pääsyn salattuihin viesteihisi."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Oletko tallentanut palautusavaimesi?"</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_signout_confirmation_dialog_content">"Êtes-vous sûr de vouloir vous déconnecter ?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Se déconnecter"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Se déconnecter"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Déconnexion…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Vous êtes en train de vous déconnecter de votre dernière session. Si vous vous déconnectez maintenant, vous perdrez l’accès à l’historique de vos discussions chiffrées."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Vous avez désactivé la sauvegarde"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Vos clés étaient en cours de sauvegarde lorsque vous avez perdu la connexion au réseau. Il faudrait rétablir cette connexion afin de pouvoir terminer la sauvegarde avant de vous déconnecter."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Vos clés sont en cours de sauvegarde"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Veuillez attendre que cela se termine avant de vous déconnecter."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Vos clés sont en cours de sauvegarde"</string>
|
||||
<string name="screen_signout_preference_item">"Se déconnecter"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Vous êtes sur le point de vous déconnecter de votre dernier appareil. Si vous le faites maintenant, vous perdrez l’accès à l’historique de vos messages."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"La récupération n’est pas configurée."</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Vous êtes sur le point de vous déconnecter de votre dernière session. Si vous le faites maintenant, vous perdrez l’accès à l’historique de vos discussions chiffrées."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Avez-vous sauvegardé votre clé de récupération ?"</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_signout_confirmation_dialog_content">"Biztos, hogy kijelentkezik?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Kijelentkezés"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Kijelentkezés"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Kijelentkezés…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Arra készül, hogy kijelentkezzen az utolsó munkamenetéből is. Ha most kijelentkezik, akkor elveszti a hozzáférését a titkosított üzeneteihez."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Kikapcsolta a biztonsági mentést"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"A kulcsai mentése során bontotta a kapcsolatot. Kapcsolódjon újra, hogy a kulcsai továbbra is mentésre kerüljenek mielőtt kijelentkezik."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"A kulcsai mentése még folyamatban van"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Kijelentkezés előtt várja meg a befejezését."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"A kulcsai mentése még folyamatban van"</string>
|
||||
<string name="screen_signout_preference_item">"Kijelentkezés"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Arra készül, hogy kijelentkezzen az utolsó munkamenetéből is. Ha most kijelentkezik, akkor elveszti a hozzáférését a titkosított üzeneteihez."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"A helyreállítás nincs beállítva"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Arra készül, hogy kijelentkezzen az utolsó munkamenetéből is. Ha most kijelentkezik, akkor elveszítheti a hozzáférését a titkosított üzeneteihez."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Mentette a helyreállítási kulcsát?"</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_signout_confirmation_dialog_content">"Apakah Anda yakin ingin keluar dari akun?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Keluar dari akun"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Keluar dari akun"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Mengeluarkan dari akun…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Anda akan keluar dari sesi terakhir Anda. Jika Anda keluar sekarang, Anda akan kehilangan akses ke pesan terenkripsi Anda."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Anda telah menonaktifkan pencadangan"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Kunci Anda masih dicadangkan saat Anda luring. Sambungkan kembali sehingga kunci Anda dapat dicadangkan sebelum keluar."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Kunci Anda masih dicadangkan"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Mohon tunggu hingga proses ini selesai sebelum keluar."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Kunci Anda masih dicadangkan"</string>
|
||||
<string name="screen_signout_preference_item">"Keluar dari akun"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Anda akan keluar dari sesi Anda yang terakhir. Jika Anda keluar sekarang, Anda akan kehilangan akses ke pesan terenkripsi Anda."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Pemulihan belum disiapkan"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Anda akan keluar dari sesi terakhir Anda. Jika Anda keluar sekarang, Anda mungkin kehilangan akses ke pesan terenkripsi Anda."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Apakah Anda sudah menyimpan kunci pemulihan Anda?"</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_signout_confirmation_dialog_content">"Sei sicuro di voler uscire?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Disconnetti"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Disconnetti"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Disconnessione in corso…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Stai per disconnettere la tua ultima sessione. Se esci ora, perderai l\'accesso ai tuoi messaggi cifrati."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Hai disattivato il backup"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Il backup delle chiavi era ancora in corso quando sei andato offline. Riconnettiti per eseguire il backup delle chiavi prima di uscire."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Il backup delle chiavi è ancora in corso"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Attendi il completamento dell\'operazione prima di uscire."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Il backup delle chiavi è ancora in corso"</string>
|
||||
<string name="screen_signout_preference_item">"Disconnetti"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Stai per disconnettere la tua ultima sessione. Se esci ora, perderai l\'accesso ai tuoi messaggi cifrati."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Recupero non impostato"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Stai per disconnettere la tua ultima sessione. Se esci ora, potresti perdere l\'accesso ai tuoi messaggi cifrati."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Hai salvato la chiave di recupero?"</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_signout_confirmation_dialog_content">"დარწმუნებული ხართ, რომ გსურთ გამოსვლა?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"გამოსვლა"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"გამოსვლა"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"გასვლა…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"თქვენ აპირებთ გასვლას თქვენი ბოლო სესიიდან. თუ ახლა გამოხვალთ, დაკარგავთ წვდომას თქვენს დაშიფრულ შეტყობინებებზე."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"თქვენ გამორთეთ სარეზერვო ასლი"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"თქვენი გასაღებების სარეზერვო ასლის შექმნა მიმდინარეობდა იმ დროს, როდესაც გამოხვედით. დაკავშირდით ისევ ისე, რომ სარეზერვო ასლი შეიქმნას ანგარიშიდან გამოსვლის გარეშე."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"თქვენი გასაღებების სარეზერვო ასლი ჯერ კიდევ შექმნის პროცესშია"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"გთხოვთ დაელოდეთ ამის დასრულებას სისტემიდან გამოსვლამდე."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"თქვენი გასაღებების სარეზერვო ასლი ჯერ კიდევ შექმნის პროცესშია"</string>
|
||||
<string name="screen_signout_preference_item">"გამოსვლა"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"თქვენ აპირებთ გასვლას თქვენი ბოლო სესიიდან. თუ ახლა გამოხვალთ, დაკარგავთ წვდომას თქვენს დაშიფრულ შეტყობინებებზე."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"აღდგენა არ არის დაყენებული"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"თქვენ აპირებთ გასვლას თქვენი ბოლო სესიიდან. თუ ახლა გამოხვალთ, შესაძლოა დაკარგოთ წვდომა თქვენს დაშიფრულ შეტყობინებებზე."</string>
|
||||
<string name="screen_signout_save_recovery_key_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_signout_confirmation_dialog_content">"정말 로그아웃하시겠습니까?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"로그아웃"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"로그아웃"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"로그아웃 중…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"마지막 세션에서 로그아웃하려고 합니다. 지금 로그아웃하면 암호화된 메시지에 액세스할 수 없게 됩니다."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"백업이 꺼져 있습니다."</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"오프라인으로 전환했을 때 키가 아직 백업 중이었습니다. 로그아웃하기 전에 키를 백업할 수 있도록 다시 연결하세요."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"귀하의 키는 아직 백업 중입니다."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"로그아웃하기 전에 이 과정이 완료될 때까지 기다려 주시기 바랍니다."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"귀하의 키는 아직 백업 중입니다."</string>
|
||||
<string name="screen_signout_preference_item">"로그아웃"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"마지막 세션에서 로그아웃할 것입니다. 지금 로그아웃하면 암호화된 메시지에 액세스할 수 없게 됩니다."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"복구가 설정되지 않았습니다"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"마지막 세션에서 로그아웃하려고 합니다. 지금 로그아웃하면 암호화된 메시지에 액세스할 수 없게 될 수 있습니다."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"복구 키를 저장하셨습니까?"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_signout_confirmation_dialog_content">"Ar tikrai norite atsijungti?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Atsijungti"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Atsijungti"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Atsijungiama…"</string>
|
||||
<string name="screen_signout_preference_item">"Atsijungti"</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_signout_confirmation_dialog_content">"Er du sikker på at du vil logge ut?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Logg ut"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Logg ut"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Logger ut…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Du er i ferd med å logge av din siste sesjon. Hvis du logger av nå, mister du tilgangen til de krypterte meldingene dine."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Du har slått av sikkerhetskopiering"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Nøklene dine ble fortsatt sikkerhetskopiert da du koblet fra. Koble til igjen, slik at nøklene dine kan sikkerhetskopieres før du logger av."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Nøklene dine blir fortsatt sikkerhetskopiert"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Vent til dette er fullført før du logger av."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Nøklene dine blir fortsatt sikkerhetskopiert"</string>
|
||||
<string name="screen_signout_preference_item">"Logg ut"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Du er i ferd med å logge ut av din siste sesjon. Hvis du logger ut nå, mister du tilgangen til de krypterte meldingene dine."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Gjenoppretting ikke konfigurert"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Du er i ferd med å logge av din siste sesjon. Hvis du logger av nå, kan du miste tilgangen til de krypterte meldingene dine."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Har du lagret gjenopprettingsnøkkelen din?"</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_signout_confirmation_dialog_content">"Weet je zeker dat je je wilt uitloggen?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Uitloggen"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Uitloggen"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Uitloggen…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Je staat op het punt uit te loggen bij je laatste sessie. Als je je nu uitlogt, verlies je de toegang tot je versleutelde berichten."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Je hebt de back-up uitgeschakeld"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"De backup van je sleutels was nog bezig toen je offline ging. Maak opnieuw verbinding zodat er een back-up van je sleutels kan worden gemaakt voordat je uitlogt."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"De backup van je sleutels is nog bezig"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Wacht tot dit voltooid is voordat je uitlogt."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"De backup van je sleutels is nog bezig"</string>
|
||||
<string name="screen_signout_preference_item">"Uitloggen"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Je staat op het punt uit te loggen bij je laatste sessie. Als je je nu uitlogt, verlies je de toegang tot je versleutelde berichten."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Herstelmogelijkheid niet ingesteld"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Je staat op het punt uit te loggen bij je laatste sessie. Als je je nu uitlogt, kan het dat je de toegang tot je versleutelde berichten verliest."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Heb je je herstelsleutel opgeslagen?"</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_signout_confirmation_dialog_content">"Czy na pewno chcesz się wylogować?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Wyloguj"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Wyloguj"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Wylogowywanie…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Wyłączyłeś backup"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Twoje klucze były nadal archiwizowane po przejściu w tryb offline. Połącz się ponownie, aby zapisać w chmurze przed wylogowaniem."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Twoje klucze są nadal archiwizowane"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Zanim się wylogujesz, poczekaj na zakończenie operacji."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Twoje klucze są nadal archiwizowane"</string>
|
||||
<string name="screen_signout_preference_item">"Wyloguj"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Nie ustawiono przywracania"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Czy zapisałeś swój klucz przywracania?"</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_signout_confirmation_dialog_content">"Você tem certeza que deseja sair?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Sair"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Sair"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Saindo…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Você está prestes a sair da sua última sessão. Se você sair agora, perderá o acesso às suas mensagens criptografadas."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Você desativou o backup"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"O backup das suas chaves ainda estava sendo feito quando você ficou off-line. Reconecte-se para que você possa fazer o backup de suas chaves antes de sair."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"O backup das suas chaves ainda está em andamento"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Aguarde até que isso seja concluído antes de sair."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"O backup das suas chaves ainda está em andamento"</string>
|
||||
<string name="screen_signout_preference_item">"Sair"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Você está prestes a sair da sua última sessão. Se você sair agora, perderá o acesso às suas mensagens criptografadas."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"A recuperação não está configurada"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Você está prestes a sair da sua última sessão. Se você sair agora, poderá perder o acesso às suas mensagens criptografadas."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Você salvou sua chave de recuperação?"</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_signout_confirmation_dialog_content">"Tens a certeza que queres terminar a sessão?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Terminar sessão"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Terminar sessão"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"A terminar sessão…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Estás prestes a terminar a tua última sessão. Se continuares, perderás o acesso às tuas mensagens cifradas."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Desativaste a cópia de segurança"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"As tuas chaves ainda estavam a ser guardadas quando ficaste desligado. Volta a ligar-te para que as tuas chaves possam ser guardadas antes de encerrares a sessão."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"As tuas chaves ainda estão a ser guardadas"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Por favor, aguarda a conclusão desta operação antes de terminares a sessão."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"As tuas chaves ainda estão a ser guardadas"</string>
|
||||
<string name="screen_signout_preference_item">"Terminar sessão"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Estás prestes a terminar a tua última sessão. Se continuares, perderás o acesso às tuas mensagens cifradas."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Recuperação não configurada"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Estás prestes a terminar a tua última sessão. Se continuares, poderás perder o acesso às tuas mensagens cifradas."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Guardaste a tua chave de recuperação?"</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_signout_confirmation_dialog_content">"Sunteți sigur că vreți să vă deconectați?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Deconectați-vă"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Deconectați-vă"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Deconectare în curs…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Sunteți pe cale să vă deconectați de la ultima sesiune. Dacă vă deconectați acum, veți pierde accesul la mesajele criptate."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Ați dezactivat backup-ul"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Cheile dumneavoastră erau încă în curs de backup atunci când ați fost deconectat. Reconectați-vă pentru ca cheile dumneavoastră să poată fi salvate înainte de a vă deconecta."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Cheile dumneavoastră sunt încă în curs de backup"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Vă rugăm să așteptați până la finalizarea acestui proces înainte de a vă deconecta."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Cheile dumneavoastră sunt încă în curs de backup"</string>
|
||||
<string name="screen_signout_preference_item">"Deconectați-vă"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Sunteți pe cale să vă deconectați de la ultima sesiune. Dacă vă deconectați acum, veți pierde accesul la mesajele criptate."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Recuperarea nu este configurată"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Sunteți pe cale să vă deconectați de la ultima sesiune. Dacă vă deconectați acum, este posibil să pierdeți accesul la mesajele criptate."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Ați salvat cheia de recuperare?"</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_signout_confirmation_dialog_content">"Вы уверены, что вы хотите выйти?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Выйти"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Выйти"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Выполняется выход…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Вы собираетесь выйти из последнего сеанса. Если вы выйдете из системы сейчас, вы потеряете доступ к зашифрованным сообщениям."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Вы отключили резервное копирование"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Когда вы перешли в автономный режим, резервное копирование ваших ключей продолжалось. Повторно подключитесь, чтобы перед выходом из системы можно было создать резервную копию ключей."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Резервное копирование ключей все еще продолжается"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Пожалуйста, дождитесь завершения процесса, прежде чем выходить из системы."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Резервное копирование ключей все еще продолжается"</string>
|
||||
<string name="screen_signout_preference_item">"Выйти"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Вы собираетесь выйти из последнего сеанса. Если вы выйдете из системы сейчас, вы потеряете доступ к зашифрованным сообщениям."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Восстановление не настроено"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Вы собираетесь выйти из последнего сеанса. Если вы выйдете из системы сейчас, вы можете потерять доступ к зашифрованным сообщениям."</string>
|
||||
<string name="screen_signout_save_recovery_key_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_signout_confirmation_dialog_content">"Ste si istí, že sa chcete odhlásiť?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Odhlásiť sa"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Odhlásiť sa"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Prebieha odhlasovanie…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, stratíte prístup k svojim šifrovaným správam."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Vypli ste zálohovanie"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Keď ste sa odpojili od internetu, vaše kľúče sa ešte stále zálohovali. Pripojte sa znova k internetu, aby sa vaše kľúče mohli zálohovať pred odhlásením."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Vaše kľúče sa ešte stále zálohujú"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Pred odhlásením počkajte, kým sa to dokončí."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Vaše kľúče sa ešte stále zálohujú"</string>
|
||||
<string name="screen_signout_preference_item">"Odhlásiť sa"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, stratíte prístup k svojim šifrovaným správam."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Obnovenie nie je nastavené"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, môžete stratiť prístup k svojim šifrovaným správam."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Uložili ste kľúč na obnovenie?"</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_signout_confirmation_dialog_content">"Är du säker på att du vill logga ut?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Logga ut"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Logga ut"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Loggar ut …"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Du är på väg att logga ut ur din senaste session. Om du loggar ut nu kommer du att förlora åtkomsten till dina krypterade meddelanden."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Du har stängt av säkerhetskopiering"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Dina nycklar säkerhetskopierades fortfarande när du gick offline. Anslut igen så att dina nycklar kan säkerhetskopieras innan du loggar ut."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Dina nycklar säkerhetskopieras fortfarande"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Vänta tills detta är klart innan du loggar ut."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Dina nycklar säkerhetskopieras fortfarande"</string>
|
||||
<string name="screen_signout_preference_item">"Logga ut"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Du är på väg att logga ut ur din sista session. Om du loggar ut nu förlorar du åtkomsten till dina krypterade meddelanden."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Återställning inte inställd"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Du är på väg att logga ut från din senaste session. Om du loggar ut nu kan du förlora åtkomsten till dina krypterade meddelanden."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Har du sparat din återställningsnyckel?"</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_signout_confirmation_dialog_content">"Çıkış yapmak istediğinize emin misiniz?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Oturumu kapat"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Oturumu kapat"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Oturum kapatılıyor…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Son oturumunuzdan çıkmak üzeresiniz. Şimdi çıkış yaparsanız, şifrelenmiş mesajlarınıza erişiminizi kaybedersiniz."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Yedeklemeyi kapattınız"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Çevrimdışı olduğunuzda anahtarlarınız hala yedekleniyordu. Oturumu kapatmadan önce anahtarlarınızın yedeklenebilmesi için yeniden bağlanın."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Anahtarlarınız hala yedekleniyor"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Lütfen oturumu kapatmadan önce bunun tamamlanmasını bekleyin."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Anahtarlarınız hala yedekleniyor"</string>
|
||||
<string name="screen_signout_preference_item">"Oturumu kapat"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Son oturumunuzdan çıkmak üzeresiniz. Şimdi çıkış yaparsanız şifrelenmiş mesajlarınıza erişiminizi kaybedersiniz."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Kurtarma ayarlanmadı"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Son oturumunuzdan çıkmak üzeresiniz. Şimdi oturumu kapatırsanız şifrelenmiş mesajlarınıza erişiminizi kaybedebilirsiniz."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Kurtarma anahtarınızı kaydettiniz mi?"</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_signout_confirmation_dialog_content">"Ви впевнені, що бажаєте вийти?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Вийти"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Вийти"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Вихід…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Ви збираєтеся вийти зі свого останнього сеансу. Якщо ви вийдете зараз, ви втратите доступ до своїх зашифрованих повідомлень."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Ви вимкнули резервне копіювання"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Коли ви вийшли з мережі, резервна копія ваших ключів все ще створювалася. Повторно під\'єднайтеся, щоб зберегти резервну копію ключів перед виходом."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Резервне копіювання ваших ключів ще триває"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Дочекайтеся завершення процесу, перш ніж вийти."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Резервне копіювання ваших ключів ще триває"</string>
|
||||
<string name="screen_signout_preference_item">"Вийти"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Ви збираєтеся вийти зі свого останнього сеансу. Якщо ви вийдете зараз, ви втратите доступ до своїх зашифрованих повідомлень."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Відновлення не налаштовано"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Ви збираєтеся вийти зі свого останнього сеансу. Якщо вийти зараз, ви можете втратити доступ до зашифрованих повідомлень."</string>
|
||||
<string name="screen_signout_save_recovery_key_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_signout_confirmation_dialog_content">"کیا آپ واقعی خارج ہونا چاہتے ہیں؟"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"خارج ہوں"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"خارج ہوں"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"خارج ہورہاہے…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"آپ اپنے آخری جلسے سے خارج ہونے والے ہیں۔ اگر آپ ابھی خارج ہوجاتے ہیں تو آپ اپنے مرموز کردہ پیغامات تک رسائی سے محروم ہو جائیں گے۔"</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"آپنے پشتارہ بند کردیا ہے"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"جب آپ پرے خط تھے تب بھی آپ کی کلیدوں کا پشتارہ کیا جا رہا تھا۔ دوبارہ جڑیں تاکہ خارج ہونے سے پہلے آپ کی کلیدوں کا پشتارہ کیا جا سکے۔"</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"آپکی کلیدوں کا ابھی بھی پشتارہ کیا جا رہا ہے۔"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"برائے مہربانی خارج ہونے سے پہلے اسکے مکمل ہونے کا انتظار کریں"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"آپکی کلیدوں کا ابھی بھی پشتارہ کیا جا رہا ہے۔"</string>
|
||||
<string name="screen_signout_preference_item">"خارج ہوں"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"آپ اپنے آخری جلسے سے خارج ہونے والے ہیں۔ اگر آپ ابھی خارج ہوجاتے ہیں تو آپ اپنے مرموز کردہ پیغامات تک رسائی سے محروم ہو جائیں گے۔"</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"بازیابی غیر مرتب"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"آپ اپنے آخری جلسے سے خارج ہونے والے ہیں۔ اگر آپ ابھی خارج ہوجاتے ہیں تو ہوسکتا ہے کہ آپ اپنے مرموز کردہ پیغامات تک رسائی سے محروم ہو جائیں گے۔"</string>
|
||||
<string name="screen_signout_save_recovery_key_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_signout_confirmation_dialog_content">"Haqiqatan ham tizimdan chiqmoqchimisiz?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Tizimdan chiqish"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Tizimdan chiqish"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Chiqish…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Siz oxirgi sessiyangizdan chiqmoqdasiz. Agar hozir chiqib ketsangiz, shifrlangan xabarlaringizga kira olmaysiz."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Siz zaxira nusxasini oʻchirdingiz"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Siz oflayn bo‘lganingizda ham kalitlaringiz zaxiralanish jarayonida edi. Tizimdan chiqishdan oldin kalitlaringizning to‘liq zaxiralanishini ta’minlash uchun qayta ulanishingiz zarur."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Kalitlaringiz hamon zaxiralanmoqda"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Tizimdan chiqishdan oldin bu jarayon tugashini kuting."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Kalitlaringiz hamon zaxiralanmoqda"</string>
|
||||
<string name="screen_signout_preference_item">"Tizimdan chiqish"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Siz oxirgi sessiyangizdan chiqmoqdasiz. Agar hozir chiqib ketsangiz, shifrlangan xabarlaringizga kira olmaysiz."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Qayta tiklash sozlanmagan"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Siz oxirgi sessiyangizdan chiqmoqdasiz. Agar hozir chiqib ketsangiz, shifrlangan xabarlaringizga kira olmay qolishingiz mumkin."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Zaxira kalitingizni saqladingizmi?"</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_signout_confirmation_dialog_content">"您確定要登出嗎?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"登出"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"登出"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"正在登出…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"您將要登出上一次作業階段。若您現在登出,將會失去對加密訊息的存取權。"</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"您已關閉備份"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"當您離線時,您的金鑰仍在備份中。請重新連線才能在您登出前備份金鑰。"</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"您的金鑰仍在備份中"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"請等待此動作完成後再登出。"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"您的金鑰仍在備份中"</string>
|
||||
<string name="screen_signout_preference_item">"登出"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"您將要登出上一次作業階段。若您現在登出,將會失去對加密訊息的存取權。"</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"未設定復原金鑰"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"您將要登出上一次作業階段。若您現在登出,將會失去對加密訊息的存取權。"</string>
|
||||
<string name="screen_signout_save_recovery_key_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_signout_confirmation_dialog_content">"确定要登出吗?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"登出"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"登出"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"正在登出…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"即将登出最后一个会话。如果现在登出,将无法访问加密的消息。"</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"您已关闭备份"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"当你离线时,密钥仍在备份中。重新连接以便在登出之前备份密钥。"</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"您的密钥仍在备份中"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"请等待此操作完成后再登出。"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"您的密钥仍在备份中"</string>
|
||||
<string name="screen_signout_preference_item">"登出"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"即将登出最后一个会话。如果现在登出,将无法访问加密的消息。"</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"未设置恢复"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"即将登出最后一个会话。如果现在登出,将无法访问加密的消息。"</string>
|
||||
<string name="screen_signout_save_recovery_key_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_signout_confirmation_dialog_content">"Are you sure you want to sign out?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Sign out"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Sign out"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Signing out…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"You are about to sign out of your last session. If you sign out now, you will lose access to your encrypted messages."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"You have turned off backup"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Your keys were still being backed up when you went offline. Reconnect so that your keys can be backed up before signing out."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Your keys are still being backed up"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Please wait for this to complete before signing out."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Your keys are still being backed up"</string>
|
||||
<string name="screen_signout_preference_item">"Sign out"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"You are about to sign out of your last session. If you sign out now, you\'ll lose access to your encrypted messages."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Recovery not set up"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"You are about to sign out of your last session. If you sign out now, you might lose access to your encrypted messages."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Have you saved your recovery key?"</string>
|
||||
</resources>
|
||||
+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.logout.impl
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.logout.api.LogoutEntryPoint
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.node.TestParentNode
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultLogoutEntryPointTest {
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Test
|
||||
fun `test node builder`() {
|
||||
val entryPoint = DefaultLogoutEntryPoint()
|
||||
|
||||
val parentNode = TestParentNode.create { buildContext, plugins ->
|
||||
LogoutNode(
|
||||
buildContext = buildContext,
|
||||
plugins = plugins,
|
||||
presenter = createLogoutPresenter(),
|
||||
)
|
||||
}
|
||||
val callback = object : LogoutEntryPoint.Callback {
|
||||
override fun navigateToSecureBackup() = lambdaError()
|
||||
}
|
||||
val result = entryPoint.createNode(
|
||||
parentNode = parentNode,
|
||||
buildContext = BuildContext.root(null),
|
||||
callback = callback,
|
||||
)
|
||||
assertThat(result).isInstanceOf(LogoutNode::class.java)
|
||||
assertThat(result.plugins).contains(callback)
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
package io.element.android.features.logout.impl
|
||||
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
||||
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
||||
import io.element.android.libraries.sessionstorage.test.aSessionData
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultLogoutUseCaseTest {
|
||||
@Test
|
||||
fun `test logout from one session`() = runTest {
|
||||
val logoutLambda1 = lambdaRecorder<Boolean, Boolean, Unit> { _, _ -> }
|
||||
val client1 = FakeMatrixClient(A_USER_ID).apply {
|
||||
logoutLambda = logoutLambda1
|
||||
}
|
||||
val sut = DefaultLogoutUseCase(
|
||||
sessionStore = InMemorySessionStore(
|
||||
initialList = listOf(
|
||||
aSessionData(sessionId = A_USER_ID.value),
|
||||
)
|
||||
),
|
||||
matrixClientProvider = FakeMatrixClientProvider(
|
||||
getClient = { sessionId ->
|
||||
when (sessionId) {
|
||||
A_USER_ID -> Result.success(client1)
|
||||
else -> error("Unexpected sessionId")
|
||||
}
|
||||
}
|
||||
),
|
||||
)
|
||||
sut.logoutAll(ignoreSdkError = true)
|
||||
logoutLambda1.assertions().isCalledOnce().with(value(true), value(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test logout from several sessions`() = runTest {
|
||||
val logoutLambda1 = lambdaRecorder<Boolean, Boolean, Unit> { _, _ -> }
|
||||
val logoutLambda2 = lambdaRecorder<Boolean, Boolean, Unit> { _, _ -> }
|
||||
val client1 = FakeMatrixClient(A_USER_ID).apply {
|
||||
logoutLambda = logoutLambda1
|
||||
}
|
||||
val client2 = FakeMatrixClient(A_USER_ID_2).apply {
|
||||
logoutLambda = logoutLambda2
|
||||
}
|
||||
val sut = DefaultLogoutUseCase(
|
||||
sessionStore = InMemorySessionStore(
|
||||
initialList = listOf(
|
||||
aSessionData(sessionId = A_USER_ID.value),
|
||||
aSessionData(sessionId = A_USER_ID_2.value),
|
||||
)
|
||||
),
|
||||
matrixClientProvider = FakeMatrixClientProvider(
|
||||
getClient = { sessionId ->
|
||||
when (sessionId) {
|
||||
A_USER_ID -> Result.success(client1)
|
||||
A_USER_ID_2 -> Result.success(client2)
|
||||
else -> error("Unexpected sessionId")
|
||||
}
|
||||
}
|
||||
),
|
||||
)
|
||||
sut.logoutAll(ignoreSdkError = true)
|
||||
logoutLambda1.assertions().isCalledOnce().with(value(true), value(true))
|
||||
logoutLambda2.assertions().isCalledOnce().with(value(true), value(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test logout session not found is ignored`() = runTest {
|
||||
val sut = DefaultLogoutUseCase(
|
||||
sessionStore = InMemorySessionStore(
|
||||
initialList = listOf(
|
||||
aSessionData(sessionId = A_USER_ID.value),
|
||||
)
|
||||
),
|
||||
matrixClientProvider = FakeMatrixClientProvider(
|
||||
getClient = { sessionId ->
|
||||
when (sessionId) {
|
||||
A_USER_ID -> Result.failure(Exception("Session not found"))
|
||||
else -> error("Unexpected sessionId")
|
||||
}
|
||||
}
|
||||
),
|
||||
)
|
||||
sut.logoutAll(ignoreSdkError = true)
|
||||
// No error
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test logout no sessions`() = runTest {
|
||||
val sut = DefaultLogoutUseCase(
|
||||
sessionStore = InMemorySessionStore(
|
||||
initialList = emptyList()
|
||||
),
|
||||
matrixClientProvider = FakeMatrixClientProvider(
|
||||
getClient = { sessionId ->
|
||||
when (sessionId) {
|
||||
else -> error("Unexpected sessionId")
|
||||
}
|
||||
}
|
||||
),
|
||||
)
|
||||
sut.logoutAll(ignoreSdkError = true)
|
||||
// No error
|
||||
}
|
||||
}
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.ReceiveTurbine
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||
import io.element.android.libraries.workmanager.test.FakeWorkManagerScheduler
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class LogoutPresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createLogoutPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.isLastDevice).isFalse()
|
||||
assertThat(initialState.backupState).isEqualTo(BackupState.UNKNOWN)
|
||||
assertThat(initialState.doesBackupExistOnServer).isTrue()
|
||||
assertThat(initialState.recoveryState).isEqualTo(RecoveryState.UNKNOWN)
|
||||
assertThat(initialState.backupUploadState).isEqualTo(BackupUploadState.Unknown)
|
||||
assertThat(initialState.waitingForALongTime).isFalse()
|
||||
assertThat(initialState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state - last session`() = runTest {
|
||||
val presenter = createLogoutPresenter(
|
||||
encryptionService = FakeEncryptionService().apply {
|
||||
emitIsLastDevice(true)
|
||||
}
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(2)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.isLastDevice).isTrue()
|
||||
assertThat(initialState.backupUploadState).isEqualTo(BackupUploadState.Unknown)
|
||||
assertThat(initialState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state - waiting a long time`() = runTest {
|
||||
val encryptionService = FakeEncryptionService()
|
||||
encryptionService.givenWaitForBackupUploadSteadyStateFlow(
|
||||
flow {
|
||||
emit(BackupUploadState.Waiting)
|
||||
delay(3_000)
|
||||
}
|
||||
)
|
||||
val presenter = createLogoutPresenter(
|
||||
encryptionService = encryptionService
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.waitingForALongTime).isFalse()
|
||||
assertThat(initialState.backupUploadState).isEqualTo(BackupUploadState.Unknown)
|
||||
val waitingState = awaitItem()
|
||||
assertThat(waitingState.backupUploadState).isEqualTo(BackupUploadState.Waiting)
|
||||
assertThat(initialState.waitingForALongTime).isFalse()
|
||||
skipItems(1)
|
||||
val waitingALongTimeState = awaitItem()
|
||||
assertThat(waitingALongTimeState.backupUploadState).isEqualTo(BackupUploadState.Waiting)
|
||||
assertThat(waitingALongTimeState.waitingForALongTime).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state - backing up`() = runTest {
|
||||
val encryptionService = FakeEncryptionService()
|
||||
encryptionService.givenWaitForBackupUploadSteadyStateFlow(
|
||||
flow {
|
||||
emit(BackupUploadState.Waiting)
|
||||
delay(1)
|
||||
emit(BackupUploadState.Uploading(backedUpCount = 1, totalCount = 2))
|
||||
delay(1)
|
||||
emit(BackupUploadState.Done)
|
||||
}
|
||||
)
|
||||
val presenter = createLogoutPresenter(
|
||||
encryptionService = encryptionService
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.isLastDevice).isFalse()
|
||||
assertThat(initialState.backupUploadState).isEqualTo(BackupUploadState.Unknown)
|
||||
assertThat(initialState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
val waitingState = awaitItem()
|
||||
assertThat(waitingState.backupUploadState).isEqualTo(BackupUploadState.Waiting)
|
||||
skipItems(1)
|
||||
val uploadingState = awaitItem()
|
||||
assertThat(uploadingState.backupUploadState).isEqualTo(BackupUploadState.Uploading(backedUpCount = 1, totalCount = 2))
|
||||
val doneState = awaitItem()
|
||||
assertThat(doneState.backupUploadState).isEqualTo(BackupUploadState.Done)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - logout then cancel`() = runTest {
|
||||
val presenter = createLogoutPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false))
|
||||
val confirmationState = awaitItem()
|
||||
assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
initialState.eventSink.invoke(LogoutEvents.CloseDialogs)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - logout then confirm`() = runTest {
|
||||
val cancelWorkManagerJobsLambda = lambdaRecorder<SessionId, Unit> {}
|
||||
val workManagerScheduler = FakeWorkManagerScheduler(cancelLambda = cancelWorkManagerJobsLambda)
|
||||
val presenter = createLogoutPresenter(workManagerScheduler = workManagerScheduler)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false))
|
||||
val confirmationState = awaitItem()
|
||||
assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
confirmationState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false))
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val successState = awaitItem()
|
||||
assertThat(successState.logoutAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
|
||||
cancelWorkManagerJobsLambda.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - logout with error then cancel`() = runTest {
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
logoutLambda = { _, _ ->
|
||||
throw AN_EXCEPTION
|
||||
}
|
||||
}
|
||||
val presenter = createLogoutPresenter(
|
||||
matrixClient,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false))
|
||||
val confirmationState = awaitItem()
|
||||
assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
confirmationState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false))
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val errorState = awaitItem()
|
||||
assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
|
||||
errorState.eventSink.invoke(LogoutEvents.CloseDialogs)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - logout with error then force`() = runTest {
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
logoutLambda = { ignoreSdkError, _ ->
|
||||
if (!ignoreSdkError) {
|
||||
throw AN_EXCEPTION
|
||||
}
|
||||
}
|
||||
}
|
||||
val presenter = createLogoutPresenter(
|
||||
matrixClient,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false))
|
||||
val confirmationState = awaitItem()
|
||||
assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
confirmationState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false))
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val errorState = awaitItem()
|
||||
assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
|
||||
errorState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = true))
|
||||
val loadingState2 = awaitItem()
|
||||
assertThat(loadingState2.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val successState = awaitItem()
|
||||
assertThat(successState.logoutAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
|
||||
skipItems(2)
|
||||
return awaitItem()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createLogoutPresenter(
|
||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
||||
encryptionService: EncryptionService = FakeEncryptionService(),
|
||||
workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(cancelLambda = {}),
|
||||
): LogoutPresenter = LogoutPresenter(
|
||||
matrixClient = matrixClient,
|
||||
encryptionService = encryptionService,
|
||||
workManagerScheduler = workManagerScheduler,
|
||||
)
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
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 io.element.android.tests.testutils.pressTag
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LogoutViewTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `clicking on logout sends a LogoutEvents`() {
|
||||
val eventsRecorder = EventsRecorder<LogoutEvents>()
|
||||
rule.setLogoutView(
|
||||
aLogoutState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_signout)
|
||||
eventsRecorder.assertSingle(LogoutEvents.Logout(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `confirming logout sends a LogoutEvents`() {
|
||||
val eventsRecorder = EventsRecorder<LogoutEvents>()
|
||||
rule.setLogoutView(
|
||||
aLogoutState(
|
||||
logoutAction = AsyncAction.ConfirmingNoParams,
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.pressTag(TestTags.dialogPositive.value)
|
||||
eventsRecorder.assertSingle(LogoutEvents.Logout(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on back invoke back callback`() {
|
||||
val eventsRecorder = EventsRecorder<LogoutEvents>(expectEvents = false)
|
||||
ensureCalledOnce { callback ->
|
||||
rule.setLogoutView(
|
||||
aLogoutState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
onBackClick = callback,
|
||||
)
|
||||
rule.pressBack()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on confirm after error sends a LogoutEvents`() {
|
||||
val eventsRecorder = EventsRecorder<LogoutEvents>()
|
||||
rule.setLogoutView(
|
||||
aLogoutState(
|
||||
logoutAction = AsyncAction.Failure(Exception("Failed to logout")),
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_signout_anyway)
|
||||
eventsRecorder.assertSingle(LogoutEvents.Logout(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on cancel after error sends a LogoutEvents`() {
|
||||
val eventsRecorder = EventsRecorder<LogoutEvents>()
|
||||
rule.setLogoutView(
|
||||
aLogoutState(
|
||||
logoutAction = AsyncAction.Failure(Exception("Failed to logout")),
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_cancel)
|
||||
eventsRecorder.assertSingle(LogoutEvents.CloseDialogs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `last session setting button invoke onChangeRecoveryKeyClicked`() {
|
||||
val eventsRecorder = EventsRecorder<LogoutEvents>(expectEvents = false)
|
||||
ensureCalledOnce { callback ->
|
||||
rule.setLogoutView(
|
||||
aLogoutState(
|
||||
isLastDevice = true,
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
onChangeRecoveryKeyClick = callback,
|
||||
)
|
||||
rule.clickOn(CommonStrings.common_settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setLogoutView(
|
||||
state: LogoutState,
|
||||
onChangeRecoveryKeyClick: () -> Unit = EnsureNeverCalled(),
|
||||
onBackClick: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
setContent {
|
||||
LogoutView(
|
||||
state = state,
|
||||
onChangeRecoveryKeyClick = onChangeRecoveryKeyClick,
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.logout.impl.direct
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvents
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
import io.element.android.tests.testutils.pressBackKey
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class DefaultDirectLogoutViewTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `clicking on confirm logout sends expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<DirectLogoutEvents>()
|
||||
rule.setDefaultDirectLogoutView(
|
||||
state = aDirectLogoutState(
|
||||
logoutAction = AsyncAction.ConfirmingNoParams,
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_signout)
|
||||
eventsRecorder.assertSingle(DirectLogoutEvents.Logout(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on cancel logout sends expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<DirectLogoutEvents>()
|
||||
rule.setDefaultDirectLogoutView(
|
||||
state = aDirectLogoutState(
|
||||
logoutAction = AsyncAction.ConfirmingNoParams,
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_cancel)
|
||||
eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs)
|
||||
}
|
||||
|
||||
@Ignore("Pressing back key should dismiss the dialog, and so generate the expected event, but it's not the case.")
|
||||
@Test
|
||||
fun `clicking on back invoke back callback`() {
|
||||
val eventsRecorder = EventsRecorder<DirectLogoutEvents>()
|
||||
rule.setDefaultDirectLogoutView(
|
||||
state = aDirectLogoutState(
|
||||
logoutAction = AsyncAction.ConfirmingNoParams,
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
)
|
||||
rule.pressBackKey()
|
||||
eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on confirm after error sends expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<DirectLogoutEvents>()
|
||||
rule.setDefaultDirectLogoutView(
|
||||
state = aDirectLogoutState(
|
||||
logoutAction = AsyncAction.Failure(Exception("Error")),
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_signout_anyway)
|
||||
eventsRecorder.assertSingle(DirectLogoutEvents.Logout(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on cancel after error sends expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<DirectLogoutEvents>()
|
||||
rule.setDefaultDirectLogoutView(
|
||||
state = aDirectLogoutState(
|
||||
logoutAction = AsyncAction.Failure(Exception("Error")),
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_cancel)
|
||||
eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setDefaultDirectLogoutView(
|
||||
state: DirectLogoutState,
|
||||
) {
|
||||
setContent {
|
||||
DefaultDirectLogoutView().Render(state)
|
||||
}
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-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.logout.impl.direct
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.ReceiveTurbine
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvents
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class DirectLogoutPresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createDirectLogoutPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.canDoDirectSignOut).isTrue()
|
||||
assertThat(initialState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state - last session`() = runTest {
|
||||
val presenter = createDirectLogoutPresenter(
|
||||
encryptionService = FakeEncryptionService().apply {
|
||||
emitIsLastDevice(true)
|
||||
}
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.canDoDirectSignOut).isFalse()
|
||||
assertThat(initialState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state - backing up`() = runTest {
|
||||
val encryptionService = FakeEncryptionService()
|
||||
encryptionService.givenWaitForBackupUploadSteadyStateFlow(
|
||||
flow {
|
||||
emit(BackupUploadState.Waiting)
|
||||
}
|
||||
)
|
||||
val presenter = createDirectLogoutPresenter(
|
||||
encryptionService = encryptionService
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.canDoDirectSignOut).isFalse()
|
||||
assertThat(initialState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - logout then cancel`() = runTest {
|
||||
val presenter = createDirectLogoutPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false))
|
||||
val confirmationState = awaitItem()
|
||||
assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
initialState.eventSink.invoke(DirectLogoutEvents.CloseDialogs)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - logout then confirm`() = runTest {
|
||||
val presenter = createDirectLogoutPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false))
|
||||
val confirmationState = awaitItem()
|
||||
assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
confirmationState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false))
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val successState = awaitItem()
|
||||
assertThat(successState.logoutAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - logout with error then cancel`() = runTest {
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
logoutLambda = { _, _ ->
|
||||
throw AN_EXCEPTION
|
||||
}
|
||||
}
|
||||
val presenter = createDirectLogoutPresenter(
|
||||
matrixClient,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false))
|
||||
val confirmationState = awaitItem()
|
||||
assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
confirmationState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false))
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val errorState = awaitItem()
|
||||
assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
|
||||
errorState.eventSink.invoke(DirectLogoutEvents.CloseDialogs)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - logout with error then force`() = runTest {
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
logoutLambda = { ignoreSdkError, _ ->
|
||||
if (!ignoreSdkError) {
|
||||
throw AN_EXCEPTION
|
||||
}
|
||||
}
|
||||
}
|
||||
val presenter = createDirectLogoutPresenter(
|
||||
matrixClient,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false))
|
||||
val confirmationState = awaitItem()
|
||||
assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
confirmationState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false))
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val errorState = awaitItem()
|
||||
assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
|
||||
errorState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = true))
|
||||
val loadingState2 = awaitItem()
|
||||
assertThat(loadingState2.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val successState = awaitItem()
|
||||
assertThat(successState.logoutAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
|
||||
return awaitItem()
|
||||
}
|
||||
|
||||
private fun createDirectLogoutPresenter(
|
||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
||||
encryptionService: EncryptionService = FakeEncryptionService(),
|
||||
): DirectLogoutPresenter = DirectLogoutPresenter(
|
||||
matrixClient = matrixClient,
|
||||
encryptionService = encryptionService,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user