diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index fae6fc9c15..fd389645a9 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -59,6 +59,7 @@ dependencies { testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.oidc.impl) + testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushproviders.test) testImplementation(projects.features.networkmonitor.test) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt index cd156bce02..4f60e543bb 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt @@ -9,4 +9,6 @@ package io.element.android.appnav.loggedin sealed interface LoggedInEvents { data class CloseErrorDialog(val doNotShowAgain: Boolean) : LoggedInEvents + data object CheckSlidingSyncProxyAvailability : LoggedInEvents + data object LogoutAndMigrateToNativeSlidingSync : LoggedInEvents } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt index e6779c2c99..c619629152 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt @@ -16,6 +16,7 @@ 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 im.vector.app.features.analytics.plan.CryptoSessionStateChange import im.vector.app.features.analytics.plan.UserProperties import io.element.android.features.networkmonitor.api.NetworkMonitor @@ -29,6 +30,7 @@ import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus +import io.element.android.libraries.preferences.api.store.EnableNativeSlidingSyncUseCase import io.element.android.libraries.push.api.PushService import io.element.android.libraries.pushproviders.api.RegistrationFailure import io.element.android.services.analytics.api.AnalyticsService @@ -48,6 +50,7 @@ class LoggedInPresenter @Inject constructor( private val sessionVerificationService: SessionVerificationService, private val analyticsService: AnalyticsService, private val encryptionService: EncryptionService, + private val enableNativeSlidingSyncUseCase: EnableNativeSlidingSyncUseCase, ) : Presenter { @Composable override fun present(): LoggedInState { @@ -78,6 +81,7 @@ class LoggedInPresenter @Inject constructor( networkStatus == NetworkStatus.Online && syncIndicator == RoomListService.SyncIndicator.Show } } + var forceNativeSlidingSyncMigration by remember { mutableStateOf(false) } LaunchedEffect(Unit) { combine( sessionVerificationService.sessionVerifiedStatus, @@ -97,6 +101,18 @@ class LoggedInPresenter @Inject constructor( } } } + LoggedInEvents.CheckSlidingSyncProxyAvailability -> coroutineScope.launch { + // Force the user to log out if they were using the proxy sliding sync and it's no longer available, but native sliding sync is. + forceNativeSlidingSyncMigration = !matrixClient.isUsingNativeSlidingSync() && + matrixClient.isNativeSlidingSyncSupported() && + !matrixClient.isSlidingSyncProxySupported() + } + LoggedInEvents.LogoutAndMigrateToNativeSlidingSync -> coroutineScope.launch { + // Enable native sliding sync if it wasn't already the case + enableNativeSlidingSyncUseCase() + // Then force the logout + matrixClient.logout(userInitiated = true, ignoreSdkError = true) + } } } @@ -104,6 +120,7 @@ class LoggedInPresenter @Inject constructor( showSyncSpinner = showSyncSpinner, pusherRegistrationState = pusherRegistrationState.value, ignoreRegistrationError = ignoreRegistrationError, + forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration, eventSink = ::handleEvent ) } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt index 7ef0e6f549..2735b02706 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt @@ -13,5 +13,6 @@ data class LoggedInState( val showSyncSpinner: Boolean, val pusherRegistrationState: AsyncData, val ignoreRegistrationError: Boolean, + val forceNativeSlidingSyncMigration: Boolean, val eventSink: (LoggedInEvents) -> Unit, ) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt index bcc88b1c3a..13d100820b 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt @@ -16,15 +16,18 @@ open class LoggedInStateProvider : PreviewParameterProvider { aLoggedInState(), aLoggedInState(showSyncSpinner = true), aLoggedInState(pusherRegistrationState = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable())), + aLoggedInState(forceNativeSlidingSyncMigration = true), ) } fun aLoggedInState( showSyncSpinner: Boolean = false, pusherRegistrationState: AsyncData = AsyncData.Uninitialized, + forceNativeSlidingSyncMigration: Boolean = false, ) = LoggedInState( showSyncSpinner = showSyncSpinner, pusherRegistrationState = pusherRegistrationState, ignoreRegistrationError = false, + forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration, eventSink = {}, ) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index 0af150e0b7..3134270965 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -15,10 +15,14 @@ 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.lifecycle.Lifecycle +import io.element.android.appnav.R import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.dialogs.ErrorDialogWithDoNotShowAgain import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.matrix.api.exception.isNetworkError import io.element.android.libraries.ui.strings.CommonStrings @@ -28,6 +32,11 @@ fun LoggedInView( navigateToNotificationTroubleshoot: () -> Unit, modifier: Modifier = Modifier ) { + OnLifecycleEvent { _, event -> + if (event == Lifecycle.Event.ON_RESUME) { + state.eventSink(LoggedInEvents.CheckSlidingSyncProxyAvailability) + } + } Box( modifier = modifier .fillMaxSize() @@ -61,6 +70,13 @@ fun LoggedInView( } } } + + // Set the force migration dialog here so it's always displayed over every screen + if (state.forceNativeSlidingSyncMigration) { + ForceNativeSlidingSyncMigrationDialog(onSubmit = { + state.eventSink(LoggedInEvents.LogoutAndMigrateToNativeSlidingSync) + }) + } } private fun Throwable.getReason(): String? { @@ -80,6 +96,19 @@ private fun Throwable.getReason(): String? { } } +@Composable +private fun ForceNativeSlidingSyncMigrationDialog( + onSubmit: () -> Unit, +) { + ErrorDialog( + title = null, + content = stringResource(R.string.banner_migrate_to_native_sliding_sync_force_logout_title), + submitText = stringResource(R.string.banner_migrate_to_native_sliding_sync_action), + onSubmit = onSubmit, + canDismiss = false, + ) +} + @PreviewsDayNight @Composable internal fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview { diff --git a/appnav/src/main/res/values/localazy.xml b/appnav/src/main/res/values/localazy.xml new file mode 100644 index 0000000000..bb6df44051 --- /dev/null +++ b/appnav/src/main/res/values/localazy.xml @@ -0,0 +1,5 @@ + + + "Log Out & Upgrade" + "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app." + diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt index ee060f3444..519cceddfe 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt @@ -29,6 +29,8 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService +import io.element.android.libraries.preferences.api.store.EnableNativeSlidingSyncUseCase +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.libraries.push.api.PushService import io.element.android.libraries.push.test.FakePushService import io.element.android.libraries.pushproviders.api.Distributor @@ -42,6 +44,10 @@ import io.element.android.tests.testutils.lambda.any import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -91,7 +97,8 @@ class LoggedInPresenterTest { pushService = FakePushService(), sessionVerificationService = verificationService, analyticsService = analyticsService, - encryptionService = encryptionService + encryptionService = encryptionService, + enableNativeSlidingSyncUseCase = EnableNativeSlidingSyncUseCase(InMemoryAppPreferencesStore(), this), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -487,26 +494,103 @@ class LoggedInPresenterTest { ) } + @Test + fun `present - CheckSlidingSyncProxyAvailability forces the sliding sync migration under the right circumstances`() = runTest { + // The migration will be forced if: + // - The user is not using the native sliding sync + // - The sliding sync proxy is no longer supported + // - The native sliding sync is supported + val matrixClient = FakeMatrixClient( + isUsingNativeSlidingSyncLambda = { false }, + isSlidingSyncProxySupportedLambda = { false }, + isNativeSlidingSyncSupportedLambda = { true }, + ) + val presenter = createLoggedInPresenter(matrixClient = matrixClient) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.forceNativeSlidingSyncMigration).isFalse() + + initialState.eventSink(LoggedInEvents.CheckSlidingSyncProxyAvailability) + + assertThat(awaitItem().forceNativeSlidingSyncMigration).isTrue() + } + } + + @Test + fun `present - CheckSlidingSyncProxyAvailability will not force the migration if native sliding sync is not supported too`() = runTest { + val matrixClient = FakeMatrixClient( + isUsingNativeSlidingSyncLambda = { false }, + isSlidingSyncProxySupportedLambda = { false }, + isNativeSlidingSyncSupportedLambda = { false }, + ) + val presenter = createLoggedInPresenter(matrixClient = matrixClient) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.forceNativeSlidingSyncMigration).isFalse() + + initialState.eventSink(LoggedInEvents.CheckSlidingSyncProxyAvailability) + + expectNoEvents() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - LogoutAndMigrateToNativeSlidingSync enables native sliding sync and logs out the user`() = runTest { + val logoutLambda = lambdaRecorder { userInitiated, ignoreSdkError -> + assertThat(userInitiated).isTrue() + assertThat(ignoreSdkError).isTrue() + null + } + val matrixClient = FakeMatrixClient().apply { + this.logoutLambda = logoutLambda + } + val appPreferencesStore = InMemoryAppPreferencesStore() + val enableNativeSlidingSyncUseCase = EnableNativeSlidingSyncUseCase(appPreferencesStore, this) + val presenter = createLoggedInPresenter(matrixClient = matrixClient, enableNativeSlidingSyncUseCase = enableNativeSlidingSyncUseCase) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + assertThat(appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first()).isFalse() + + initialState.eventSink(LoggedInEvents.LogoutAndMigrateToNativeSlidingSync) + + advanceUntilIdle() + + assertThat(appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first()).isTrue() + assertThat(logoutLambda.assertions().isCalledOnce()) + } + } + private suspend fun ReceiveTurbine.awaitFirstItem(): T { skipItems(1) return awaitItem() } - private fun createLoggedInPresenter( + private fun TestScope.createLoggedInPresenter( roomListService: RoomListService = FakeRoomListService(), networkStatus: NetworkStatus = NetworkStatus.Offline, analyticsService: AnalyticsService = FakeAnalyticsService(), sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(), encryptionService: EncryptionService = FakeEncryptionService(), pushService: PushService = FakePushService(), + enableNativeSlidingSyncUseCase: EnableNativeSlidingSyncUseCase = EnableNativeSlidingSyncUseCase(InMemoryAppPreferencesStore(), this), + matrixClient: MatrixClient = FakeMatrixClient(roomListService = roomListService), ): LoggedInPresenter { return LoggedInPresenter( - matrixClient = FakeMatrixClient(roomListService = roomListService), + matrixClient = matrixClient, networkMonitor = FakeNetworkMonitor(networkStatus), pushService = pushService, sessionVerificationService = sessionVerificationService, analyticsService = analyticsService, - encryptionService = encryptionService + encryptionService = encryptionService, + enableNativeSlidingSyncUseCase = enableNativeSlidingSyncUseCase, ) } } diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt index 7d0f1f6676..4641e4e7bc 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt @@ -111,7 +111,7 @@ internal fun CallScreenView( is AsyncData.Failure -> ErrorDialog( content = state.urlState.error.message.orEmpty(), - onDismiss = { state.eventSink(CallScreenEvents.Hangup) }, + onSubmit = { state.eventSink(CallScreenEvents.Hangup) }, ) is AsyncData.Success -> Unit } diff --git a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt index 6ee2990c5b..a98bfbff2f 100644 --- a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt +++ b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt @@ -105,7 +105,7 @@ private fun LeaveRoomErrorDialog( is LeaveRoomState.Error.Hidden -> {} is LeaveRoomState.Error.Shown -> ErrorDialog( content = stringResource(CommonStrings.error_unknown), - onDismiss = { state.eventSink(LeaveRoomEvent.HideError) } + onSubmit = { state.eventSink(LeaveRoomEvent.HideError) } ) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt index b794f2eed4..b3cd532487 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt @@ -116,7 +116,7 @@ private fun SetupPinContent( ErrorDialog( title = state.setupPinFailure.title(), content = state.setupPinFailure.content(), - onDismiss = { + onSubmit = { state.eventSink(SetupPinEvents.ClearFailure) } ) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt index 702e253f03..8667b3806a 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt @@ -104,7 +104,7 @@ fun PinUnlockView( if (state.showBiometricUnlockError) { ErrorDialog( content = state.biometricUnlockErrorMessage ?: "", - onDismiss = { state.eventSink(PinUnlockEvents.ClearBiometricError) } + onSubmit = { state.eventSink(PinUnlockEvents.ClearBiometricError) } ) } } @@ -206,7 +206,7 @@ private fun SignOutPrompt( ErrorDialog( title = stringResource(id = R.string.screen_app_lock_signout_alert_title), content = stringResource(id = R.string.screen_app_lock_signout_alert_message), - onDismiss = onSignOut, + onSubmit = onSignOut, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt index a2c3b48251..c6139ae492 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt @@ -36,7 +36,7 @@ fun ChangeServerView( ErrorDialog( modifier = modifier, content = error.message(), - onDismiss = { + onSubmit = { eventSink.invoke(ChangeServerEvents.ClearError) } ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index a516c1b4a6..da1106d463 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -103,7 +103,7 @@ fun ConfirmAccountProviderView( is ChangeServerError.Error -> { ErrorDialog( content = error.message(), - onDismiss = { + onSubmit = { eventSink.invoke(ConfirmAccountProviderEvents.ClearError) } ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index bd6437ddba..48fe71e0fc 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -283,7 +283,7 @@ private fun LoginErrorDialog(error: Throwable, onDismiss: () -> Unit) { ErrorDialog( title = stringResource(id = CommonStrings.dialog_title_error), content = stringResource(loginError(error)), - onDismiss = onDismiss + onSubmit = onDismiss ) } diff --git a/features/login/impl/src/main/res/values-be/translations.xml b/features/login/impl/src/main/res/values-be/translations.xml index f8e675854e..777acc423d 100644 --- a/features/login/impl/src/main/res/values-be/translations.xml +++ b/features/login/impl/src/main/res/values-be/translations.xml @@ -78,10 +78,4 @@ "Тут будуць захоўвацца вашыя размовы - сапраўды гэтак жа, як вы выкарыстоўваеце паштовага правайдара для захоўвання сваіх лістоў." "Вы збіраецеся ўвайсці ў %1$s" "Вы збіраецеся стварыць уліковы запіс на %1$s" - "Зараз існуе высокі попыт на %1$s на %2$s. Калі ласка, вярніцеся ў праграму праз некалькі дзён і паспрабуйце зноў. - -Дзякуй за цярпенне!" - "Вітаем у %1$s!" - "Амаль гатова." - "Вы зарэгістраваны." diff --git a/features/login/impl/src/main/res/values-bg/translations.xml b/features/login/impl/src/main/res/values-bg/translations.xml index cfc51c3f8c..eced3ef828 100644 --- a/features/login/impl/src/main/res/values-bg/translations.xml +++ b/features/login/impl/src/main/res/values-bg/translations.xml @@ -24,5 +24,4 @@ "Това е мястото, където ще живеят вашите разговори — точно както бихте използвали имейл доставчик, за да съхранявате вашите имейли." "На път сте да влезете в %1$s" "На път сте да създадете акаунт в %1$s" - "Добре дошли в %1$s!" diff --git a/features/login/impl/src/main/res/values-cs/translations.xml b/features/login/impl/src/main/res/values-cs/translations.xml index c0f00a87af..d65a5571e4 100644 --- a/features/login/impl/src/main/res/values-cs/translations.xml +++ b/features/login/impl/src/main/res/values-cs/translations.xml @@ -78,10 +78,4 @@ Zkuste se přihlásit ručně nebo naskenujte QR kód pomocí jiného zařízen "Zde budou uloženy vaše konverzace - podobně jako u poskytovatele e-mailových služeb uchováváte své e-maily." "Chystáte se přihlásit do služby %1$s" "Chystáte se vytvořit účet na %1$s" - "Na %2$s je momentálně vysoká poptávka po %1$s. Vraťte se do aplikace za pár dní a zkuste to znovu. - -Díky za trpělivost!" - "Vítá vás %1$s!" - "Jste v pořadníku!" - "Jdete do toho!" diff --git a/features/login/impl/src/main/res/values-de/translations.xml b/features/login/impl/src/main/res/values-de/translations.xml index 2399ec86ba..c2bdf37ca7 100644 --- a/features/login/impl/src/main/res/values-de/translations.xml +++ b/features/login/impl/src/main/res/values-de/translations.xml @@ -78,10 +78,4 @@ Versuche, dich manuell anzumelden, oder scanne den QR-Code mit einem anderen Ger "Hier werden deine Gespräche gespeichert - so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würden." "Du bist dabei, dich bei %1$s anzumelden" "Du bist dabei, ein Konto auf %1$s zu erstellen" - "Derzeit besteht eine hohe Nachfrage nach %1$s auf %2$s. Kehre in ein paar Tagen zur App zurück und versuche es erneut. - -Danke für deine Geduld!" - "Willkommen bei %1$s!" - "Du bist fast am Ziel." - "Du bist dabei." diff --git a/features/login/impl/src/main/res/values-el/translations.xml b/features/login/impl/src/main/res/values-el/translations.xml index 8ff0227168..24f26483fb 100644 --- a/features/login/impl/src/main/res/values-el/translations.xml +++ b/features/login/impl/src/main/res/values-el/translations.xml @@ -78,10 +78,4 @@ "Εδώ θα ζουν οι συνομιλίες σου - όπως θα χρησιμοποιούσες έναν πάροχο email για να διατηρήσεις τα email σου." "Πρόκειται να συνδεθείς στο %1$s" "Πρόκειται να δημιουργήσεις έναν λογαριασμό στο %1$s" - "Υπάρχει μεγάλη ζήτηση για το %1$s στον %2$s αυτή τη στιγμή. Επέστρεψε στην εφαρμογή σε λίγες μέρες και δοκίμασε ξανά. - -Ευχαριστώ για την υπομονή σου!" - "Καλώς ήρθες στο %1$s!" - "Σχεδόν τα κατάφερες." - "Είσαι μέσα." diff --git a/features/login/impl/src/main/res/values-es/translations.xml b/features/login/impl/src/main/res/values-es/translations.xml index c7537c9d7e..60324ae89d 100644 --- a/features/login/impl/src/main/res/values-es/translations.xml +++ b/features/login/impl/src/main/res/values-es/translations.xml @@ -37,10 +37,4 @@ "Aquí es donde se alojarán tus conversaciones — justo como utilizarías un proveedor de correo electrónico para guardar tus correos electrónicos." "Estás a punto de iniciar sesión en %1$s" "Estás a punto de crear una cuenta en %1$s" - "Hay una gran demanda para %1$s en %2$s en este momento. Vuelve a la aplicación en unos días e inténtalo de nuevo. - -¡Gracias por tu paciencia!" - "¡Bienvenido a %1$s!" - "Ya casi has terminado." - "Estás dentro." diff --git a/features/login/impl/src/main/res/values-et/translations.xml b/features/login/impl/src/main/res/values-et/translations.xml index 0558b349b4..f82ea50b91 100644 --- a/features/login/impl/src/main/res/values-et/translations.xml +++ b/features/login/impl/src/main/res/values-et/translations.xml @@ -78,10 +78,4 @@ Proovi käsitsi sisselogimist või skaneeri QR-koodi mõne muu seadmega.""See on koht, kus sinu vestlused elavad – just nagu kasutaksid oma e-kirjade säilitamiseks e-postitenuse pakkujat." "Sa oled sisselogimas koduserverisse %1$s" "Sa oled loomas kasutajakontot koduserveris %1$s" - "%1$s kasutamiseks %2$s koduserveris on hetkel palju huvilisi. Proovi seda samast rakendusest mõne päeva pärast. - -Täname kannatlikkuse eest!" - "Tere tulemast rakendusse %1$s!" - "Peaaegu olemas." - "Oled nüüd jututoas." diff --git a/features/login/impl/src/main/res/values-fr/translations.xml b/features/login/impl/src/main/res/values-fr/translations.xml index 49e513c2a8..a5928ffb3f 100644 --- a/features/login/impl/src/main/res/values-fr/translations.xml +++ b/features/login/impl/src/main/res/values-fr/translations.xml @@ -76,10 +76,4 @@ "C’est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails." "Vous êtes sur le point de vous connecter à %1$s" "Vous êtes sur le point de créer un compte sur %1$s" - "Il y a une forte demande pour %1$s sur %2$s à l’heure actuelle. Revenez sur l’application dans quelques jours et réessayez. - -Merci pour votre patience !" - "Bienvenue dans %1$s !" - "Vous y êtes presque." - "Vous y êtes." diff --git a/features/login/impl/src/main/res/values-hu/translations.xml b/features/login/impl/src/main/res/values-hu/translations.xml index 8d8bf40f3f..a37288fd6f 100644 --- a/features/login/impl/src/main/res/values-hu/translations.xml +++ b/features/login/impl/src/main/res/values-hu/translations.xml @@ -78,10 +78,4 @@ Próbáljon meg kézileg bejelentkezni, vagy olvassa be a QR-kódot egy másik e "Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez." "Hamarosan bejelentkezik ebbe: %1$s" "Hamarosan létrehoz egy fiókot ezen: %1$s" - "Jelenleg nagy a kereslet a(z) %2$s oldalon futó %1$s iránt. Térjen vissza néhány nap múlva az alkalmazáshoz, és próbálja újra. - -Köszönjük a türelmét!" - "Üdvözli az %1$s!" - "Már majdnem kész van." - "Bent van." diff --git a/features/login/impl/src/main/res/values-in/translations.xml b/features/login/impl/src/main/res/values-in/translations.xml index 97d84ff1f4..749406da04 100644 --- a/features/login/impl/src/main/res/values-in/translations.xml +++ b/features/login/impl/src/main/res/values-in/translations.xml @@ -78,10 +78,4 @@ Coba masuk secara manual, atau pindai kode QR dengan perangkat lain." "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda." "Anda akan masuk ke %1$s" "Anda akan membuat akun di %1$s" - "Ada permintaan tinggi untuk %1$s di %2$s saat ini. Kembalilah ke aplikasi dalam beberapa hari dan coba lagi. - -Terima kasih atas kesabaran Anda!" - "Selamat datang di %1$s!" - "Anda hampir selesai." - "Anda sudah masuk." diff --git a/features/login/impl/src/main/res/values-it/translations.xml b/features/login/impl/src/main/res/values-it/translations.xml index dcd9a7c2ae..eaa5313b03 100644 --- a/features/login/impl/src/main/res/values-it/translations.xml +++ b/features/login/impl/src/main/res/values-it/translations.xml @@ -78,10 +78,4 @@ Prova ad accedere manualmente o scansiona il codice QR con un altro dispositivo. "Qui è dove vivranno le tue conversazioni — proprio come useresti un fornitore di posta elettronica per conservare le tue email." "Stai per accedere a %1$s" "Stai per creare un account su %1$s" - "Al momento c\'è una grande richiesta per %1$s su %2$s. Torna a visitare l\'app tra qualche giorno e riprova. - -Grazie per la pazienza!" - "Benvenuti in %1$s!" - "Ci sei quasi." - "Sei dentro." diff --git a/features/login/impl/src/main/res/values-ka/translations.xml b/features/login/impl/src/main/res/values-ka/translations.xml index 84e97e0a34..6fc49e7b41 100644 --- a/features/login/impl/src/main/res/values-ka/translations.xml +++ b/features/login/impl/src/main/res/values-ka/translations.xml @@ -34,10 +34,4 @@ "აქ იქნება თქვენი საუბრები - ისევე, როგორც თქვენ ელ. ფოსტაში ინახება თქვენი ელ.წერილები." "თქვენ აპირებთ შესვლას %1$s-ში" "თქვენ აპირებთ ანგარიშის შექმნას %1$s-ში" - "ახლა დიდი მოთხოვნაა %1$s-ზე %2$s-ში. დაბრუნდით რამდენიმე დღეში და სცადეთ ერთხელაც. - -მადლობა მოთმენისათვის!" - "კეთილი იყოს თქვენი მობრძანება %1$s-ში!" - "თითქმის მზადაა." - "თქვენ შეხვედით." diff --git a/features/login/impl/src/main/res/values-nl/translations.xml b/features/login/impl/src/main/res/values-nl/translations.xml index 80fbe57afb..0e351f8498 100644 --- a/features/login/impl/src/main/res/values-nl/translations.xml +++ b/features/login/impl/src/main/res/values-nl/translations.xml @@ -35,10 +35,4 @@ "Dit is waar je gesprekken zullen worden bewaard — net zoals je een e-mailprovider zou gebruiken om je e-mails te bewaren." "Je staat op het punt je aan te melden bij %1$s" "Je staat op het punt een account aan te maken op %1$s" - "Er is momenteel veel vraag naar %1$s op %2$s. Kom over een paar dagen terug naar de app en probeer het opnieuw. - -Bedankt voor je geduld!" - "Welkom bij %1$s!" - "Je bent er bijna." - "Je bent binnen." diff --git a/features/login/impl/src/main/res/values-pl/translations.xml b/features/login/impl/src/main/res/values-pl/translations.xml index 4d23e15fe8..6990121a43 100644 --- a/features/login/impl/src/main/res/values-pl/translations.xml +++ b/features/login/impl/src/main/res/values-pl/translations.xml @@ -78,10 +78,4 @@ Spróbuj zalogować się ręcznie lub zeskanuj kod QR na innym urządzeniu.""Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail." "Zamierzasz się zalogować do %1$s" "Zamierzasz utworzyć konto na %1$s" - "Obecnie istnieje duże zapotrzebowanie na %1$s na %2$s. Wróć do aplikacji za kilka dni i spróbuj ponownie. - -Dziękujemy za Twoją cierpliwość!" - "Witamy w %1$s!" - "Już prawie gotowe!" - "Witamy!" diff --git a/features/login/impl/src/main/res/values-pt-rBR/translations.xml b/features/login/impl/src/main/res/values-pt-rBR/translations.xml index 4cb9b4f7b0..7b753ffb9f 100644 --- a/features/login/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/login/impl/src/main/res/values-pt-rBR/translations.xml @@ -34,10 +34,4 @@ "Aqui é onde suas conversas vão ficar — assim como você usa um provedor de e-mails para manter seus e-mails." "Você está prestes a fazer login em %1$s" "Você está prestes a criar uma conta em %1$s" - "Há uma grande demanda por %1$s sobre %2$s no momento. Volte ao aplicativo em alguns dias e tente novamente. - -Obrigado pela sua paciência!" - "Bem-vindo ao %1$s!" - "Você está quase lá." - "Você está dentro." diff --git a/features/login/impl/src/main/res/values-pt/translations.xml b/features/login/impl/src/main/res/values-pt/translations.xml index f5feb0cbdd..f03b48c5dd 100644 --- a/features/login/impl/src/main/res/values-pt/translations.xml +++ b/features/login/impl/src/main/res/values-pt/translations.xml @@ -78,10 +78,4 @@ Tenta iniciar a sessão manualmente ou digitaliza o código QR com outro disposi "É aqui que as tuas conversas vão ficar — tal como num serviço de e-mail." "Irás iniciar sessão em %1$s" "Irás criar uma conta em %1$s" - "Há uma grande procura pela %1$s no %2$s, de momento. Volta à aplicação daqui a uns dias e tenta novamente. - -Obrigado!" - "Bem-vindo à %1$s!" - "Estás quase lá." - "Estás dentro." diff --git a/features/login/impl/src/main/res/values-ro/translations.xml b/features/login/impl/src/main/res/values-ro/translations.xml index 1ff3fe2436..261f16d9f6 100644 --- a/features/login/impl/src/main/res/values-ro/translations.xml +++ b/features/login/impl/src/main/res/values-ro/translations.xml @@ -78,10 +78,4 @@ "Aici vor trăi conversațiile dvs. - la fel cum ați folosi un furnizor de e-mail pentru a vă păstra e-mailurile." "Sunteți pe cale să vă conectați la %1$s" "Sunteți pe cale să creați un cont pe %1$s" - "Există o cerere mare pentru %1$s pentru %2$s în acest moment. Reveniți la aplicație în câteva zile și încercați din nou. - -Vă mulțumim pentru răbdare!" - "Bun venit la%1$s!" - "Sunteți pe lista de așteptare" - "Sunteți conectat!" diff --git a/features/login/impl/src/main/res/values-ru/translations.xml b/features/login/impl/src/main/res/values-ru/translations.xml index c64d683a12..71a2691b4c 100644 --- a/features/login/impl/src/main/res/values-ru/translations.xml +++ b/features/login/impl/src/main/res/values-ru/translations.xml @@ -78,10 +78,4 @@ "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." "Вы собираетесь войти в %1$s" "Вы собираетесь создать учетную запись на %1$s" - "В настоящее время существует высокий спрос на %1$s на %2$s. Вернитесь в приложение через несколько дней и попробуйте снова. - -Спасибо за терпение!" - "Добро пожаловать в %1$s!" - "Почти готово." - "Вы зарегистрированы." diff --git a/features/login/impl/src/main/res/values-sk/translations.xml b/features/login/impl/src/main/res/values-sk/translations.xml index fea7879d01..8d862609b7 100644 --- a/features/login/impl/src/main/res/values-sk/translations.xml +++ b/features/login/impl/src/main/res/values-sk/translations.xml @@ -78,10 +78,4 @@ Skúste sa prihlásiť manuálne alebo naskenujte QR kód pomocou iného zariade "Tu budú žiť vaše konverzácie - podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov." "Chystáte sa prihlásiť do %1$s" "Chystáte sa vytvoriť účet na %1$s" - "Momentálne je veľký dopyt po %1$s na %2$s. Vráťte sa do aplikácie za pár dní a skúste to znova. - -Ďakujeme za trpezlivosť!" - "Vitajte v %1$s!" - "Ste na čakanej listine!" - "Ste dnu!" diff --git a/features/login/impl/src/main/res/values-sv/translations.xml b/features/login/impl/src/main/res/values-sv/translations.xml index d50610adcc..de290469ca 100644 --- a/features/login/impl/src/main/res/values-sv/translations.xml +++ b/features/login/impl/src/main/res/values-sv/translations.xml @@ -78,10 +78,4 @@ Prova att logga in manuellt eller skanna QR-koden med en annan enhet." "Det är här dina konversationer kommer att sparas - precis som du skulle använda en e-postleverantör för att spara dina e-brev." "Du är på väg att logga in på %1$s" "Du är på väg att skapa ett konto på %1$s" - "Det finns en stor efterfrågan på %1$s på %2$s just nu. Kom tillbaka till appen om några dagar och försök igen. - -Tack för ditt tålamod!" - "Välkommen till %1$s!" - "Du är nästan framme." - "Du är inne." diff --git a/features/login/impl/src/main/res/values-uk/translations.xml b/features/login/impl/src/main/res/values-uk/translations.xml index 9cb6eb1f5f..df436a9a69 100644 --- a/features/login/impl/src/main/res/values-uk/translations.xml +++ b/features/login/impl/src/main/res/values-uk/translations.xml @@ -78,10 +78,4 @@ "Тут будуть зберігатися Ваші розмови - так само, як Ви використовуєте поштову скриньку для зберігання своїх електронних листів." "Ви збираєтесь увійти в %1$s" "Ви збираєтеся створити обліковий запис на %1$s" - "На цей момент існує високий попит на %1$s в %2$s. Поверніться до застосунку через кілька днів і спробуйте ще раз. - -Дякуємо за терпіння!" - "Ласкаво просимо до %1$s!" - "Майже готово." - "Готово." diff --git a/features/login/impl/src/main/res/values-uz/translations.xml b/features/login/impl/src/main/res/values-uz/translations.xml index 67dc5129d7..db16e17b09 100644 --- a/features/login/impl/src/main/res/values-uz/translations.xml +++ b/features/login/impl/src/main/res/values-uz/translations.xml @@ -33,10 +33,4 @@ "Bu sizning suhbatlaringiz yashaydigan joy - xuddi siz elektron pochta xabarlaringizni saqlash uchun elektron pochta provayderidan foydalanganingiz kabi." "Siz tizimga kirmoqchisiz%1$s" "Hisob yaratmoqchisiz%1$s" - "Hozirgi paytda %2$sga %1$sda talab yuqori. Bir necha kundan keyin ilovaga qayting va qaytadan urining. - -Sabr-toqatingiz uchun rahmat!" - "%1$sga Xush kelibsiz!" - "Siz deyarli keldingiz." - "Siz kirdingiz." diff --git a/features/login/impl/src/main/res/values-zh-rTW/translations.xml b/features/login/impl/src/main/res/values-zh-rTW/translations.xml index d345095c4b..b66cfca31f 100644 --- a/features/login/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/login/impl/src/main/res/values-zh-rTW/translations.xml @@ -29,5 +29,4 @@ "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。" "您即將登入 %1$s" "您即將在 %1$s 建立帳號" - "歡迎使用 %1$s!" diff --git a/features/login/impl/src/main/res/values-zh/translations.xml b/features/login/impl/src/main/res/values-zh/translations.xml index 0afd6dad93..2f24aacf69 100644 --- a/features/login/impl/src/main/res/values-zh/translations.xml +++ b/features/login/impl/src/main/res/values-zh/translations.xml @@ -78,10 +78,4 @@ "这是您的对话将进行的地方,就像您使用电子邮件提供商来保存电子邮件一样。" "即将登录 %1$s" "即将在 %1$s 上创建一个账户" - "目前 %1$s 上 %2$s 的负载很大。过几天再回来试试吧。 - -感谢您的耐心!" - "欢迎使用 %1$s" - "马上就好。" - "您已加入。" diff --git a/features/login/impl/src/main/res/values/localazy.xml b/features/login/impl/src/main/res/values/localazy.xml index cade81c4f2..b0b6252d52 100644 --- a/features/login/impl/src/main/res/values/localazy.xml +++ b/features/login/impl/src/main/res/values/localazy.xml @@ -78,10 +78,4 @@ Try signing in manually, or scan the QR code with another device." "This is where your conversations will live — just like you would use an email provider to keep your emails." "You’re about to sign in to %1$s" "You’re about to create an account on %1$s" - "There\'s a high demand for %1$s on %2$s at the moment. Come back to the app in a few days and try again. - -Thanks for your patience!" - "Welcome to %1$s!" - "You’re almost there." - "You\'re in." diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt index 8a03ed857c..a9c12fe732 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt @@ -108,7 +108,7 @@ private fun PinnedMessagesListContent( ErrorDialog( title = stringResource(id = CommonStrings.error_unknown), content = stringResource(id = CommonStrings.error_failed_loading_messages), - onDismiss = onErrorDismiss + onSubmit = onErrorDismiss ) } PinnedMessagesListState.Empty -> PinnedMessagesListEmpty() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt index 7bd5301208..75d4d5ebac 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt @@ -36,7 +36,7 @@ fun FocusRequestStateView( } ErrorDialog( content = errorMessage, - onDismiss = onClearFocusRequestState, + onSubmit = onClearFocusRequestState, modifier = modifier, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt index f2cc385553..ffed5f31ac 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt @@ -19,7 +19,7 @@ internal fun VoiceMessageSendingFailedDialog( ErrorDialog( title = stringResource(CommonStrings.common_error), content = stringResource(CommonStrings.error_failed_uploading_voice_message), - onDismiss = onDismiss, + onSubmit = onDismiss, submitText = stringResource(CommonStrings.action_ok), ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt index 25cc166103..22884bc6ed 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt @@ -259,7 +259,7 @@ private fun InvalidNotificationSettingsView( ErrorDialog( title = stringResource(id = CommonStrings.dialog_title_error), content = stringResource(id = R.string.screen_notification_settings_failed_fixing_configuration), - onDismiss = onDismissError + onSubmit = onDismissError ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt index bfc19026a9..a3f39895df 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt @@ -124,7 +124,7 @@ fun RolesAndPermissionsView( is AsyncAction.Failure -> { ErrorDialog( content = stringResource(CommonStrings.error_unknown), - onDismiss = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) } + onSubmit = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) } ) } else -> Unit diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt index 2f7cfb16a0..ce362373f2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt @@ -210,7 +210,7 @@ fun ChangeRolesView( is AsyncAction.Failure -> { ErrorDialog( content = stringResource(CommonStrings.error_unknown), - onDismiss = { state.eventSink(ChangeRolesEvent.ClearError) } + onSubmit = { state.eventSink(ChangeRolesEvent.ClearError) } ) } is AsyncAction.Success -> { diff --git a/features/roomlist/impl/src/main/res/values/localazy.xml b/features/roomlist/impl/src/main/res/values/localazy.xml index ff6c626c63..36dc4b8d65 100644 --- a/features/roomlist/impl/src/main/res/values/localazy.xml +++ b/features/roomlist/impl/src/main/res/values/localazy.xml @@ -2,6 +2,7 @@ "Log Out & Upgrade" "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later." + "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app." "Upgrade available" "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices." "Set up recovery" diff --git a/features/securebackup/impl/src/main/res/values-be/translations.xml b/features/securebackup/impl/src/main/res/values-be/translations.xml index 75e4c2ccb9..910350f41e 100644 --- a/features/securebackup/impl/src/main/res/values-be/translations.xml +++ b/features/securebackup/impl/src/main/res/values-be/translations.xml @@ -38,7 +38,6 @@ "Паўтарыце спробу, каб пацвердзіць доступ да рэзервовай копіі чата." "Няправільны ключ аднаўлення" "Калі ў вас ёсць ключ аднаўлення або парольная фраза, гэта таксама будзе працаваць." - "Ключ аднаўлення або код доступу" "Увесці…" "Страцілі ключ аднаўлення?" "Ключ аднаўлення пацверджаны" diff --git a/features/securebackup/impl/src/main/res/values-cs/translations.xml b/features/securebackup/impl/src/main/res/values-cs/translations.xml index 6425f52b66..ba113f9c89 100644 --- a/features/securebackup/impl/src/main/res/values-cs/translations.xml +++ b/features/securebackup/impl/src/main/res/values-cs/translations.xml @@ -39,7 +39,6 @@ "Zkuste prosím znovu potvrdit přístup k záloze chatu." "Nesprávný klíč pro obnovení" "Pokud máte bezpečnostní klíč nebo bezpečnostní frázi, bude to fungovat také." - "Klíč pro obnovení nebo přístupový kód" "Zadejte…" "Ztratili jste klíč pro obnovení?" "Klíč pro obnovení potvrzen" diff --git a/features/securebackup/impl/src/main/res/values-de/translations.xml b/features/securebackup/impl/src/main/res/values-de/translations.xml index c95e51db7e..413ae1ea81 100644 --- a/features/securebackup/impl/src/main/res/values-de/translations.xml +++ b/features/securebackup/impl/src/main/res/values-de/translations.xml @@ -51,10 +51,6 @@ Das bedeutet:" "Bitte versuche es noch einmal, um den Zugriff auf dein Chat-Backup zu bestätigen." "Falscher Wiederherstellungsschlüssel" "Dies funktioniert auch mit einem Sicherheitsschlüssel oder Sicherheitsphrase." - - "Wiederherstellungsschlüssel" - " oder Passcode" - "Eingeben…" "Hast du deinen Wiederherstellungschlüssel vergessen?" "Wiederherstellungsschlüssel bestätigt" diff --git a/features/securebackup/impl/src/main/res/values-el/translations.xml b/features/securebackup/impl/src/main/res/values-el/translations.xml index e62f465743..b3c6eb5a54 100644 --- a/features/securebackup/impl/src/main/res/values-el/translations.xml +++ b/features/securebackup/impl/src/main/res/values-el/translations.xml @@ -33,7 +33,6 @@ "Προσπάθησε ξανά για να επιβεβαιώσεις την πρόσβαση στο αντίγραφο ασφαλείας της συνομιλίας σου." "Λανθασμένο κλειδί ανάκτησης" "Εάν έχεις ένα κλειδί ασφαλείας ή μια φράση ασφαλείας, θα λειτουργήσει επίσης." - "Κλειδί ανάκτησης ή κωδικός πρόσβασης" "Εισαγωγή…" "Έχασες το κλειδί ανάκτησης;" "Επιβεβαιώθηκε το κλειδί ανάκτησης" diff --git a/features/securebackup/impl/src/main/res/values-et/translations.xml b/features/securebackup/impl/src/main/res/values-et/translations.xml index 2c3aa2d12b..b618432839 100644 --- a/features/securebackup/impl/src/main/res/values-et/translations.xml +++ b/features/securebackup/impl/src/main/res/values-et/translations.xml @@ -39,7 +39,6 @@ "Kinnitamaks ligipääsu sinu vestluse varukoopiale, palun proovi uuesti" "Vigane taastevõti" "Kui sul on turvavõti või turvafraas, siis need toimivad ka." - "Taastevõti või turvafraas" "Sisesta…" "Kas sa oled taastevõtme kaotanud?" "Taastevõti on kinnitatud" diff --git a/features/securebackup/impl/src/main/res/values-fr/translations.xml b/features/securebackup/impl/src/main/res/values-fr/translations.xml index 6656e0a8e3..1b36266924 100644 --- a/features/securebackup/impl/src/main/res/values-fr/translations.xml +++ b/features/securebackup/impl/src/main/res/values-fr/translations.xml @@ -37,7 +37,6 @@ "Veuillez réessayer afin de pouvoir accéder à vos anciens messages." "Clé de récupération incorrecte" "Si vous avez une clé de sécurité ou une phrase de sécurité, cela fonctionnera également." - "Clé de récupération" "Saisissez la clé ici…" "Clé de récupération perdue?" "Clé de récupération confirmée" diff --git a/features/securebackup/impl/src/main/res/values-hu/translations.xml b/features/securebackup/impl/src/main/res/values-hu/translations.xml index 20575540b7..48fe08a31b 100644 --- a/features/securebackup/impl/src/main/res/values-hu/translations.xml +++ b/features/securebackup/impl/src/main/res/values-hu/translations.xml @@ -39,7 +39,6 @@ "Próbálja meg újra megerősíteni a csevegés biztonsági mentéséhez való hozzáférését." "Helytelen helyreállítási kulcs" "Ha van biztonsági kulcsa vagy biztonsági jelmondata, akkor ez is fog működni." - "Helyreállítási kulcs vagy jelkód" "Megadás…" "Elvesztette a helyreállítási kulcsát?" "Helyreállítási kulcs megerősítve" diff --git a/features/securebackup/impl/src/main/res/values-in/translations.xml b/features/securebackup/impl/src/main/res/values-in/translations.xml index 83fa1f8092..62968d3471 100644 --- a/features/securebackup/impl/src/main/res/values-in/translations.xml +++ b/features/securebackup/impl/src/main/res/values-in/translations.xml @@ -33,7 +33,6 @@ "Silakan coba lagi untuk mengonfirmasi akses ke cadangan percakapan Anda." "Kunci pemulihan salah" "Jika Anda memiliki kunci keamanan atau frasa keamanan, ini juga bisa digunakan." - "Kunci pemulihan atau kode sandi" "Masukkan…" "Kehilangan kunci pemulihan Anda?" "Kunci pemulihan dikonfirmasi" diff --git a/features/securebackup/impl/src/main/res/values-it/translations.xml b/features/securebackup/impl/src/main/res/values-it/translations.xml index c1d67299ba..0e069b0ab2 100644 --- a/features/securebackup/impl/src/main/res/values-it/translations.xml +++ b/features/securebackup/impl/src/main/res/values-it/translations.xml @@ -38,7 +38,6 @@ "Riprova per confermare l\'accesso al backup della chat." "Chiave di recupero errata" "Se hai una chiave di sicurezza o una password, andrà bene anche questo." - "Chiave di recupero o codice di accesso" "Inserisci…" "Hai perso la chiave di recupero?" "Chiave di recupero confermata" diff --git a/features/securebackup/impl/src/main/res/values-pl/translations.xml b/features/securebackup/impl/src/main/res/values-pl/translations.xml index eea550627f..d708e18bab 100644 --- a/features/securebackup/impl/src/main/res/values-pl/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pl/translations.xml @@ -38,7 +38,6 @@ "Spróbuj ponownie, aby potwierdzić dostęp do backupu czatu." "Nieprawidłowy klucz przywracania" "To też zadziała, jeśli posiadasz klucz lub frazę bezpieczeństwa." - "Klucz przywracania lub hasło" "Wprowadź…" "Zgubiłeś swój kod przywracania?" "Potwierdzono klucz przywracania" diff --git a/features/securebackup/impl/src/main/res/values-pt/translations.xml b/features/securebackup/impl/src/main/res/values-pt/translations.xml index f93e5dedf4..0e409ea22c 100644 --- a/features/securebackup/impl/src/main/res/values-pt/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pt/translations.xml @@ -38,7 +38,6 @@ "Por favor, tenta novamente para confirmar o acesso à tua cópia de segurança das conversas." "Chave de recuperação incorreta" "Também funciona se tiveres uma chave ou frase de segurança." - "Chave ou código de recuperação" "Inserir…" "Perdeste a tua chave?" "Chave de recuperação confirmada" diff --git a/features/securebackup/impl/src/main/res/values-ro/translations.xml b/features/securebackup/impl/src/main/res/values-ro/translations.xml index 6b2f2e31dc..7266533d78 100644 --- a/features/securebackup/impl/src/main/res/values-ro/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ro/translations.xml @@ -33,7 +33,6 @@ "Vă rugăm să încercați din nou să confirmați accesul la backup." "Cheie de recuperare incorectă" "Dacă aveți o cheie de securitate sau o frază de securitate, aceasta va funcționa și ea." - "Cheie de recuperare sau cod de acces" "Introduceți…" "Ați pierdut cheia de recuperare?" "Cheia de recuperare confirmată" diff --git a/features/securebackup/impl/src/main/res/values-ru/translations.xml b/features/securebackup/impl/src/main/res/values-ru/translations.xml index 8866dc9613..ae45ab45eb 100644 --- a/features/securebackup/impl/src/main/res/values-ru/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ru/translations.xml @@ -60,10 +60,6 @@ "ключ восстановления" "Если у вас есть пароль для восстановления или секретный пароль/ключ, это тоже сработает." - - "Ключ восстановления" - " или пароль" - "Вход…" "Потеряли ключ восстановления?" diff --git a/features/securebackup/impl/src/main/res/values-sk/translations.xml b/features/securebackup/impl/src/main/res/values-sk/translations.xml index 8a088f4245..24d80c6b00 100644 --- a/features/securebackup/impl/src/main/res/values-sk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-sk/translations.xml @@ -39,7 +39,6 @@ "Skúste prosím znova potvrdiť prístup k vašej zálohe konverzácie." "Nesprávny kľúč na obnovenie" "Ak máte bezpečnostný kľúč alebo bezpečnostnú frázu, bude to fungovať tiež." - "Kľúč na obnovenie alebo prístupový kód" "Zadať…" "Stratili ste kľúč na obnovenie?" "Kľúč na obnovu potvrdený" diff --git a/features/securebackup/impl/src/main/res/values-sv/translations.xml b/features/securebackup/impl/src/main/res/values-sv/translations.xml index 3599320cd8..7b2364f450 100644 --- a/features/securebackup/impl/src/main/res/values-sv/translations.xml +++ b/features/securebackup/impl/src/main/res/values-sv/translations.xml @@ -39,7 +39,6 @@ "Vänligen pröva igen för att bekräfta åtkomsten till din chattsäkerhetskopia." "Felaktig återställningsnyckel" "Om du har en säkerhetsnyckel eller säkerhetsfras så funkar den också." - "Återställningsnyckel eller lösenkod" "Ange …" "Blivit av med din återställningsnyckel?" "Återställningsnyckel bekräftad" diff --git a/features/securebackup/impl/src/main/res/values-uk/translations.xml b/features/securebackup/impl/src/main/res/values-uk/translations.xml index 14e47f8bad..7d88980f8b 100644 --- a/features/securebackup/impl/src/main/res/values-uk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-uk/translations.xml @@ -38,7 +38,6 @@ "Будь ласка, спробуйте ще раз, щоб підтвердити доступ до резервної копії чату." "Неправильний ключ відновлення" "Якщо у вас є ключ безпеки або фраза безпеки, це теж спрацює." - "Ключ відновлення або код допуску" "Ввести…" "Загубили ключ відновлення?" "Ключ відновлення підтверджено" diff --git a/features/securebackup/impl/src/main/res/values-zh/translations.xml b/features/securebackup/impl/src/main/res/values-zh/translations.xml index 0b4f12b17a..c8f2b7d9ed 100644 --- a/features/securebackup/impl/src/main/res/values-zh/translations.xml +++ b/features/securebackup/impl/src/main/res/values-zh/translations.xml @@ -38,7 +38,6 @@ "请重试以访问您的聊天备份。" "恢复密钥不正确" "如果您有安全密钥或安全短语,也可以用。" - "恢复密钥或密码" "输入……" "丢失了恢复密钥?" "恢复密钥已确认" diff --git a/features/securebackup/impl/src/main/res/values/localazy.xml b/features/securebackup/impl/src/main/res/values/localazy.xml index f4795c23da..55c6c547f5 100644 --- a/features/securebackup/impl/src/main/res/values/localazy.xml +++ b/features/securebackup/impl/src/main/res/values/localazy.xml @@ -39,7 +39,6 @@ "Please try again to confirm access to your chat backup." "Incorrect recovery key" "If you have a security key or security phrase, this will work too." - "Recovery key or passcode" "Enter…" "Lost your recovery key?" "Recovery key confirmed" diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt index 08c2f352e6..6e42909f0b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt @@ -48,7 +48,7 @@ fun AsyncActionView( ErrorDialog( title = errorTitle(async.error), content = errorMessage(async.error), - onDismiss = onErrorDismiss + onSubmit = onErrorDismiss ) } else { RetryDialog( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt index acef5bf9f9..add7bf2904 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.window.DialogProperties import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup @@ -25,17 +26,23 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun ErrorDialog( content: String, - onDismiss: () -> Unit, + onSubmit: () -> Unit, modifier: Modifier = Modifier, - title: String = ErrorDialogDefaults.title, + title: String? = ErrorDialogDefaults.title, submitText: String = ErrorDialogDefaults.submitText, + onDismiss: () -> Unit = onSubmit, + canDismiss: Boolean = true, ) { - BasicAlertDialog(modifier = modifier, onDismissRequest = onDismiss) { + BasicAlertDialog( + modifier = modifier, + onDismissRequest = onDismiss, + properties = DialogProperties(dismissOnClickOutside = canDismiss, dismissOnBackPress = canDismiss) + ) { ErrorDialogContent( title = title, content = content, submitText = submitText, - onSubmitClick = onDismiss, + onSubmitClick = onSubmit, ) } } @@ -44,7 +51,7 @@ fun ErrorDialog( private fun ErrorDialogContent( content: String, onSubmitClick: () -> Unit, - title: String = ErrorDialogDefaults.title, + title: String? = ErrorDialogDefaults.title, submitText: String = ErrorDialogDefaults.submitText, ) { SimpleAlertDialogContent( @@ -78,6 +85,6 @@ internal fun ErrorDialogContentPreview() { internal fun ErrorDialogPreview() = ElementPreview { ErrorDialog( content = "Content", - onDismiss = {}, + onSubmit = {}, ) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index e45686b1a2..3b7441395a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -131,6 +131,9 @@ interface MatrixClient : Closeable { /** Returns `true` if the home server supports native sliding sync. */ suspend fun isNativeSlidingSyncSupported(): Boolean - /** Returns `true` if the current session is using native sliding sync. */ + /** Returns `true` if the home server supports sliding sync using a proxy. */ + suspend fun isSlidingSyncProxySupported(): Boolean + + /** Returns `true` if the current session is using native sliding sync, `false` if it's using a proxy. */ fun isUsingNativeSlidingSync(): Boolean } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 0fc9f6695b..c9989796e5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -534,6 +534,10 @@ class RustMatrixClient( return client.availableSlidingSyncVersions().contains(SlidingSyncVersion.Native) } + override suspend fun isSlidingSyncProxySupported(): Boolean { + return client.availableSlidingSyncVersions().any { it is SlidingSyncVersion.Proxy } + } + override fun isUsingNativeSlidingSync(): Boolean { return client.session().slidingSyncVersion == SlidingSyncVersion.Native } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt index 7681dda6f0..ad4c2d1549 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -7,7 +7,6 @@ package io.element.android.libraries.matrix.impl -import io.element.android.appconfig.AuthenticationConfig import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -19,12 +18,10 @@ import io.element.android.libraries.matrix.impl.paths.getSessionPaths import io.element.android.libraries.matrix.impl.proxy.ProxyProvider import io.element.android.libraries.matrix.impl.util.anonymizedTokens import io.element.android.libraries.network.useragent.UserAgentProvider -import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.Session @@ -47,7 +44,6 @@ class RustMatrixClientFactory @Inject constructor( private val proxyProvider: ProxyProvider, private val clock: SystemClock, private val utdTracker: UtdTracker, - private val appPreferencesStore: AppPreferencesStore, private val featureFlagService: FeatureFlagService, ) { suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) { @@ -55,7 +51,7 @@ class RustMatrixClientFactory @Inject constructor( val client = getBaseClientBuilder( sessionPaths = sessionData.getSessionPaths(), passphrase = sessionData.passphrase, - restore = true, + slidingSyncType = ClientBuilderSlidingSync.Restored, ) .homeserverUrl(sessionData.homeserverUrl) .username(sessionData.userId) @@ -88,16 +84,8 @@ class RustMatrixClientFactory @Inject constructor( internal suspend fun getBaseClientBuilder( sessionPaths: SessionPaths, passphrase: String?, - restore: Boolean, + slidingSyncType: ClientBuilderSlidingSync, ): ClientBuilder { - val slidingSync = when { - // Always check restore first, since otherwise other values could accidentally override the already persisted config - restore -> ClientBuilderSlidingSync.Restored - AuthenticationConfig.SLIDING_SYNC_PROXY_URL != null -> ClientBuilderSlidingSync.CustomProxy(AuthenticationConfig.SLIDING_SYNC_PROXY_URL!!) - appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first() -> ClientBuilderSlidingSync.Simplified - else -> ClientBuilderSlidingSync.Discovered - } - return ClientBuilder() .sessionPaths( dataPath = sessionPaths.fileDirectory.absolutePath, @@ -117,9 +105,9 @@ class RustMatrixClientFactory @Inject constructor( ) .run { // Apply sliding sync version settings - when (slidingSync) { + when (slidingSyncType) { ClientBuilderSlidingSync.Restored -> this - is ClientBuilderSlidingSync.CustomProxy -> slidingSyncVersionBuilder(SlidingSyncVersionBuilder.Proxy(slidingSync.url)) + is ClientBuilderSlidingSync.CustomProxy -> slidingSyncVersionBuilder(SlidingSyncVersionBuilder.Proxy(slidingSyncType.url)) ClientBuilderSlidingSync.Discovered -> slidingSyncVersionBuilder(SlidingSyncVersionBuilder.DiscoverProxy) ClientBuilderSlidingSync.Simplified -> slidingSyncVersionBuilder(SlidingSyncVersionBuilder.DiscoverNative) ClientBuilderSlidingSync.ForcedSimplified -> slidingSyncVersionBuilder(SlidingSyncVersionBuilder.Native) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index 344ae588a1..47a010cf2d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.impl.auth import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.appconfig.AuthenticationConfig import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.di.AppScope @@ -19,6 +20,7 @@ import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync import io.element.android.libraries.matrix.impl.RustMatrixClientFactory import io.element.android.libraries.matrix.impl.auth.qrlogin.QrErrorMapper import io.element.android.libraries.matrix.impl.auth.qrlogin.SdkQrCodeLoginData @@ -28,6 +30,7 @@ import io.element.android.libraries.matrix.impl.keys.PassphraseGenerator import io.element.android.libraries.matrix.impl.mapper.toSessionData import io.element.android.libraries.matrix.impl.paths.SessionPaths import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory +import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.sessionstorage.api.LoggedInState import io.element.android.libraries.sessionstorage.api.LoginType import io.element.android.libraries.sessionstorage.api.SessionStore @@ -35,9 +38,13 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.Client +import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.HumanQrLoginException +import org.matrix.rustcomponents.sdk.OidcConfiguration +import org.matrix.rustcomponents.sdk.QrCodeData import org.matrix.rustcomponents.sdk.QrCodeDecodeException import org.matrix.rustcomponents.sdk.QrLoginProgress import org.matrix.rustcomponents.sdk.QrLoginProgressListener @@ -55,6 +62,7 @@ class RustMatrixAuthenticationService @Inject constructor( private val rustMatrixClientFactory: RustMatrixClientFactory, private val passphraseGenerator: PassphraseGenerator, private val oidcConfigurationProvider: OidcConfigurationProvider, + private val appPreferencesStore: AppPreferencesStore, ) : MatrixAuthenticationService { // Passphrase which will be used for new sessions. Existing sessions will use the passphrase // stored in the SessionData. @@ -117,9 +125,10 @@ class RustMatrixAuthenticationService @Inject constructor( withContext(coroutineDispatchers.io) { val emptySessionPath = rotateSessionPath() runCatching { - val client = getBaseClientBuilder(emptySessionPath) - .serverNameOrHomeserverUrl(homeserver) - .build() + val client = makeClient(sessionPaths = emptySessionPath) { + serverNameOrHomeserverUrl(homeserver) + } + currentClient = client val homeServerDetails = client.homeserverLoginDetails().map() currentHomeserver.value = homeServerDetails.copy(url = homeserver) @@ -207,23 +216,24 @@ class RustMatrixAuthenticationService @Inject constructor( override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) = withContext(coroutineDispatchers.io) { + val sdkQrCodeLoginData = (qrCodeData as SdkQrCodeLoginData).rustQrCodeData val emptySessionPaths = rotateSessionPath() + val oidcConfiguration = oidcConfigurationProvider.get() + val progressListener = object : QrLoginProgressListener { + override fun onUpdate(state: QrLoginProgress) { + Timber.d("QR Code login progress: $state") + progress(state.toStep()) + } + } runCatching { - val client = rustMatrixClientFactory.getBaseClientBuilder( + val client = makeQrCodeLoginClient( sessionPaths = emptySessionPaths, passphrase = pendingPassphrase, - restore = false, + qrCodeData = sdkQrCodeLoginData, + oidcConfiguration = oidcConfiguration, + progressListener = progressListener, ) - .buildWithQrCode( - qrCodeData = (qrCodeData as SdkQrCodeLoginData).rustQrCodeData, - oidcConfiguration = oidcConfigurationProvider.get(), - progressListener = object : QrLoginProgressListener { - override fun onUpdate(state: QrLoginProgress) { - Timber.d("QR Code login progress: $state") - progress(state.toStep()) - } - } - ) + client.use { rustClient -> val sessionData = rustClient.session() .toSessionData( @@ -249,14 +259,80 @@ class RustMatrixAuthenticationService @Inject constructor( } } - private suspend fun getBaseClientBuilder( + private suspend fun makeClient( sessionPaths: SessionPaths, - ) = rustMatrixClientFactory - .getBaseClientBuilder( - sessionPaths = sessionPaths, - passphrase = pendingPassphrase, - restore = false, - ) + config: suspend ClientBuilder.() -> ClientBuilder, + ): Client { + val slidingSyncType = getSlidingSyncType() + if (slidingSyncType is ClientBuilderSlidingSync.Simplified) { + Timber.d("Creating client with simplified sliding sync") + try { + return rustMatrixClientFactory + .getBaseClientBuilder( + sessionPaths = sessionPaths, + passphrase = pendingPassphrase, + slidingSyncType = slidingSyncType, + ) + .run { config() } + .build() + } catch (e: HumanQrLoginException.SlidingSyncNotAvailable) { + Timber.e(e, "Failed to create client with simplified sliding sync, trying with Proxy now") + } + } + Timber.d("Creating client with Proxy sliding sync") + return rustMatrixClientFactory + .getBaseClientBuilder( + sessionPaths = sessionPaths, + passphrase = pendingPassphrase, + slidingSyncType = getSlidingSyncProxy(), + ) + .run { config() } + .build() + } + + private suspend fun makeQrCodeLoginClient( + sessionPaths: SessionPaths, + passphrase: String?, + qrCodeData: QrCodeData, + oidcConfiguration: OidcConfiguration, + progressListener: QrLoginProgressListener, + ): Client { + val slidingSyncType = getSlidingSyncType() + if (slidingSyncType is ClientBuilderSlidingSync.Simplified) { + Timber.d("Creating client for QR Code login with simplified sliding sync") + try { + return rustMatrixClientFactory + .getBaseClientBuilder( + sessionPaths = sessionPaths, + passphrase = pendingPassphrase, + slidingSyncType = slidingSyncType, + ) + .passphrase(passphrase) + .buildWithQrCode(qrCodeData, oidcConfiguration, progressListener) + } catch (e: HumanQrLoginException.SlidingSyncNotAvailable) { + Timber.e(e, "Failed to create client with simplified sliding sync, trying with Proxy now") + } + } + Timber.d("Creating client for QR Code login with Proxy sliding sync") + return rustMatrixClientFactory + .getBaseClientBuilder( + sessionPaths = sessionPaths, + passphrase = pendingPassphrase, + slidingSyncType = getSlidingSyncProxy(), + ) + .passphrase(passphrase) + .buildWithQrCode(qrCodeData, oidcConfiguration, progressListener) + } + + private suspend fun getSlidingSyncType(nativeSlidingSyncFailed: Boolean = false) = when { + appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first() && !nativeSlidingSyncFailed -> ClientBuilderSlidingSync.Simplified + else -> getSlidingSyncProxy() + } + + private fun getSlidingSyncProxy() = when { + AuthenticationConfig.SLIDING_SYNC_PROXY_URL != null -> ClientBuilderSlidingSync.CustomProxy(AuthenticationConfig.SLIDING_SYNC_PROXY_URL!!) + else -> ClientBuilderSlidingSync.Discovered + } private fun clear() { currentClient?.close() diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index ed484f4426..5db394f77d 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -79,6 +79,7 @@ class FakeMatrixClient( private val userIdServerNameLambda: () -> String = { lambdaError() }, private val getUrlLambda: (String) -> Result = { lambdaError() }, var isNativeSlidingSyncSupportedLambda: suspend () -> Boolean = { true }, + var isSlidingSyncProxySupportedLambda: suspend () -> Boolean = { true }, var isUsingNativeSlidingSyncLambda: () -> Boolean = { true }, ) : MatrixClient { var setDisplayNameCalled: Boolean = false @@ -324,6 +325,10 @@ class FakeMatrixClient( return isNativeSlidingSyncSupportedLambda() } + override suspend fun isSlidingSyncProxySupported(): Boolean { + return isSlidingSyncProxySupportedLambda() + } + override fun isUsingNativeSlidingSync(): Boolean { return isUsingNativeSlidingSyncLambda() } diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt index 8e09547fb4..ab985e798d 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt @@ -87,7 +87,7 @@ class DefaultAppPreferencesStore @Inject constructor( override fun isSimplifiedSlidingSyncEnabledFlow(): Flow { return store.data.map { prefs -> - prefs[simplifiedSlidingSyncKey] ?: false + prefs[simplifiedSlidingSyncKey] ?: true } } diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index f7c3213ba4..b4fec91a0c 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -36,6 +36,7 @@ "Back" "Call" "Cancel" + "Cancel for now" "Choose photo" "Clear" "Close" @@ -283,6 +284,12 @@ Reason: %1$s." "Pinned messages" "You\'re about to go to your %1$s account to reset your identity. Afterwards you\'ll be taken back to the app." "Can\'t confirm? Go to your account to reset your identity." + "Withdraw verification and send" + "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$s." + "Your message was not sent because %1$s’s verified identity has changed" + "Send message anyway" + "%1$s is using one or more unverified devices. You can send the message anyway, or you can cancel for now and try again later after %2$s has verified all their devices." + "Your message was not sent because %1$s has not verified one or more devices" "Pinned messages" "Failed processing media to upload, please try again." "Could not retrieve user details" @@ -304,6 +311,8 @@ Reason: %1$s." "Open in Google Maps" "Open in OpenStreetMap" "Share this location" + "Message not sent because %1$s’s verified identity has changed." + "Message not sent because %1$s has not verified one or more devices." "Location" "Version: %1$s (%2$s)" "en" diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt index 5319baf10e..19e1390f47 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt @@ -55,11 +55,11 @@ class MainActivity : ComponentActivity() { proxyProvider = proxyProvider, clock = DefaultSystemClock(), utdTracker = UtdTracker(NoopAnalyticsService()), - appPreferencesStore = InMemoryAppPreferencesStore(), featureFlagService = AlwaysEnabledFeatureFlagService(), ), passphraseGenerator = NullPassphraseGenerator(), oidcConfigurationProvider = OidcConfigurationProvider(baseDirectory), + appPreferencesStore = InMemoryAppPreferencesStore(), ) } diff --git a/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt b/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt index 80d3de2dfe..2bd46eeb00 100644 --- a/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt +++ b/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt @@ -36,7 +36,7 @@ private fun AppErrorViewContent( ErrorDialog( title = title, content = body, - onDismiss = onDismiss, + onSubmit = onDismiss, ) } diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_3_en.png new file mode 100644 index 0000000000..e625c84b24 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98f68bcb22f867add02028ec43cc9b609d8cd76ace450eec1f010899b3f29445 +size 24728 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_3_en.png new file mode 100644 index 0000000000..6d9414c34a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48e881658f3627275743a5f5cf172f312d26ea0633fa84243da4b277b664f66b +size 22753 diff --git a/tools/localazy/config.json b/tools/localazy/config.json index f736825725..6f41be8693 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -1,5 +1,12 @@ { "modules" : [ + { + "name" : ":appnav", + "includeRegex" : [ + "banner\\.migrate_to_native_sliding_sync\\.force_logout.title", + "banner\\.migrate_to_native_sliding_sync\\.action" + ] + }, { "name" : ":features:rageshake:impl", "includeRegex" : [