Merge branch 'develop' into fix

This commit is contained in:
ganfra 2025-09-23 14:38:45 +02:00 committed by GitHub
commit ad63de00df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
207 changed files with 2230 additions and 1541 deletions

View file

@ -2,7 +2,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Gweinyddwyr yn unig"</string>
<string name="screen_room_change_permissions_ban_people">"Gwahardd pobl"</string>
<string name="screen_room_change_permissions_delete_messages">"Dileu negeseuon"</string>
<string name="screen_room_change_permissions_delete_messages">"Tynnu negeseuon"</string>
<string name="screen_room_change_permissions_everyone">"Pawb"</string>
<string name="screen_room_change_permissions_invite_people">"Gwahodd pobl a derbyn ceisiadau i ymuno"</string>
<string name="screen_room_change_permissions_member_moderation">"Cymedroli aelodau"</string>
@ -17,13 +17,17 @@
<string name="screen_room_change_role_administrators_title">"Golygu Gweinyddwyr"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Fyddwch chi ddim yn gallu dadwneud y weithred hon. Rydych chi\'n hyrwyddo\'r defnyddiwr i gael yr un lefel pŵer â chi."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Ychwanegu Gweinyddwr?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Fyddwch chi ddim yn gallu dadwneud y weithred hon. Rydych yn trosglwyddo\'r berchnogaeth i\'r defnyddwyr a ddewiswyd. Unwaith y byddwch yn gadael bydd hyn yn barhaol."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Trosglwyddo perchnogaeth?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Gostwng"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Fyddwch chi ddim yn gallu dadwneud y newid hwn gan eich bod yn israddio eich hun, os mai chi yw\'r defnyddiwr breintiedig olaf yn yr ystafell bydd yn amhosibl adennill breintiau."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Israddio eich hun?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Yn aros)"</string>
<string name="screen_room_change_role_invited_member_name_android">"Yn aros"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Mae gan weinyddwyr freintiau cymedrolwr yn awtomatig"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Mae gan berchnogion freintiau gweinyddwr yn awtomatig."</string>
<string name="screen_room_change_role_moderators_title">"Golygu Cymedrolwyr"</string>
<string name="screen_room_change_role_owners_title">"Dewiswch Berchnogion"</string>
<string name="screen_room_change_role_section_administrators">"Gweinyddwyr"</string>
<string name="screen_room_change_role_section_moderators">"Cymedrolwyr"</string>
<string name="screen_room_change_role_section_users">"Aelodau"</string>
@ -48,15 +52,18 @@
<string name="screen_room_member_list_pending_header_title">"Dan ystyriaeth"</string>
<string name="screen_room_member_list_role_administrator">"Gweinyddwr"</string>
<string name="screen_room_member_list_role_moderator">"Cymedrolwr"</string>
<string name="screen_room_member_list_role_owner">"Perchennog"</string>
<string name="screen_room_member_list_room_members_header_title">"Aelodau\'r ystafell"</string>
<string name="screen_room_member_list_unbanning_user">"Dad-wahardd %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Gweinyddwyr"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Gweinyddwyr a pherchnogion"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Newid fy rôl"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Israddio aelod"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Israddio cymedrolwr"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Cymedroli aelodau"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Negeseuon a chynnwys"</string>
<string name="screen_room_roles_and_permissions_moderators">"Cymedrolwyr"</string>
<string name="screen_room_roles_and_permissions_owners">"Perchnogion"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Caniatâd"</string>
<string name="screen_room_roles_and_permissions_reset">"Ailosod caniatâd"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Ar ôl i chi ailosod caniatâd, byddwch yn colli\'r gosodiadau cyfredol."</string>

View file

@ -16,12 +16,12 @@
<string name="screen_room_change_permissions_send_messages">"Viestien lähettäminen"</string>
<string name="screen_room_change_role_administrators_title">"Muokkaa ylläpitäjiä"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Et voi peruuttaa tätä toimenpidettä. Ylennät käyttäjän samalle oikeustasolle kuin sinä."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Lisää ylläpitäjä?"</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Lisätäänkö ylläpitäjä?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Et voi kumota tätä toimintoa. Olet siirtämässä omistajuuden valituille käyttäjille. Kun poistut, muutos on pysyvä."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Siirretäänkö omistajuus?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Alenna"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Et voi perua tätä muutosta, koska olet alentamassa itseäsi. Jos olet viimeinen oikeutettu henkilö tässä huoneessa, oikeuksia ei voi enää saada takaisin."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Alenna itsesi?"</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Haluatko alentaa itsesi?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Kutsuttu)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Kutsuttu)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Ylläpitäjillä on automaattisesti valvojan oikeudet"</string>
@ -32,7 +32,7 @@
<string name="screen_room_change_role_section_moderators">"Valvojat"</string>
<string name="screen_room_change_role_section_users">"Jäsenet"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Sinulla on tallentamattomia muutoksia"</string>
<string name="screen_room_change_role_unsaved_changes_title">"Tallenna muutokset?"</string>
<string name="screen_room_change_role_unsaved_changes_title">"Tallennetaanko muutokset?"</string>
<string name="screen_room_member_list_banned_empty">"Tässä huoneessa ei ole porttikieltoja"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d henkilö"</item>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_deactivate_account_list_item_3">"Delete your account information from our server."</string>
</resources>

View file

@ -15,9 +15,6 @@ import kotlinx.coroutines.flow.StateFlow
interface FtueService {
/** The current state of the FTUE. */
val state: StateFlow<FtueState>
/** Reset the FTUE state. */
suspend fun reset()
}
/** The state of the FTUE. */

View file

