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 f6faee0acf..f2f6e1312f 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 @@ -105,9 +105,18 @@ class LoggedInPresenter( } val syncIndicator by matrixClient.roomListService.syncIndicator.collectAsState() val isOnline by syncService.isOnline.collectAsState() - val showSyncSpinner by remember { + val hasNetwork by networkMonitor.connectivity.collectAsState() + val syncIndicatorState by remember { derivedStateOf { - isOnline && syncIndicator == RoomListService.SyncIndicator.Show + when { + isOnline -> if (syncIndicator == RoomListService.SyncIndicator.Show) { + SyncIndicatorState.Syncing + } else { + SyncIndicatorState.Hidden + } + hasNetwork == NetworkStatus.Connected -> SyncIndicatorState.ServerUnreachable + else -> SyncIndicatorState.Hidden + } } } var forceNativeSlidingSyncMigration by remember { mutableStateOf(false) } @@ -181,7 +190,7 @@ class LoggedInPresenter( } return LoggedInState( - showSyncSpinner = showSyncSpinner, + syncIndicatorState = syncIndicatorState, pusherRegistrationState = pusherRegistrationState.value, ignoreRegistrationError = ignoreRegistrationError, forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration, 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 33499341e7..8ac675a146 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 @@ -11,8 +11,14 @@ package io.element.android.appnav.loggedin import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionDialog +enum class SyncIndicatorState { + Hidden, + Syncing, + ServerUnreachable, +} + data class LoggedInState( - val showSyncSpinner: Boolean, + val syncIndicatorState: SyncIndicatorState, val pusherRegistrationState: AsyncData, val ignoreRegistrationError: Boolean, val forceNativeSlidingSyncMigration: Boolean, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStatePreviewParam.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStatePreviewParam.kt index b479e00279..705f6fbe61 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStatePreviewParam.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStatePreviewParam.kt @@ -17,7 +17,8 @@ open class LoggedInStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLoggedInState(), - aLoggedInState(showSyncSpinner = true), + aLoggedInState(syncIndicatorState = SyncIndicatorState.Syncing), + aLoggedInState(syncIndicatorState = SyncIndicatorState.ServerUnreachable), aLoggedInState(pusherRegistrationState = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable())), aLoggedInState(forceNativeSlidingSyncMigration = true), aLoggedInState(localNetworkPermissionDialog = LocalNetworkPermissionDialog.Rationale), @@ -26,13 +27,13 @@ open class LoggedInStatePreviewParam : PreviewParameterProvider { } fun aLoggedInState( - showSyncSpinner: Boolean = false, + syncIndicatorState: SyncIndicatorState = SyncIndicatorState.Hidden, pusherRegistrationState: AsyncData = AsyncData.Uninitialized, forceNativeSlidingSyncMigration: Boolean = false, appName: String = "Element X", localNetworkPermissionDialog: LocalNetworkPermissionDialog = LocalNetworkPermissionDialog.None, ) = LoggedInState( - showSyncSpinner = showSyncSpinner, + syncIndicatorState = syncIndicatorState, pusherRegistrationState = pusherRegistrationState, ignoreRegistrationError = false, forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration, 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 060a24855a..1d3a355663 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 @@ -47,7 +47,7 @@ fun LoggedInView( ) { SyncStateView( modifier = Modifier.align(Alignment.TopCenter), - isVisible = state.showSyncSpinner, + state = state.syncIndicatorState, ) } when (state.pusherRegistrationState) { diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt index 7c5f196a4c..db39658ad4 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt @@ -25,18 +25,23 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun SyncStateView( - isVisible: Boolean, + state: SyncIndicatorState, modifier: Modifier = Modifier ) { AnimatedVisibility( - visible = isVisible, + visible = state != SyncIndicatorState.Hidden, modifier = modifier, enter = fadeIn(spring(stiffness = 500F)), exit = fadeOut(spring(stiffness = 500F)), ) { - AsyncIndicator.Loading( - text = stringResource(id = CommonStrings.common_syncing), - ) + when (state) { + SyncIndicatorState.ServerUnreachable -> AsyncIndicator.Failure( + text = stringResource(id = CommonStrings.common_server_unreachable), + ) + else -> AsyncIndicator.Loading( + text = stringResource(id = CommonStrings.common_syncing), + ) + } } } @@ -46,7 +51,18 @@ internal fun SyncStateViewPreview() = ElementPreview { // Add a box to see the shadow Box(modifier = Modifier.padding(24.dp)) { SyncStateView( - isVisible = true + state = SyncIndicatorState.Syncing + ) + } +} + +@PreviewsDayNight +@Composable +internal fun SyncStateViewServerUnreachablePreview() = ElementPreview { + // Add a box to see the shadow + Box(modifier = Modifier.padding(24.dp)) { + SyncStateView( + state = SyncIndicatorState.ServerUnreachable ) } } 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 f85d35bbb5..474253c8df 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 @@ -67,7 +67,7 @@ class LoggedInPresenterTest { fun `present - initial state`() = runTest { createLoggedInPresenter().test { val initialState = awaitItem() - assertThat(initialState.showSyncSpinner).isFalse() + assertThat(initialState.syncIndicatorState).isEqualTo(SyncIndicatorState.Hidden) assertThat(initialState.pusherRegistrationState.isUninitialized()).isTrue() assertThat(initialState.ignoreRegistrationError).isFalse() } @@ -97,11 +97,33 @@ class LoggedInPresenterTest { matrixClient = FakeMatrixClient(roomListService = roomListService), ).test { val initialState = awaitItem() - assertThat(initialState.showSyncSpinner).isFalse() + assertThat(initialState.syncIndicatorState).isEqualTo(SyncIndicatorState.Hidden) roomListService.postSyncIndicator(RoomListService.SyncIndicator.Show) - consumeItemsUntilPredicate { it.showSyncSpinner } + consumeItemsUntilPredicate { it.syncIndicatorState == SyncIndicatorState.Syncing } roomListService.postSyncIndicator(RoomListService.SyncIndicator.Hide) - consumeItemsUntilPredicate { !it.showSyncSpinner } + consumeItemsUntilPredicate { it.syncIndicatorState != SyncIndicatorState.Syncing } + } + } + + @Test + fun `present - say the server is unreachable when the device has network but the sync is offline`() = runTest { + createLoggedInPresenter( + syncState = SyncState.Offline, + networkMonitor = FakeNetworkMonitor(initialStatus = NetworkStatus.Connected), + ).test { + consumeItemsUntilPredicate { it.syncIndicatorState == SyncIndicatorState.ServerUnreachable } + cancelAndConsumeRemainingEvents() + } + } + + @Test + fun `present - say nothing when the device itself has no network`() = runTest { + createLoggedInPresenter( + syncState = SyncState.Offline, + networkMonitor = FakeNetworkMonitor(initialStatus = NetworkStatus.Disconnected), + ).test { + consumeItemsUntilPredicate { it.syncIndicatorState == SyncIndicatorState.Hidden } + cancelAndConsumeRemainingEvents() } } diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt index e066a6b7ec..ed901a99b4 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt @@ -133,6 +133,7 @@ class KonsistPreviewTest { "SecureBackupSetupViewChangePreview", "SelectedUserCannotRemovePreview", "SpaceMembersViewNoHeroesPreview", + "SyncStateViewServerUnreachablePreview", "TextComposerAddCaptionPreview", "TextComposerCaptionPreview", "TextComposerEditCaptionPreview", diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_2_en.png index 6082c07349..ae687af6c7 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:062413bedff67d6264f7c498db69e38babdfad4cc5b5c0a581f9031dcd03ae10 -size 36968 +oid sha256:fdce2c0a9c41b04870e79cc5e0413bf09ddea7907650d798cc4206ece881933f +size 9782 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 index ad2925d791..6082c07349 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ed6c634f81fca7a0d8cf242f294d37fe44b4dd5f0832c1bb1e29df245f06546 -size 24161 +oid sha256:062413bedff67d6264f7c498db69e38babdfad4cc5b5c0a581f9031dcd03ae10 +size 36968 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png index 5adfe35003..ad2925d791 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:965cd4692e88a413e5e0a83e16a190386e8f199716fdd7288f4e05b8c623a8b2 -size 33756 +oid sha256:8ed6c634f81fca7a0d8cf242f294d37fe44b4dd5f0832c1bb1e29df245f06546 +size 24161 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png index bf9aed6830..5adfe35003 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:771c01d4563687aff208b0f279c9fb98192b0cd2b2ba56d43627f6c785e92b90 -size 32956 +oid sha256:965cd4692e88a413e5e0a83e16a190386e8f199716fdd7288f4e05b8c623a8b2 +size 33756 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_6_en.png new file mode 100644 index 0000000000..bf9aed6830 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:771c01d4563687aff208b0f279c9fb98192b0cd2b2ba56d43627f6c785e92b90 +size 32956 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_2_en.png index 72eca81304..5439563841 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e612d46ec9d5234d26768bf68f7f58877f9344858184bd54c6d6be893e972fa -size 34955 +oid sha256:5b11f9ba2fda961d1571b46c1bcbd7a7ccde6efb4a320ead276779c391421223 +size 8543 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 index 8e38bbe539..72eca81304 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb9ef99adff85610ec2808bc468a5f8161db436283e51bde77fc2d6f5b61e214 -size 22502 +oid sha256:8e612d46ec9d5234d26768bf68f7f58877f9344858184bd54c6d6be893e972fa +size 34955 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png index 75fdd8172d..8e38bbe539 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f2d01f11bcca2ba3465b6308f7368797c6fd9712bf90f4e9493b30dbece8277 -size 31907 +oid sha256:bb9ef99adff85610ec2808bc468a5f8161db436283e51bde77fc2d6f5b61e214 +size 22502 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png index 28fd0cfc21..75fdd8172d 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:484173d52fcb0acff42f62d28f88cd544e205bde49370ccc8230c30b193e1959 -size 31173 +oid sha256:8f2d01f11bcca2ba3465b6308f7368797c6fd9712bf90f4e9493b30dbece8277 +size 31907 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_6_en.png new file mode 100644 index 0000000000..28fd0cfc21 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:484173d52fcb0acff42f62d28f88cd544e205bde49370ccc8230c30b193e1959 +size 31173 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Day_0_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Day_0_en.png new file mode 100644 index 0000000000..d1e672507d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:433fd969f604ec432cc0b324109c5aa3f60abb44c692c6965731314c66377924 +size 10200 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Night_0_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Night_0_en.png new file mode 100644 index 0000000000..95c1b90315 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab1c87fe0b7d5c7e5f1ccad62fba68c2a12607bad342046e58ae11abe981a383 +size 8834