From 44f0c23149b6f6068b17a93eddbb92bcfb16017f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 7 Apr 2025 11:58:02 +0200 Subject: [PATCH] Add a inderminate progress bar when loging out and in Waiting state. (#4538) * Add a check network connection when the Waiting state last too long. * Update screenshots --------- Co-authored-by: ElementBot --- .../features/logout/impl/LogoutPresenter.kt | 13 +++++ .../features/logout/impl/LogoutState.kt | 1 + .../logout/impl/LogoutStateProvider.kt | 11 ++++ .../features/logout/impl/LogoutView.kt | 51 ++++++++++++------- .../logout/impl/LogoutPresenterTest.kt | 29 +++++++++++ ...tures.logout.impl_LogoutView_Day_10_en.png | 3 ++ ...tures.logout.impl_LogoutView_Day_11_en.png | 3 ++ ...res.logout.impl_LogoutView_Night_10_en.png | 3 ++ ...res.logout.impl_LogoutView_Night_11_en.png | 3 ++ 9 files changed, 100 insertions(+), 17 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_10_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_11_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_10_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_11_en.png diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt index c26f3a7407..3d1cacd89c 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt @@ -15,6 +15,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 io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -25,6 +26,7 @@ import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.BackupUploadState import io.element.android.libraries.matrix.api.encryption.EncryptionService import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import javax.inject.Inject @@ -44,6 +46,16 @@ class LogoutPresenter @Inject constructor( } .collectAsState(initial = BackupUploadState.Unknown) + var waitingForALongTime by remember { mutableStateOf(false) } + LaunchedEffect(backupUploadState) { + if (backupUploadState is BackupUploadState.Waiting) { + delay(2_000) + waitingForALongTime = true + } else { + waitingForALongTime = false + } + } + val isLastDevice by encryptionService.isLastDevice.collectAsState() val backupState by encryptionService.backupStateStateFlow.collectAsState() val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() @@ -79,6 +91,7 @@ class LogoutPresenter @Inject constructor( doesBackupExistOnServer = doesBackupExistOnServerAction.value.dataOrNull().orTrue(), recoveryState = recoveryState, backupUploadState = backupUploadState, + waitingForALongTime = waitingForALongTime, logoutAction = logoutAction.value, eventSink = ::handleEvents ) diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt index 2f84c837c6..584d7a0ac9 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt @@ -18,6 +18,7 @@ data class LogoutState( val doesBackupExistOnServer: Boolean, val recoveryState: RecoveryState, val backupUploadState: BackupUploadState, + val waitingForALongTime: Boolean, val logoutAction: AsyncAction, val eventSink: (LogoutEvents) -> Unit, ) diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStateProvider.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStateProvider.kt index 12e83fc713..a55a9afb96 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStateProvider.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStateProvider.kt @@ -29,6 +29,15 @@ open class LogoutStateProvider : PreviewParameterProvider { aLogoutState(isLastDevice = true, recoveryState = RecoveryState.DISABLED), // Last session no backup aLogoutState(isLastDevice = true, backupState = BackupState.UNKNOWN, doesBackupExistOnServer = false), + aLogoutState( + isLastDevice = false, + backupUploadState = BackupUploadState.Waiting, + ), + aLogoutState( + isLastDevice = false, + backupUploadState = BackupUploadState.Waiting, + waitingForALongTime = true, + ), ) } @@ -38,6 +47,7 @@ fun aLogoutState( doesBackupExistOnServer: Boolean = true, recoveryState: RecoveryState = RecoveryState.ENABLED, backupUploadState: BackupUploadState = BackupUploadState.Unknown, + waitingForALongTime: Boolean = false, logoutAction: AsyncAction = AsyncAction.Uninitialized, eventSink: (LogoutEvents) -> Unit = {}, ) = LogoutState( @@ -46,6 +56,7 @@ fun aLogoutState( doesBackupExistOnServer = doesBackupExistOnServer, recoveryState = recoveryState, backupUploadState = backupUploadState, + waitingForALongTime = waitingForALongTime, logoutAction = logoutAction, eventSink = eventSink, ) diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt index f8e5a428c2..321879b009 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt @@ -143,24 +143,41 @@ private fun ColumnScope.Buttons( @Composable private fun Content( state: LogoutState, + modifier: Modifier = Modifier, ) { - if (state.backupUploadState is BackupUploadState.Uploading) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(top = 60.dp, start = 20.dp, end = 20.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - LinearProgressIndicator( - modifier = Modifier.fillMaxWidth(), - progress = { state.backupUploadState.backedUpCount.toFloat() / state.backupUploadState.totalCount.toFloat() }, - trackColor = ElementTheme.colors.progressIndicatorTrackColor, - ) - Text( - modifier = Modifier.align(Alignment.End), - text = "${state.backupUploadState.backedUpCount} / ${state.backupUploadState.totalCount}", - style = ElementTheme.typography.fontBodySmRegular, - ) + Column( + modifier = modifier + .fillMaxWidth() + .padding(top = 60.dp, start = 20.dp, end = 20.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + when (state.backupUploadState) { + is BackupUploadState.Uploading -> { + LinearProgressIndicator( + modifier = Modifier.fillMaxWidth(), + progress = { state.backupUploadState.backedUpCount.toFloat() / state.backupUploadState.totalCount.toFloat() }, + trackColor = ElementTheme.colors.progressIndicatorTrackColor, + ) + Text( + modifier = Modifier.align(Alignment.End), + text = "${state.backupUploadState.backedUpCount} / ${state.backupUploadState.totalCount}", + style = ElementTheme.typography.fontBodySmRegular, + ) + } + BackupUploadState.Waiting -> { + LinearProgressIndicator( + modifier = Modifier.fillMaxWidth(), + trackColor = ElementTheme.colors.progressIndicatorTrackColor, + ) + if (state.waitingForALongTime) { + Text( + modifier = Modifier.align(Alignment.CenterHorizontally), + text = stringResource(CommonStrings.common_please_check_internet_connection), + style = ElementTheme.typography.fontBodySmRegular, + ) + } + } + else -> Unit } } } diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt index d054556f87..61a1f1371d 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt @@ -44,6 +44,7 @@ class LogoutPresenterTest { assertThat(initialState.doesBackupExistOnServer).isTrue() assertThat(initialState.recoveryState).isEqualTo(RecoveryState.UNKNOWN) assertThat(initialState.backupUploadState).isEqualTo(BackupUploadState.Unknown) + assertThat(initialState.waitingForALongTime).isFalse() assertThat(initialState.logoutAction).isEqualTo(AsyncAction.Uninitialized) } } @@ -66,6 +67,34 @@ class LogoutPresenterTest { } } + @Test + fun `present - initial state - waiting a long time`() = runTest { + val encryptionService = FakeEncryptionService() + encryptionService.givenWaitForBackupUploadSteadyStateFlow( + flow { + emit(BackupUploadState.Waiting) + delay(3_000) + } + ) + val presenter = createLogoutPresenter( + encryptionService = encryptionService + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.waitingForALongTime).isFalse() + assertThat(initialState.backupUploadState).isEqualTo(BackupUploadState.Unknown) + val waitingState = awaitItem() + assertThat(waitingState.backupUploadState).isEqualTo(BackupUploadState.Waiting) + assertThat(initialState.waitingForALongTime).isFalse() + skipItems(1) + val waitingALongTimeState = awaitItem() + assertThat(waitingALongTimeState.backupUploadState).isEqualTo(BackupUploadState.Waiting) + assertThat(waitingALongTimeState.waitingForALongTime).isTrue() + } + } + @Test fun `present - initial state - backing up`() = runTest { val encryptionService = FakeEncryptionService() diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_10_en.png new file mode 100644 index 0000000000..b159547818 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_10_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b005a634331a485e2f80fb773a147207c782eaf2d923d1a87c5b45597309c658 +size 25794 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_11_en.png new file mode 100644 index 0000000000..e6d14a0cd4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_11_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d800ec0c02e8e2f8fff755d12d2e657d6fbae10a8b9f070bb627b3ccf9d3e159 +size 30566 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_10_en.png new file mode 100644 index 0000000000..9e60f85a51 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_10_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27587909106650e33d7bc7854c0f2dd7ca6e2dad0aaf6487bf266753288ec6f6 +size 24898 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_11_en.png new file mode 100644 index 0000000000..960535f462 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_11_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e65635400bfda5d242d07ebe31113e3ef2d039f21208421023f099997d5e4f9 +size 29603