@ -14,7 +14,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
@ -30,18 +29,16 @@ import io.element.android.features.ftue.impl.notifications.NotificationsOptInNod
import io.element.android.features.ftue.impl.sessionverification.FtueSessionVerificationFlowNode
import io.element.android.features.ftue.impl.state.DefaultFtueService
import io.element.android.features.ftue.impl.state.FtueStep
import io.element.android.features.ftue.impl.state.InternalFtueState
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.di.SessionScope
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
@ContributesNode(SessionScope::class)
@ -49,9 +46,8 @@ import kotlinx.parcelize.Parcelize
class FtueFlowNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
private val ftueState: DefaultFtueService,
private val defaultFtueService: DefaultFtueService,
private val analyticsEntryPoint: AnalyticsEntryPoint,
private val analyticsService: AnalyticsService,
private val lockScreenEntryPoint: LockScreenEntryPoint,
) : BaseFlowNode<FtueFlowNode.NavTarget>(
backstack = BackStack(
@ -80,19 +76,11 @@ class FtueFlowNode(
override fun onBuilt() {
super.onBuilt()
lifecycle.subscribe(onCreate = {
moveToNextStepIfNeeded()
})
analyticsService.didAskUserConsentFlow
.distinctUntilChanged()
.onEach { moveToNextStepIfNeeded() }
.launchIn(lifecycleScope)
ftueState.isVerificationStatusKnown
.filter { it }
.onEach { moveToNextStepIfNeeded() }
defaultFtueService.ftueStepStateFlow
.filterIsInstance(InternalFtueState.Incomplete::class)
.onEach {
showStep(it.nextStep)
}
.launchIn(lifecycleScope)
}
@ -104,7 +92,7 @@ class FtueFlowNode(
is NavTarget.SessionVerification -> {
val callback = object : FtueSessionVerificationFlowNode.Callback {
override fun onDone() {
moveToNextStepIfNeeded()
defaultFtueService.onUserCompletedSessionVerification()
}
}
createNode<FtueSessionVerificationFlowNode>(buildContext, listOf(callback))
@ -112,7 +100,7 @@ class FtueFlowNode(
NavTarget.NotificationsOptIn -> {
val callback = object : NotificationsOptInNode.Callback {
override fun onNotificationsOptInFinished() {
moveToNextStepIfNeeded()
defaultFtueService.updateFtueStep()
}
}
createNode<NotificationsOptInNode>(buildContext, listOf(callback))
@ -123,7 +111,7 @@ class FtueFlowNode(
NavTarget.LockScreenSetup -> {
val callback = object : LockScreenEntryPoint.Callback {
override fun onSetupDone() {
moveToNextStepIfNeeded()
defaultFtueService.updateFtueStep()
}
}
lockScreenEntryPoint.nodeBuilder(this, buildContext, LockScreenEntryPoint.Target.Setup)
@ -133,8 +121,8 @@ class FtueFlowNode(
}
}
private fun moveToNextStepIfNeeded() = lifecycleScope.launch {
when (ftueState.getNextStep()) {
private fun showStep(ftueStep: FtueStep) {
when (ftueStep) {
FtueStep.WaitingForInitialState -> {
backstack.newRoot(NavTarget.Placeholder)
}
@ -150,7 +138,6 @@ class FtueFlowNode(
FtueStep.LockscreenSetup -> {
backstack.newRoot(NavTarget.LockScreenSetup)
}
null -> Unit
}
}

View file

@ -9,13 +9,13 @@ package io.element.android.features.ftue.impl.state
import android.Manifest
import android.os.Build
import androidx.annotation.VisibleForTesting
import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import io.element.android.features.ftue.api.state.FtueService
import io.element.android.features.ftue.api.state.FtueState
import io.element.android.features.lockscreen.api.LockScreenService
import io.element.android.libraries.core.coroutine.mapState
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
@ -26,61 +26,70 @@ import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@ContributesBinding(SessionScope::class)
@SingleIn(SessionScope::class)
@Inject
class DefaultFtueService(
private val sdkVersionProvider: BuildVersionSdkIntProvider,
@SessionCoroutineScope sessionCoroutineScope: CoroutineScope,
@SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope,
private val analyticsService: AnalyticsService,
private val permissionStateProvider: PermissionStateProvider,
private val lockScreenService: LockScreenService,
private val sessionVerificationService: SessionVerificationService,
private val sessionPreferencesStore: SessionPreferencesStore,
) : FtueService {
override val state = MutableStateFlow<FtueState>(FtueState.Unknown)
private val userNeedsToConfirmSessionVerificationSuccess = MutableStateFlow(false)
/**
* This flow emits true when the FTUE flow is ready to be displayed.
* In this case, the FTUE flow is ready when the session verification status is known.
*/
val isVerificationStatusKnown = sessionVerificationService.sessionVerifiedStatus
.map { it != SessionVerifiedStatus.Unknown }
.distinctUntilChanged()
val ftueStepStateFlow = MutableStateFlow<InternalFtueState>(InternalFtueState.Unknown)
override suspend fun reset() {
analyticsService.reset()
if (sdkVersionProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
permissionStateProvider.resetPermission(Manifest.permission.POST_NOTIFICATIONS)
override val state = ftueStepStateFlow
.mapState {
when (it) {
is InternalFtueState.Unknown -> FtueState.Unknown
is InternalFtueState.Incomplete -> FtueState.Incomplete
is InternalFtueState.Complete -> FtueState.Complete
}
}
init {
combine(
sessionVerificationService.sessionVerifiedStatus.onEach { sessionVerifiedStatus ->
if (sessionVerifiedStatus == SessionVerifiedStatus.NotVerified) {
// Ensure we wait for the user to confirm the session verified screen before going further
userNeedsToConfirmSessionVerificationSuccess.value = true
}
},
userNeedsToConfirmSessionVerificationSuccess,
analyticsService.didAskUserConsentFlow.distinctUntilChanged(),
) {
updateFtueStep()
}
.launchIn(sessionCoroutineScope)
}
fun updateFtueStep() = sessionCoroutineScope.launch {
val step = getNextStep(null)
ftueStepStateFlow.value = when (step) {
null -> InternalFtueState.Complete
else -> InternalFtueState.Incomplete(step)
}
}
init {
sessionVerificationService.sessionVerifiedStatus
.onEach { updateState() }
.launchIn(sessionCoroutineScope)
analyticsService.didAskUserConsentFlow
.distinctUntilChanged()
.onEach { updateState() }
.launchIn(sessionCoroutineScope)
}
suspend fun getNextStep(currentStep: FtueStep? = null): FtueStep? =
when (currentStep) {
private suspend fun getNextStep(completedStep: FtueStep? = null): FtueStep? =
when (completedStep) {
null -> if (!isSessionVerificationStateReady()) {
FtueStep.WaitingForInitialState
} else {
getNextStep(FtueStep.WaitingForInitialState)
}
FtueStep.WaitingForInitialState -> if (isSessionNotVerified()) {
FtueStep.WaitingForInitialState -> if (isSessionNotVerified() || userNeedsToConfirmSessionVerificationSuccess.value) {
FtueStep.SessionVerification
} else {
getNextStep(FtueStep.SessionVerification)
@ -108,9 +117,6 @@ class DefaultFtueService(
}
private suspend fun isSessionNotVerified(): Boolean {
// Wait until the session verification status is known
isVerificationStatusKnown.filter { it }.first()
return sessionVerificationService.sessionVerifiedStatus.value == SessionVerifiedStatus.NotVerified && !canSkipVerification()
}
@ -137,14 +143,8 @@ class DefaultFtueService(
return lockScreenService.isSetupRequired().first()
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal suspend fun updateState() {
val nextStep = getNextStep()
state.value = when {
// Final state, there aren't any more next steps
nextStep == null -> FtueState.Complete
else -> FtueState.Incomplete
}
fun onUserCompletedSessionVerification() {
userNeedsToConfirmSessionVerificationSuccess.value = false
}
}

View file

@ -0,0 +1,18 @@
/*
* 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.ftue.impl.state
sealed interface InternalFtueState {
data object Unknown : InternalFtueState
data class Incomplete(
val nextStep: FtueStep,
) : InternalFtueState
data object Complete : InternalFtueState
}

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_identity_confirmation_create_new_recovery_key">"Create a new backup password"</string>
<string name="screen_identity_confirmation_subtitle">"Confirm this device to set up secure messaging."</string>
<string name="screen_identity_confirmation_title">"Confirm it\'s you"</string>
<string name="screen_identity_confirmation_use_recovery_key">"Use backup password"</string>
<string name="screen_identity_confirmed_title">"Device confirmed"</string>
<string name="screen_session_verification_enter_recovery_key">"Enter backup password"</string>
</resources>

View file

@ -14,7 +14,6 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
import com.google.common.truth.Truth.assertThat
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import kotlinx.coroutines.test.runTest
@ -36,8 +35,7 @@ class DefaultFtueEntryPointTest {
buildContext = buildContext,
plugins = plugins,
analyticsEntryPoint = { _, _ -> lambdaError() },
ftueState = createDefaultFtueService(),
analyticsService = FakeAnalyticsService(),
defaultFtueService = createDefaultFtueService(),
lockScreenEntryPoint = object : LockScreenEntryPoint {
override fun nodeBuilder(
parentNode: com.bumble.appyx.core.node.Node,

View file

@ -13,6 +13,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.ftue.api.state.FtueState
import io.element.android.features.ftue.impl.state.DefaultFtueService
import io.element.android.features.ftue.impl.state.FtueStep
import io.element.android.features.ftue.impl.state.InternalFtueState
import io.element.android.features.lockscreen.api.LockScreenService
import io.element.android.features.lockscreen.test.FakeLockScreenService
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
@ -26,8 +27,6 @@ import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.noop.NoopAnalyticsService
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
@ -69,9 +68,11 @@ class DefaultFtueServiceTest {
analyticsService.setDidAskUserConsent()
permissionStateProvider.setPermissionGranted()
lockScreenService.setIsPinSetup(true)
service.updateState()
assertThat(service.state.value).isEqualTo(FtueState.Complete)
service.updateFtueStep()
service.state.test {
assertThat(awaitItem()).isEqualTo(FtueState.Unknown)
assertThat(awaitItem()).isEqualTo(FtueState.Complete)
}
}
@Test
@ -90,9 +91,11 @@ class DefaultFtueServiceTest {
sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.Verified)
permissionStateProvider.setPermissionGranted()
lockScreenService.setIsPinSetup(true)
service.updateState()
assertThat(service.state.value).isEqualTo(FtueState.Complete)
service.updateFtueStep()
service.state.test {
assertThat(awaitItem()).isEqualTo(FtueState.Unknown)
assertThat(awaitItem()).isEqualTo(FtueState.Complete)
}
}
@Test
@ -109,35 +112,30 @@ class DefaultFtueServiceTest {
permissionStateProvider = permissionStateProvider,
lockScreenService = lockScreenService,
)
val steps = mutableListOf<FtueStep?>()
// Session verification
steps.add(service.getNextStep(steps.lastOrNull()))
sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.NotVerified)
// Notifications opt in
steps.add(service.getNextStep(steps.lastOrNull()))
permissionStateProvider.setPermissionGranted()
// Entering PIN code
steps.add(service.getNextStep(steps.lastOrNull()))
lockScreenService.setIsPinSetup(true)
// Analytics opt in
steps.add(service.getNextStep(steps.lastOrNull()))
analyticsService.setDidAskUserConsent()
// Final step (null)
steps.add(service.getNextStep(steps.lastOrNull()))
assertThat(steps).containsExactly(
FtueStep.SessionVerification,
FtueStep.NotificationsOptIn,
FtueStep.LockscreenSetup,
FtueStep.AnalyticsOptIn,
// Final state
null,
)
service.ftueStepStateFlow.test {
assertThat(awaitItem()).isEqualTo(InternalFtueState.Unknown)
// Session verification
assertThat(awaitItem()).isEqualTo(InternalFtueState.Incomplete(FtueStep.SessionVerification))
sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.Verified)
// User completes verification
service.onUserCompletedSessionVerification()
// Notifications opt in
assertThat(awaitItem()).isEqualTo(InternalFtueState.Incomplete(FtueStep.NotificationsOptIn))
permissionStateProvider.setPermissionGranted()
// Simulate event from NotificationsOptInNode.Callback.onNotificationsOptInFinished
service.updateFtueStep()
// Entering PIN code
assertThat(awaitItem()).isEqualTo(InternalFtueState.Incomplete(FtueStep.LockscreenSetup))
lockScreenService.setIsPinSetup(true)
// Simulate event from LockScreenEntryPoint.Callback.onSetupDone()
service.updateFtueStep()
// Analytics opt in
assertThat(awaitItem()).isEqualTo(InternalFtueState.Incomplete(FtueStep.AnalyticsOptIn))
analyticsService.setDidAskUserConsent()
// Final step
assertThat(awaitItem()).isEqualTo(InternalFtueState.Complete)
}
}
@Test
@ -158,10 +156,13 @@ class DefaultFtueServiceTest {
permissionStateProvider.setPermissionGranted()
lockScreenService.setIsPinSetup(true)
assertThat(service.getNextStep()).isEqualTo(FtueStep.AnalyticsOptIn)
analyticsService.setDidAskUserConsent()
assertThat(service.getNextStep(null)).isNull()
service.ftueStepStateFlow.test {
assertThat(awaitItem()).isEqualTo(InternalFtueState.Unknown)
// Analytics opt in
assertThat(awaitItem()).isEqualTo(InternalFtueState.Incomplete(FtueStep.AnalyticsOptIn))
analyticsService.setDidAskUserConsent()
assertThat(awaitItem()).isEqualTo(InternalFtueState.Complete)
}
}
@Test
@ -180,51 +181,13 @@ class DefaultFtueServiceTest {
sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.Verified)
lockScreenService.setIsPinSetup(true)
assertThat(service.getNextStep()).isEqualTo(FtueStep.AnalyticsOptIn)
analyticsService.setDidAskUserConsent()
assertThat(service.getNextStep(null)).isNull()
}
@Test
fun `reset do the expected actions S`() = runTest {
val resetAnalyticsLambda = lambdaRecorder<Unit> { }
val analyticsService = FakeAnalyticsService(
resetLambda = resetAnalyticsLambda
)
val resetPermissionLambda = lambdaRecorder<String, Unit> { }
val permissionStateProvider = FakePermissionStateProvider(
resetPermissionLambda = resetPermissionLambda
)
val service = createDefaultFtueService(
sdkIntVersion = Build.VERSION_CODES.S,
permissionStateProvider = permissionStateProvider,
analyticsService = analyticsService,
)
service.reset()
resetAnalyticsLambda.assertions().isCalledOnce()
resetPermissionLambda.assertions().isNeverCalled()
}
@Test
fun `reset do the expected actions TIRAMISU`() = runTest {
val resetLambda = lambdaRecorder<Unit> { }
val analyticsService = FakeAnalyticsService(
resetLambda = resetLambda
)
val resetPermissionLambda = lambdaRecorder<String, Unit> { }
val permissionStateProvider = FakePermissionStateProvider(
resetPermissionLambda = resetPermissionLambda
)
val service = createDefaultFtueService(
sdkIntVersion = Build.VERSION_CODES.TIRAMISU,
permissionStateProvider = permissionStateProvider,
analyticsService = analyticsService,
)
service.reset()
resetLambda.assertions().isCalledOnce()
resetPermissionLambda.assertions().isCalledOnce()
.with(value("android.permission.POST_NOTIFICATIONS"))
service.ftueStepStateFlow.test {
assertThat(awaitItem()).isEqualTo(InternalFtueState.Unknown)
// Analytics opt in
assertThat(awaitItem()).isEqualTo(InternalFtueState.Incomplete(FtueStep.AnalyticsOptIn))
analyticsService.setDidAskUserConsent()
assertThat(awaitItem()).isEqualTo(InternalFtueState.Complete)
}
}
}

View file

@ -9,18 +9,11 @@ package io.element.android.features.ftue.test
import io.element.android.features.ftue.api.state.FtueService
import io.element.android.features.ftue.api.state.FtueState
import io.element.android.tests.testutils.lambda.lambdaError
import kotlinx.coroutines.flow.MutableStateFlow
class FakeFtueService(
private val resetLambda: () -> Unit = { lambdaError() },
) : FtueService {
class FakeFtueService : FtueService {
override val state: MutableStateFlow<FtueState> = MutableStateFlow(FtueState.Unknown)
override suspend fun reset() {
resetLambda()
}
suspend fun emitState(newState: FtueState) {
state.emit(newState)
}

View file

@ -13,6 +13,7 @@
<string name="full_screen_intent_banner_message">"Er mwyn sicrhau fyddwch chi ddim yn colli galwad bwysig, newidiwch eich gosodiadau i ganiatáu hysbysiadau sgrin lawn pan fydd eich ffôn wedi\'i gloi."</string>
<string name="full_screen_intent_banner_title">"Gwella profiad eich galwadau"</string>
<string name="screen_home_tab_chats">"Sgyrsiau"</string>
<string name="screen_home_tab_spaces">"Gofodau"</string>
<string name="screen_invites_decline_chat_message">"Ydych chi\'n siŵr eich bod am wrthod y gwahoddiad i ymuno â %1$s?"</string>
<string name="screen_invites_decline_chat_title">"Gwrthod y gwahoddiad"</string>
<string name="screen_invites_decline_direct_chat_message">"Ydych chi\'n siŵr eich bod am wrthod y sgwrs breifat hon gyda %1$s?"</string>
@ -32,6 +33,7 @@ Am y tro, gallwch ddad-ddewis hidlwyr er mwyn gweld eich sgyrsiau eraill"</strin
<string name="screen_roomlist_filter_invites">"Gwahoddiadau"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"Does gennych chi ddim gwahoddiadau yn aros."</string>
<string name="screen_roomlist_filter_low_priority">"Blaenoriaeth Isel"</string>
<string name="screen_roomlist_filter_low_priority_empty_state_title">"Does gennych chi ddim sgyrsiau blaenoriaeth isel eto"</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Gallwch ddad-ddewis hidlwyr er mwyn gweld eich sgyrsiau eraill"</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"Does gennych chi ddim sgyrsiau ar gyfer y dewis hwn"</string>
<string name="screen_roomlist_filter_people">"Pobl"</string>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="banner_set_up_recovery_content">"Restore your account security and message history with a backup password if you have lost all your existing devices."</string>
<string name="banner_set_up_recovery_submit">"Set up backup"</string>
<string name="banner_set_up_recovery_title">"Set up backup to protect your account"</string>
<string name="confirm_recovery_key_banner_message">"Confirm your backup password to maintain access to your message backup and message history."</string>
<string name="confirm_recovery_key_banner_primary_button_title">"Enter your backup password"</string>
<string name="confirm_recovery_key_banner_secondary_button_title">"Forgot your backup password?"</string>
<string name="confirm_recovery_key_banner_title">"Your message backup is out of sync"</string>
<string name="session_verification_banner_message">"Looks like you\'re using a new device. Confirm it with another connected device to access your encrypted messages."</string>
</resources>

View file

@ -33,6 +33,7 @@ Toistaiseksi voit poistaa suodattimien valinnan, jotta näet muut keskustelut."<
<string name="screen_roomlist_filter_invites">"Kutsut"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"Sinulla ei ole yhtään odottavaa kutsua."</string>
<string name="screen_roomlist_filter_low_priority">"Matala prioriteetti"</string>
<string name="screen_roomlist_filter_low_priority_empty_state_title">"Sinulla ei ole vielä yhtään matalan prioriteetin keskustelua"</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Voit poistaa suodattimien valinnan nähdäksesi muut keskustelusi."</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"Sinulla ei ole sopivia keskusteluja tähän valintaan"</string>
<string name="screen_roomlist_filter_people">"Ihmiset"</string>

View file

@ -13,6 +13,7 @@
<string name="full_screen_intent_banner_message">"For å sikre at du aldri går glipp av en viktig samtale, må du endre innstillingene dine for å tillate fullskjermvarsler når telefonen er låst."</string>
<string name="full_screen_intent_banner_title">"Forbedre samtaleopplevelsen din"</string>
<string name="screen_home_tab_chats">"Chatter"</string>
<string name="screen_home_tab_spaces">"Områder"</string>
<string name="screen_invites_decline_chat_message">"Er du sikker på at du vil takke nei til invitasjonen til å bli med i %1$s?"</string>
<string name="screen_invites_decline_chat_title">"Avvis invitasjon"</string>
<string name="screen_invites_decline_direct_chat_message">"Er du sikker på at du vil avslå denne private chatten med %1$s?"</string>

View file

@ -33,6 +33,7 @@
<string name="screen_roomlist_filter_invites">"邀請"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"您沒有任何擱置中的邀請。"</string>
<string name="screen_roomlist_filter_low_priority">"低優先度"</string>
<string name="screen_roomlist_filter_low_priority_empty_state_title">"您尚無任何低優先程度聊天"</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"您可以取消選取篩選條件以檢視其他聊天"</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"您並無此選擇的聊天"</string>
<string name="screen_roomlist_filter_people">"夥伴"</string>

View file

@ -18,6 +18,7 @@
<string name="screen_join_room_join_action">"Ymuno â\'r ystafell"</string>
<string name="screen_join_room_join_restricted_message">"Efallai y bydd angen i chi gael eich gwahodd neu fod yn aelod o ofod er mwyn ymuno."</string>
<string name="screen_join_room_knock_action">"Anfon cais i ymuno"</string>
<string name="screen_join_room_knock_message_characters_count">"Nodau a ganiateir %1$d o %2$d"</string>
<string name="screen_join_room_knock_message_description">"Neges (dewisol)"</string>
<string name="screen_join_room_knock_sent_description">"Byddwch yn derbyn gwahoddiad i ymuno â\'r ystafell os caiff eich cais ei dderbyn."</string>
<string name="screen_join_room_knock_sent_title">"Anfonwyd y cais i ymuno"</string>

View file

@ -3,5 +3,8 @@
<string name="leave_conversation_alert_subtitle">"Ydych chi\'n siŵr eich bod am adael y sgwrs hon? Dyw\'r sgwrs hon ddim yn gyhoeddus a fyddwch chi ddim yn gallu ailymuno heb wahoddiad."</string>
<string name="leave_room_alert_empty_subtitle">"Ydych chi\'n siŵr eich bod am adael yr ystafell hon? Chi yw\'r unig berson yma. Os byddwch yn gadael, fydd neb yn gallu ymuno yn y dyfodol, gan gynnwys chi."</string>
<string name="leave_room_alert_private_subtitle">"Ydych chi\'n siŵr eich bod am adael yr ystafell hon? Dyw\'r ystafell hon ddim yn gyhoeddus a fyddwch chi ddim yn gallu ailymuno heb wahoddiad."</string>
<string name="leave_room_alert_select_new_owner_action">"Dewiswch Berchnogion"</string>
<string name="leave_room_alert_select_new_owner_subtitle">"Chi yw unig berchennog yr ystafell hon. Mae angen i chi drosglwyddo perchnogaeth i rywun arall cyn i chi adael yr room."</string>
<string name="leave_room_alert_select_new_owner_title">"Trosglwyddo perchnogaeth"</string>
<string name="leave_room_alert_subtitle">"Ydych chi\'n siŵr eich bod am adael yr ystafell?"</string>
</resources>

View file

@ -9,7 +9,7 @@
<string name="screen_app_lock_settings_enable_biometric_unlock">"Salli biometrinen tunnistus"</string>
<string name="screen_app_lock_settings_remove_pin">"Poista PIN-koodi"</string>
<string name="screen_app_lock_settings_remove_pin_alert_message">"Haluatko varmasti poistaa PIN-koodin?"</string>
<string name="screen_app_lock_settings_remove_pin_alert_title">"Poista PIN-koodi?"</string>
<string name="screen_app_lock_settings_remove_pin_alert_title">"Poistetaanko PIN-koodi?"</string>
<string name="screen_app_lock_setup_biometric_unlock_allow_title">"Salli %1$s"</string>
<string name="screen_app_lock_setup_biometric_unlock_skip">"Käytän mieluummin PIN-koodia"</string>
<string name="screen_app_lock_setup_biometric_unlock_subtitle">"Säästä aikaa ja ota käyttöön %1$s"</string>

View file

@ -13,6 +13,9 @@
<string name="screen_change_account_provider_other">"Arall"</string>
<string name="screen_change_account_provider_subtitle">"Defnyddiwch ddarparwr cyfrif gwahanol, fel eich gweinydd preifat eich hun neu gyfrif gwaith."</string>
<string name="screen_change_account_provider_title">"Newid darparwr cyfrif"</string>
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
<string name="screen_change_server_error_element_pro_required_message">"Mae angen yr ap Element Pro ar %1$s. Llwythwch ef o\'r siop."</string>
<string name="screen_change_server_error_element_pro_required_title">"Mae angen Element Pro"</string>
<string name="screen_change_server_error_invalid_homeserver">"Doedd dim modd i ni gyrraedd y gweinydd cartref hwn. Gwiriwch eich bod wedi rhoi URL y gweinydd cartref yn gywir. Os yw\'r URL yn gywir, cysylltwch â gweinyddwr eich gweinydd cartref am ragor o help."</string>
<string name="screen_change_server_error_invalid_well_known">"Dyw cydweddu llithrig ddim ar gael oherwydd problem yn y ffeil .well-known:
%1$s"</string>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_qr_code_login_connection_note_secure_state_description">"A secure connection could not be made to the new device. Your existing connected devices are still safe and you don\'t need to worry about them."</string>
</resources>

View file

@ -13,6 +13,7 @@
<string name="screen_change_account_provider_other">"Annet"</string>
<string name="screen_change_account_provider_subtitle">"Bruk en annen kontotilbyder, for eksempel din egen private server eller en arbeidskonto."</string>
<string name="screen_change_account_provider_title">"Bytt kontotilbyder"</string>
<string name="screen_change_server_error_element_pro_required_action_android">"Google Play"</string>
<string name="screen_change_server_error_element_pro_required_message">"Element Pro-appen er nødvendig på %1$s. Last den ned fra butikken."</string>
<string name="screen_change_server_error_element_pro_required_title">"Element Pro kreves"</string>
<string name="screen_change_server_error_invalid_homeserver">"Vi kunne ikke nå denne hjemmeserveren. Kontroller at du har skrevet inn hjemmeserverens URL riktig. Hvis URL-en er riktig, kontakt administratoren for hjemmeserveren din for å få mer hjelp."</string>

View file

@ -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_key_backup_offline_subtitle">"Your messages were still being backed up when you went offline. Reconnect so that your messages can be backed up before signing out."</string>
<string name="screen_signout_key_backup_offline_title">"Your messages are still being backed up"</string>
<string name="screen_signout_key_backup_ongoing_title">"Your messages are still being backed up"</string>
<string name="screen_signout_recovery_disabled_title">"Backup not set up"</string>
<string name="screen_signout_save_recovery_key_title">"Have you saved your backup password?"</string>
</resources>

View file

@ -87,6 +87,7 @@ import io.element.android.libraries.designsystem.components.ExpandableBottomShee
import io.element.android.libraries.designsystem.components.ExpandableBottomSheetLayoutState
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
@ -95,6 +96,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.toAnnotatedString
import io.element.android.libraries.designsystem.theme.components.BottomSheetDragHandle
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
@ -111,10 +113,14 @@ import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.textcomposer.model.TextEditorState
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.wysiwyg.link.Link
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import timber.log.Timber
import kotlin.time.Duration.Companion.milliseconds
@ -202,7 +208,13 @@ fun MessagesView(
Column {
ConnectivityIndicatorView(isOnline = state.hasNetworkConnection)
if (state.timelineState.timelineMode is Timeline.Mode.Thread) {
ThreadTopBar(onBackClick = onBackClick)
ThreadTopBar(
roomName = state.roomName,
roomAvatarData = state.roomAvatar,
heroes = state.heroes,
isTombstoned = state.isTombstoned,
onBackClick = onBackClick,
)
} else {
MessagesViewTopBar(
roomName = state.roomName,
@ -573,14 +585,48 @@ private fun MessagesViewTopBar(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ThreadTopBar(
roomName: String?,
roomAvatarData: AvatarData,
heroes: ImmutableList<AvatarData>,
isTombstoned: Boolean,
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
) {
TopAppBar(
modifier = modifier,
navigationIcon = {
BackButton(onClick = onBackClick)
},
title = {
Text(stringResource(CommonStrings.common_thread))
Row(verticalAlignment = Alignment.CenterVertically) {
Avatar(
avatarData = roomAvatarData,
avatarType = AvatarType.Room(
heroes = heroes,
isTombstoned = isTombstoned,
),
)
Column(
modifier = Modifier.fillMaxWidth()
.padding(horizontal = 8.dp)
.semantics {
heading()
},
) {
Text(
text = stringResource(CommonStrings.common_thread),
style = ElementTheme.typography.fontBodyLgMedium,
)
Text(
text = roomName ?: stringResource(CommonStrings.common_no_room_name),
style = ElementTheme.typography.fontBodySmRegular,
fontStyle = FontStyle.Italic.takeIf { roomName == null },
color = ElementTheme.colors.textSecondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
}
)
}
@ -673,3 +719,58 @@ internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class)
knockRequestsBannerView = {},
)
}
@PreviewsDayNight
@Composable
internal fun ThreadTopBarPreview() {
ElementPreview {
val name = "Room name"
val initialsAvatarData = AvatarData(
id = "id",
name = name,
url = null,
size = AvatarSize.TimelineRoom,
)
Column {
ThreadTopBar(
roomName = name,
roomAvatarData = initialsAvatarData,
heroes = persistentListOf(),
isTombstoned = false,
onBackClick = {},
)
HorizontalDivider()
ThreadTopBar(
roomName = name,
roomAvatarData = initialsAvatarData,
heroes = aMatrixUserList().map { it.getAvatarData(AvatarSize.TimelineRoom) }.toImmutableList(),
isTombstoned = false,
onBackClick = {},
)
HorizontalDivider()
ThreadTopBar(
roomName = null,
roomAvatarData = initialsAvatarData,
heroes = persistentListOf(),
isTombstoned = false,
onBackClick = {},
)
HorizontalDivider()
ThreadTopBar(
roomName = name,
roomAvatarData = initialsAvatarData.copy(url = "https://some-avatar.jpg"),
heroes = persistentListOf(),
isTombstoned = false,
onBackClick = {},
)
HorizontalDivider()
ThreadTopBar(
roomName = name,
roomAvatarData = initialsAvatarData,
heroes = persistentListOf(),
isTombstoned = true,
onBackClick = {},
)
}
}
}

View file

@ -7,6 +7,7 @@
<string name="emoji_picker_category_objects">"Předměty"</string>
<string name="emoji_picker_category_people">"Smajlíci a lidé"</string>
<string name="emoji_picker_category_places">"Cestování a místa"</string>
<string name="emoji_picker_category_recent">"Nedávné emotikony"</string>
<string name="emoji_picker_category_symbols">"Symboly"</string>
<string name="screen_media_upload_preview_caption_warning">"Titulky nemusí být viditelné pro lidi, kteří používají starší aplikace."</string>
<string name="screen_media_upload_preview_change_video_quality_prompt">"Klepnutím změníte kvalitu nahrávání videa"</string>
@ -15,6 +16,7 @@
<string name="screen_media_upload_preview_error_failed_sending">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>
<string name="screen_media_upload_preview_error_too_large_message">"Maximální povolená velikost souboru je %1$s."</string>
<string name="screen_media_upload_preview_error_too_large_title">"Soubor je pro nahrání příliš velký."</string>
<string name="screen_media_upload_preview_item_count">"Položka %1$d z %2$d"</string>
<string name="screen_media_upload_preview_optimize_image_quality_title">"Optimalizace kvality obrazu"</string>
<string name="screen_media_upload_preview_processing">"Probíhá zpracování…"</string>
<string name="screen_report_content_block_user">"Zablokovat uživatele"</string>

View file

@ -7,10 +7,18 @@
<string name="emoji_picker_category_objects">"Gwrthrychau"</string>
<string name="emoji_picker_category_people">"Wynebau Hapus a Phobl"</string>
<string name="emoji_picker_category_places">"Teithio a Llefydd"</string>
<string name="emoji_picker_category_recent">"Emojis diweddar"</string>
<string name="emoji_picker_category_symbols">"Symbolau"</string>
<string name="screen_media_upload_preview_caption_warning">"Efallai na fydd capsiynau yn weladwy i bobl sy\'n defnyddio apiau hŷn."</string>
<string name="screen_media_upload_preview_change_video_quality_prompt">"Tapiwch i newid ansawdd llwytho\'r fideo"</string>
<string name="screen_media_upload_preview_error_could_not_be_uploaded">"Nid oedd modd llwytho\'r ffeil."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Wedi methu â phrosesu cyfryngau i\'w llwytho, ceisiwch eto."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Wedi methu llwytho cyfryngau, ceisiwch eto."</string>
<string name="screen_media_upload_preview_error_too_large_message">"Y maint ffeil mwyaf a ganiateir yw %1$s ."</string>
<string name="screen_media_upload_preview_error_too_large_title">"Mae\'r ffeil yn rhy fawr i\'w llwytho"</string>
<string name="screen_media_upload_preview_item_count">"Eitem %1$d o %2$d"</string>
<string name="screen_media_upload_preview_optimize_image_quality_title">"Optimeiddio ansawdd delwedd"</string>
<string name="screen_media_upload_preview_processing">"Prosesu…"</string>
<string name="screen_report_content_block_user">"Rhwystro defnyddiwr"</string>
<string name="screen_report_content_block_user_hint">"Gwiriwch a ydych am guddio\'r holl negeseuon presennol ac yn y dyfodol gan y defnyddiwr hwn"</string>
<string name="screen_report_content_explanation">"Bydd y neges hon yn cael ei hadrodd i weinyddwr eich gweinyddwr cartref. Fyddan nhw ddim yn gallu darllen unrhyw negeseuon wedi\'u hamgryptio."</string>
@ -38,6 +46,14 @@
<string name="screen_room_timeline_less_reactions">"Dangos llai"</string>
<string name="screen_room_timeline_message_copied">"Neges wedi\'i chopïo"</string>
<string name="screen_room_timeline_no_permission_to_post">"Does gennych chi ddim caniatâd i bostio i\'r ystafell hon"</string>
<plurals name="screen_room_timeline_reaction_a11y">
<item quantity="zero">"Ymatebodd %1$d aelodau gyda %2$s"</item>
<item quantity="one">"Ymatebodd %1$d aelodau gyda %2$s"</item>
<item quantity="two">"Ymatebodd %1$d aelod gyda %2$s"</item>
<item quantity="few">"Ymatebodd %1$d aelod gyda %2$s"</item>
<item quantity="many">"Ymatebodd %1$d aelod gyda %2$s"</item>
<item quantity="other">"Ymatebodd %1$d aelod gyda %2$s"</item>
</plurals>
<string name="screen_room_timeline_reaction_you_a11y">"Rydych chi wedi ymateb gyda %1$s"</string>
<string name="screen_room_timeline_reactions_show_less">"Dangos llai"</string>
<string name="screen_room_timeline_reactions_show_more">"Dangos rhagor"</string>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_encrypted_history_banner_unverified">"Message history is unavailable in this room. Confirm this device to see your message history."</string>
</resources>

View file

@ -7,6 +7,7 @@
<string name="emoji_picker_category_objects">"Esineet"</string>
<string name="emoji_picker_category_people">"Hymiöt ja ihmiset"</string>
<string name="emoji_picker_category_places">"Matkustaminen ja paikat"</string>
<string name="emoji_picker_category_recent">"Viimeaikaiset emojit"</string>
<string name="emoji_picker_category_symbols">"Symbolit"</string>
<string name="screen_media_upload_preview_caption_warning">"Kuvatekstit eivät välttämättä näy ihmisille, jotka käyttävät vanhempia sovelluksia."</string>
<string name="screen_media_upload_preview_change_video_quality_prompt">"Napauta muuttaaksesi videon lähetyslaatua"</string>

View file

@ -9,11 +9,14 @@
<string name="emoji_picker_category_places">"旅行與景點"</string>
<string name="emoji_picker_category_symbols">"標誌"</string>
<string name="screen_media_upload_preview_caption_warning">"使用舊應用程式的使用者可能看不到標題。"</string>
<string name="screen_media_upload_preview_change_video_quality_prompt">"輕點即可變更影片上傳品質"</string>
<string name="screen_media_upload_preview_error_could_not_be_uploaded">"無法上傳檔案。"</string>
<string name="screen_media_upload_preview_error_failed_processing">"無法處理要上傳的媒體,請再試一次。"</string>
<string name="screen_media_upload_preview_error_failed_sending">"無法上傳媒體檔案,請稍後再試。"</string>
<string name="screen_media_upload_preview_error_too_large_message">"允許的最大檔案大小為 %1$s。"</string>
<string name="screen_media_upload_preview_error_too_large_title">"檔案太大,無法上傳"</string>
<string name="screen_media_upload_preview_optimize_image_quality_title">"最佳化影像品質"</string>
<string name="screen_media_upload_preview_processing">"正在處理……"</string>
<string name="screen_report_content_block_user">"封鎖使用者"</string>
<string name="screen_report_content_block_user_hint">"檢查您是否要隱藏所有來自此使用者的目前及未來的訊息"</string>
<string name="screen_report_content_explanation">"此訊息將會回報給您的家伺服器管理員。他們將無法讀取任何已加密的訊息。"</string>

View file

@ -7,6 +7,7 @@
<string name="emoji_picker_category_objects">"Objects"</string>
<string name="emoji_picker_category_people">"Smileys &amp; People"</string>
<string name="emoji_picker_category_places">"Travel &amp; Places"</string>
<string name="emoji_picker_category_recent">"Recent emojis"</string>
<string name="emoji_picker_category_symbols">"Symbols"</string>
<string name="screen_media_upload_preview_caption_warning">"Captions might not be visible to people using older apps."</string>
<string name="screen_media_upload_preview_change_video_quality_prompt">"Tap to change the video upload quality"</string>
@ -15,6 +16,7 @@
<string name="screen_media_upload_preview_error_failed_sending">"Failed uploading media, please try again."</string>
<string name="screen_media_upload_preview_error_too_large_message">"The maximum file size allowed is %1$s."</string>
<string name="screen_media_upload_preview_error_too_large_title">"The file is too large to upload"</string>
<string name="screen_media_upload_preview_item_count">"Item %1$d of %2$d"</string>
<string name="screen_media_upload_preview_optimize_image_quality_title">"Optimise image quality"</string>
<string name="screen_media_upload_preview_processing">"Processing…"</string>
<string name="screen_report_content_block_user">"Block user"</string>

View file

@ -1,5 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<plurals name="a11y_polls_percent_of_total">
<item quantity="zero">"%1$d y cant o\'r holl bleidleisiau"</item>
<item quantity="one">"%1$d y cant o\'r holl bleidleisiau"</item>
<item quantity="two">"%1$d y cant o\'r holl bleidleisiau"</item>
<item quantity="few">"%1$d y cant o\'r holl bleidleisiau"</item>
<item quantity="many">"%1$d y cant o\'r holl bleidleisiau"</item>
<item quantity="other">"%1$d y cant o\'r holl bleidleisiau"</item>
</plurals>
<string name="a11y_polls_will_remove_selection">"Bydd yn dileu\'r dewis blaenorol"</string>
<string name="a11y_polls_winning_answer">"Dyma\'r ateb buddugol"</string>
</resources>

View file

@ -72,7 +72,6 @@ dependencies {
implementation(projects.features.rageshake.api)
implementation(projects.features.lockscreen.api)
implementation(projects.features.analytics.api)
implementation(projects.features.ftue.api)
implementation(projects.features.licenses.api)
implementation(projects.features.logout.api)
implementation(projects.features.deactivation.api)
@ -101,7 +100,6 @@ dependencies {
testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.push.test)
testImplementation(projects.libraries.pushstore.test)
testImplementation(projects.features.ftue.test)
testImplementation(projects.features.invite.test)
testImplementation(projects.features.rageshake.test)
testImplementation(projects.features.logout.test)

View file

@ -208,6 +208,10 @@ class PreferencesFlowNode(
navigateUp()
}
}
override fun openIgnoredUsers() {
backstack.push(NavTarget.BlockedUsers)
}
})
.build()
}

View file

@ -12,7 +12,6 @@ import coil3.SingletonImageLoader
import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.Provider
import io.element.android.features.ftue.api.state.FtueService
import io.element.android.features.invite.api.SeenInvitesStore
import io.element.android.features.preferences.impl.DefaultCacheService
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
@ -36,7 +35,6 @@ class DefaultClearCacheUseCase(
private val coroutineDispatchers: CoroutineDispatchers,
private val defaultCacheService: DefaultCacheService,
private val okHttpClient: Provider<OkHttpClient>,
private val ftueService: FtueService,
private val pushService: PushService,
private val seenInvitesStore: SeenInvitesStore,
private val activeRoomsHolder: ActiveRoomsHolder,
@ -56,7 +54,6 @@ class DefaultClearCacheUseCase(
// Clear app cache
context.cacheDir.deleteRecursively()
// Clear some settings
ftueService.reset()
seenInvitesStore.clear()
// Ensure any error will be displayed again
pushService.setIgnoreRegistrationError(matrixClient.sessionId, false)

View file

@ -13,6 +13,13 @@
<string name="screen_advanced_settings_media_compression_description">"Llwythwch i fyny lluniau a fideos yn gynt a lleihau\'r defnydd o ddata"</string>
<string name="screen_advanced_settings_media_compression_title">"Optimeiddio ansawdd y cyfryngau"</string>
<string name="screen_advanced_settings_moderation_and_safety_section_title">"Cymedroli a Diogelwch"</string>
<string name="screen_advanced_settings_optimise_image_upload_quality_description">"Optimeiddio delweddau\'n awtomatig ar gyfer llwytho cyflymach a meintiau ffeiliau llai."</string>
<string name="screen_advanced_settings_optimise_image_upload_quality_title">"Optimeiddio ansawdd llwytho delweddau"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_description">"%1$s Tapiwch yma i newid."</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_high">"Uchel (1080p)"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_low">"Isel (480c)"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_standard">"Safonol (720p)"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_title">"Ansawdd lwytho fideo"</string>
<string name="screen_advanced_settings_push_provider_android">"Darparwr hysbysiad gwthio"</string>
<string name="screen_advanced_settings_rich_text_editor_description">"Analluogi\'r golygydd testun cyfoethog i deipio Markdown â llaw."</string>
<string name="screen_advanced_settings_send_read_receipts">"Derbynebau darllen"</string>

View file

@ -16,6 +16,9 @@
<string name="screen_advanced_settings_optimise_image_upload_quality_description">"Optimoi kuvat automaattisesti nopeampia lähetysnopeuksia ja pienempiä tiedostokokoja varten."</string>
<string name="screen_advanced_settings_optimise_image_upload_quality_title">"Optimoi kuvien lähetyslaatu"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_description">"%1$s. Napauta tästä vaihtaaksesi."</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_high">"Korkea (1080p)"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_low">"Matala (480p)"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_standard">"Normaali (720p)"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_title">"Videon lähetyslaatu"</string>
<string name="screen_advanced_settings_push_provider_android">"Push-ilmoitusten tarjoaja"</string>
<string name="screen_advanced_settings_rich_text_editor_description">"Ota rikastettu tekstieditori pois käytöstä, jotta voit kirjoittaa Markdownia manuaalisesti."</string>

View file

@ -13,6 +13,13 @@
<string name="screen_advanced_settings_media_compression_description">"上傳照片與影片更快且減少資料使用量"</string>
<string name="screen_advanced_settings_media_compression_title">"最佳化媒體品質"</string>
<string name="screen_advanced_settings_moderation_and_safety_section_title">"管理與安全"</string>
<string name="screen_advanced_settings_optimise_image_upload_quality_description">"自動最佳化影像以提供更快的上傳速度與較小的檔案大小。"</string>
<string name="screen_advanced_settings_optimise_image_upload_quality_title">"最佳化影像上傳品質"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_description">"%1$s。輕點此處以變更。"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_high">"高 (1080p)"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_low">"低 (480p)"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_standard">"標準 (720p)"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_title">"視訊上傳品質"</string>
<string name="screen_advanced_settings_push_provider_android">"推播通知提供者"</string>
<string name="screen_advanced_settings_rich_text_editor_description">"手動輸入 Markdown停用格式化文字編輯器。"</string>
<string name="screen_advanced_settings_send_read_receipts">"已讀回條"</string>

View file

@ -10,7 +10,6 @@ package io.element.android.features.preferences.impl.tasks
import androidx.test.platform.app.InstrumentationRegistry
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.ftue.test.FakeFtueService
import io.element.android.features.invite.test.InMemorySeenInvitesStore
import io.element.android.features.preferences.impl.DefaultCacheService
import io.element.android.libraries.matrix.api.core.SessionId
@ -41,10 +40,6 @@ class DefaultClearCacheUseCaseTest {
clearCacheLambda = clearCacheLambda,
)
val defaultCacheService = DefaultCacheService()
val resetFtueLambda = lambdaRecorder<Unit> { }
val ftueService = FakeFtueService(
resetLambda = resetFtueLambda,
)
val setIgnoreRegistrationErrorLambda = lambdaRecorder<SessionId, Boolean, Unit> { _, _ -> }
val resetBatteryOptimizationStateResult = lambdaRecorder<Unit> { }
val pushService = FakePushService(
@ -59,7 +54,6 @@ class DefaultClearCacheUseCaseTest {
coroutineDispatchers = testCoroutineDispatchers(),
defaultCacheService = defaultCacheService,
okHttpClient = { OkHttpClient.Builder().build() },
ftueService = ftueService,
pushService = pushService,
seenInvitesStore = seenInvitesStore,
activeRoomsHolder = activeRoomsHolder,
@ -67,7 +61,6 @@ class DefaultClearCacheUseCaseTest {
defaultCacheService.clearedCacheEventFlow.test {
sut.invoke()
clearCacheLambda.assertions().isCalledOnce()
resetFtueLambda.assertions().isCalledOnce()
setIgnoreRegistrationErrorLambda.assertions().isCalledOnce()
.with(value(matrixClient.sessionId), value(false))
resetBatteryOptimizationStateResult.assertions().isCalledOnce()

View file

@ -50,6 +50,8 @@
<string name="screen_room_details_error_loading_notification_settings">"Při načítání nastavení oznámení došlo k chybě."</string>
<string name="screen_room_details_error_muting">"Ztišení této místnosti se nezdařilo, zkuste to prosím znovu."</string>
<string name="screen_room_details_error_unmuting">"Nepodařilo se zrušit ztišení této místnosti, zkuste to prosím znovu."</string>
<string name="screen_room_details_invite_people_dont_close">"Nezavírejte aplikaci, dokud neskončíte."</string>
<string name="screen_room_details_invite_people_preparing">"Příprava pozvánek…"</string>
<string name="screen_room_details_invite_people_title">"Pozvat přátele"</string>
<string name="screen_room_details_leave_conversation_title">"Opustit konverzaci"</string>
<string name="screen_room_details_leave_room_title">"Opustit místnost"</string>

View file

@ -7,7 +7,7 @@
<string name="screen_polls_history_title">"Pleidleisiau"</string>
<string name="screen_room_change_permissions_administrators">"Gweinyddwyr yn unig"</string>
<string name="screen_room_change_permissions_ban_people">"Gwahardd pobl"</string>
<string name="screen_room_change_permissions_delete_messages">"Dileu negeseuon"</string>
<string name="screen_room_change_permissions_delete_messages">"Tynnu negeseuon"</string>
<string name="screen_room_change_permissions_everyone">"Pawb"</string>
<string name="screen_room_change_permissions_invite_people">"Gwahodd pobl a derbyn ceisiadau i ymuno"</string>
<string name="screen_room_change_permissions_member_moderation">"Cymedroli aelodau"</string>
@ -22,13 +22,17 @@
<string name="screen_room_change_role_administrators_title">"Golygu Gweinyddwyr"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Fyddwch chi ddim yn gallu dadwneud y weithred hon. Rydych chi\'n hyrwyddo\'r defnyddiwr i gael yr un lefel pŵer â chi."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Ychwanegu Gweinyddwr?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Fyddwch chi ddim yn gallu dadwneud y weithred hon. Rydych yn trosglwyddo\'r berchnogaeth i\'r defnyddwyr a ddewiswyd. Unwaith y byddwch yn gadael bydd hyn yn barhaol."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Trosglwyddo perchnogaeth?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Gostwng"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Fyddwch chi ddim yn gallu dadwneud y newid hwn gan eich bod yn israddio eich hun, os mai chi yw\'r defnyddiwr breintiedig olaf yn yr ystafell bydd yn amhosibl adennill breintiau."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Israddio eich hun?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Yn aros)"</string>
<string name="screen_room_change_role_invited_member_name_android">"Yn aros"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Mae gan weinyddwyr freintiau cymedrolwr yn awtomatig"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Mae gan berchnogion freintiau gweinyddwr yn awtomatig."</string>
<string name="screen_room_change_role_moderators_title">"Golygu Cymedrolwyr"</string>
<string name="screen_room_change_role_owners_title">"Dewiswch Berchnogion"</string>
<string name="screen_room_change_role_section_administrators">"Gweinyddwyr"</string>
<string name="screen_room_change_role_section_moderators">"Cymedrolwyr"</string>
<string name="screen_room_change_role_section_users">"Aelodau"</string>
@ -46,6 +50,8 @@
<string name="screen_room_details_error_loading_notification_settings">"Digwyddodd gwall wrth lwytho gosodiadau hysbysu."</string>
<string name="screen_room_details_error_muting">"Wedi methu tewi\'r ystafell hon, ceisiwch eto."</string>
<string name="screen_room_details_error_unmuting">"Wedi methu dad-dewi\'r ystafell hon, ceisiwch eto."</string>
<string name="screen_room_details_invite_people_dont_close">"Peidiwch â chau\'r ap nes ei fod wedi gorffen."</string>
<string name="screen_room_details_invite_people_preparing">"Wrthi\'n paratoi gwahoddiadau…"</string>
<string name="screen_room_details_invite_people_title">"Gwahodd pobl"</string>
<string name="screen_room_details_leave_conversation_title">"Gadael y sgwrs"</string>
<string name="screen_room_details_leave_room_title">"Gadael yr ystafell"</string>
@ -83,6 +89,7 @@
<string name="screen_room_member_list_pending_header_title">"Dan ystyriaeth"</string>
<string name="screen_room_member_list_role_administrator">"Gweinyddwr"</string>
<string name="screen_room_member_list_role_moderator">"Cymedrolwr"</string>
<string name="screen_room_member_list_role_owner">"Perchennog"</string>
<string name="screen_room_member_list_room_members_header_title">"Aelodau\'r ystafell"</string>
<string name="screen_room_member_list_unbanning_user">"Dad-wahardd %1$s"</string>
<string name="screen_room_notification_settings_allow_custom">"Caniatáu gosodiad personol"</string>
@ -100,12 +107,14 @@
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Crybwylliadau ac Allweddeiriau\'n unig"</string>
<string name="screen_room_notification_settings_room_custom_settings_title">"Yn yr ystafell hon, rhowch wybod i mi am"</string>
<string name="screen_room_roles_and_permissions_admins">"Gweinyddwyr"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Gweinyddwyr a pherchnogion"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Newid fy rôl"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Israddio aelod"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Israddio cymedrolwr"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Cymedroli aelodau"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Negeseuon a chynnwys"</string>
<string name="screen_room_roles_and_permissions_moderators">"Cymedrolwyr"</string>
<string name="screen_room_roles_and_permissions_owners">"Perchnogion"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Caniatâd"</string>
<string name="screen_room_roles_and_permissions_reset">"Ailosod caniatâd"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Ar ôl i chi ailosod caniatâd, byddwch yn colli\'r gosodiadau cyfredol."</string>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_encryption_enabled_subtitle">"Messages are secured with locks. Only you and the recipients can unlock them."</string>
</resources>

View file

@ -21,12 +21,12 @@
<string name="screen_room_change_permissions_send_messages">"Viestien lähettäminen"</string>
<string name="screen_room_change_role_administrators_title">"Muokkaa ylläpitäjiä"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Et voi peruuttaa tätä toimenpidettä. Ylennät käyttäjän samalle oikeustasolle kuin sinä."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Lisää ylläpitäjä?"</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Lisätäänkö ylläpitäjä?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Et voi kumota tätä toimintoa. Olet siirtämässä omistajuuden valituille käyttäjille. Kun poistut, muutos on pysyvä."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Siirretäänkö omistajuus?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Alenna"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Et voi perua tätä muutosta, koska olet alentamassa itseäsi. Jos olet viimeinen oikeutettu henkilö tässä huoneessa, oikeuksia ei voi enää saada takaisin."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Alenna itsesi?"</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Haluatko alentaa itsesi?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Kutsuttu)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Kutsuttu)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Ylläpitäjillä on automaattisesti valvojan oikeudet"</string>
@ -37,7 +37,7 @@
<string name="screen_room_change_role_section_moderators">"Valvojat"</string>
<string name="screen_room_change_role_section_users">"Jäsenet"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Sinulla on tallentamattomia muutoksia"</string>
<string name="screen_room_change_role_unsaved_changes_title">"Tallenna muutokset?"</string>
<string name="screen_room_change_role_unsaved_changes_title">"Tallennetaanko muutokset?"</string>
<string name="screen_room_details_add_topic_title">"Lisää aihe"</string>
<string name="screen_room_details_badge_encrypted">"Salattu"</string>
<string name="screen_room_details_badge_not_encrypted">"Ei salattu"</string>
@ -50,6 +50,8 @@
<string name="screen_room_details_error_loading_notification_settings">"Ilmoitusasetuksia ladattaessa tapahtui virhe."</string>
<string name="screen_room_details_error_muting">"Tämän huoneen mykistäminen epäonnistui, yritä uudelleen."</string>
<string name="screen_room_details_error_unmuting">"Tämän huoneen mykistyksen poistaminen epäonnistui, yritä uudelleen."</string>
<string name="screen_room_details_invite_people_dont_close">"Älä sulje sovellusta ennen kuin se on valmis."</string>
<string name="screen_room_details_invite_people_preparing">"Valmistellaan kutsuja…"</string>
<string name="screen_room_details_invite_people_title">"Kutsu ihmisiä"</string>
<string name="screen_room_details_leave_conversation_title">"Poistu keskustelusta"</string>
<string name="screen_room_details_leave_room_title">"Poistu huoneesta"</string>

View file

@ -50,6 +50,7 @@
<string name="screen_room_details_error_loading_notification_settings">"Une erreur sest produite lors du chargement des paramètres de notification."</string>
<string name="screen_room_details_error_muting">"Échec de la mise en sourdine de ce salon, veuillez réessayer."</string>
<string name="screen_room_details_error_unmuting">"Échec de la désactivation de la mise en sourdine de ce salon, veuillez réessayer."</string>
<string name="screen_room_details_invite_people_preparing">"Préparation des invitations…"</string>
<string name="screen_room_details_invite_people_title">"Inviter des amis"</string>
<string name="screen_room_details_leave_conversation_title">"Quitter la discussion"</string>
<string name="screen_room_details_leave_room_title">"Quitter le salon"</string>

View file

@ -50,6 +50,8 @@
<string name="screen_room_details_error_loading_notification_settings">"載入通知設定時發生錯誤。"</string>
<string name="screen_room_details_error_muting">"無法關閉聊天室通知,請再試一次。"</string>
<string name="screen_room_details_error_unmuting">"無法開啟聊天室通知,請再試一次。"</string>
<string name="screen_room_details_invite_people_dont_close">"完成前請勿關閉應用程式。"</string>
<string name="screen_room_details_invite_people_preparing">"正在準備邀請……"</string>
<string name="screen_room_details_invite_people_title">"邀請夥伴"</string>
<string name="screen_room_details_leave_conversation_title">"離開對話"</string>
<string name="screen_room_details_leave_room_title">"離開聊天室"</string>

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_chat_backup_key_backup_action_disable">"Delete message backup"</string>
<string name="screen_chat_backup_key_backup_description">"Store your account security and messages securely on the server. This will allow you to view your message history on any new devices. %1$s."</string>
<string name="screen_chat_backup_key_backup_title">"Message backup"</string>
<string name="screen_chat_backup_key_storage_disabled_error">"Turn on message backup to set it up."</string>
<string name="screen_chat_backup_key_storage_toggle_description">"Upload messages from this device"</string>
<string name="screen_chat_backup_key_storage_toggle_title">"Allow message backup"</string>
<string name="screen_chat_backup_recovery_action_change">"Change backup password"</string>
<string name="screen_chat_backup_recovery_action_change_description">"Restore your account security and message history with a backup password if you\'ve lost all your existing devices."</string>
<string name="screen_chat_backup_recovery_action_confirm">"Enter backup password"</string>
<string name="screen_chat_backup_recovery_action_confirm_description">"Your message backup is currently out of sync."</string>
<string name="screen_chat_backup_recovery_action_setup">"Set up backup"</string>
<string name="screen_create_new_recovery_key_list_item_3">"When asked to confirm your device, select %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Follow the instructions to create a new backup password"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Save your new backup password in a password manager or encrypted note"</string>
<string name="screen_encryption_reset_bullet_3">"You will need to confirm all your existing devices and verify contacts again"</string>
<string name="screen_encryption_reset_footer">"Only start fresh if you don\'t have access to another signed-in device and you\'ve lost your backup password."</string>
<string name="screen_encryption_reset_title">"Can\'t confirm? You\'ll need to start fresh."</string>
<string name="screen_key_backup_disable_description">"Deleting message backup will remove your account security and messages from the server and turn off the following security features:"</string>
<string name="screen_key_backup_disable_title">"Are you sure you want to turn off message backup and delete it?"</string>
<string name="screen_recovery_key_change_description">"Get a new backup password if you\'ve lost your existing one. After changing your backup password, your old one will no longer work."</string>
<string name="screen_recovery_key_change_generate_key">"Generate a new backup password"</string>
<string name="screen_recovery_key_change_success">"Backup password changed"</string>
<string name="screen_recovery_key_change_title">"Change backup password?"</string>
<string name="screen_recovery_key_confirm_error_content">"Please try again to confirm access to your message backup."</string>
<string name="screen_recovery_key_confirm_error_title">"Incorrect backup password"</string>
<string name="screen_recovery_key_confirm_key_description">"You might have seen the terms \"recovery key\", \"security key\" or \"security phrase\" instead of \"backup password\". Don\'t worry, this is all the same."</string>
<string name="screen_recovery_key_confirm_success">"Backup password confirmed"</string>
<string name="screen_recovery_key_confirm_title">"Enter your backup password"</string>
<string name="screen_recovery_key_copied_to_clipboard">"Copied backup password"</string>
<string name="screen_recovery_key_save_action">"Save backup password"</string>
<string name="screen_recovery_key_save_description">"Write down this backup password somewhere safe, like a password manager, encrypted note, or a physical safe."</string>
<string name="screen_recovery_key_save_key_description">"Tap to copy backup password"</string>
<string name="screen_recovery_key_save_title">"Save your backup password somewhere safe"</string>
<string name="screen_recovery_key_setup_confirmation_description">"You will not be able to access your new backup password after this step."</string>
<string name="screen_recovery_key_setup_confirmation_title">"Have you saved your backup password?"</string>
<string name="screen_recovery_key_setup_description">"Your message backup is protected by a backup password. If you need a new backup password after setup, you can recreate it by selecting Change backup password."</string>
<string name="screen_recovery_key_setup_generate_key">"Generate your backup password"</string>
<string name="screen_recovery_key_setup_success">"Backup setup successful"</string>
<string name="screen_recovery_key_setup_title">"Set up backup"</string>
<string name="screen_reset_encryption_confirmation_alert_title">"Are you sure you want to start fresh?"</string>
<string name="screen_reset_encryption_password_subtitle">"Confirm that you want to start fresh."</string>
</resources>

View file

@ -37,7 +37,7 @@
<string name="screen_recovery_key_change_generate_key">"Luo uusi palautusavain"</string>
<string name="screen_recovery_key_change_generate_key_description">"Älä jaa tätä kenenkään kanssa!"</string>
<string name="screen_recovery_key_change_success">"Palautusavain vaihdettu"</string>
<string name="screen_recovery_key_change_title">"Vaihda palautusavain?"</string>
<string name="screen_recovery_key_change_title">"Vaihdetaanko palautusavain?"</string>
<string name="screen_recovery_key_confirm_create_new_recovery_key">"Luo uusi palautusavain"</string>
<string name="screen_recovery_key_confirm_description">"Varmista, ettei kukaan näe tätä ruutua!"</string>
<string name="screen_recovery_key_confirm_error_content">"Yritä uudelleen vahvistaaksesi pääsyn avainten säilytykseen."</string>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_identity_confirmation_create_new_recovery_key">"Create a new backup password"</string>
<string name="screen_identity_confirmation_subtitle">"Confirm this device to set up secure messaging."</string>
<string name="screen_identity_confirmation_title">"Confirm it\'s you"</string>
<string name="screen_identity_confirmation_use_recovery_key">"Use backup password"</string>
<string name="screen_identity_confirmed_title">"Device confirmed"</string>
<string name="screen_session_verification_complete_user_subtitle">"Now you can trust this user when sending or receiving messages."</string>
<string name="screen_session_verification_enter_recovery_key">"Enter backup password"</string>
<string name="screen_session_verification_request_success_title">"Device confirmed"</string>
<string name="screen_session_verification_use_another_device_title">"Open the app on another confirmed device"</string>
<string name="screen_session_verification_user_responder_subtitle">"For extra security, another user wants to verify you. You\'ll be shown a set of emojis to compare."</string>
</resources>