Skip to content

Commit 8b97d50

Browse files
authored
Merge pull request #920 from vector-im/feature/bma/slidingSyncState
Sliding sync state rendering
2 parents 4162d16 + 0ed7afc commit 8b97d50

File tree

29 files changed

+200
-32
lines changed

29 files changed

+200
-32
lines changed

app/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@ dependencies {
198198
allLibrariesImpl()
199199
allServicesImpl()
200200
allFeaturesImpl(rootDir, logger)
201-
implementation(projects.tests.uitests)
202201
implementation(projects.anvilannotations)
203202
implementation(projects.appnav)
204203
anvil(projects.anvilcodegen)

appnav/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ dependencies {
5151
implementation(projects.libraries.permissions.api)
5252
implementation(projects.libraries.permissions.noop)
5353

54-
implementation(projects.tests.uitests)
5554
implementation(libs.coil)
5655

5756
implementation(projects.features.ftue.api)

appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ class LoggedInFlowNode @AssistedInject constructor(
164164
syncService.syncState,
165165
networkMonitor.connectivity
166166
) { syncState, networkStatus ->
167-
syncState == SyncState.InError && networkStatus == NetworkStatus.Online
167+
syncState == SyncState.Error && networkStatus == NetworkStatus.Online
168168
}
169169
.distinctUntilChanged()
170170
.collect { restartSync ->

appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import android.Manifest
2020
import android.os.Build
2121
import androidx.compose.runtime.Composable
2222
import androidx.compose.runtime.LaunchedEffect
23+
import androidx.compose.runtime.collectAsState
2324
import io.element.android.libraries.architecture.Presenter
2425
import io.element.android.libraries.matrix.api.MatrixClient
2526
import io.element.android.libraries.permissions.api.PermissionsPresenter
@@ -52,6 +53,7 @@ class LoggedInPresenter @Inject constructor(
5253
pushService.registerWith(matrixClient, pushProvider, distributor)
5354
}
5455

56+
val syncState = matrixClient.syncService().syncState.collectAsState()
5557
val permissionsState = postNotificationPermissionsPresenter.present()
5658

5759
// fun handleEvents(event: LoggedInEvents) {
@@ -60,6 +62,7 @@ class LoggedInPresenter @Inject constructor(
6062
// }
6163

6264
return LoggedInState(
65+
syncState = syncState.value,
6366
permissionsState = permissionsState,
6467
// eventSink = ::handleEvents
6568
)

appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616

1717
package io.element.android.appnav.loggedin
1818

19+
import io.element.android.libraries.matrix.api.sync.SyncState
1920
import io.element.android.libraries.permissions.api.PermissionsState
2021

2122
data class LoggedInState(
23+
val syncState: SyncState,
2224
val permissionsState: PermissionsState,
2325
// val eventSink: (LoggedInEvents) -> Unit
2426
)

appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,22 @@
1717
package io.element.android.appnav.loggedin
1818

1919
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
20+
import io.element.android.libraries.matrix.api.sync.SyncState
2021
import io.element.android.libraries.permissions.api.createDummyPostNotificationPermissionsState
2122

2223
open class LoggedInStateProvider : PreviewParameterProvider<LoggedInState> {
2324
override val values: Sequence<LoggedInState>
2425
get() = sequenceOf(
2526
aLoggedInState(),
27+
aLoggedInState(syncState = SyncState.Idle),
2628
// Add other state here
2729
)
2830
}
2931

30-
fun aLoggedInState() = LoggedInState(
32+
fun aLoggedInState(
33+
syncState: SyncState = SyncState.Running,
34+
) = LoggedInState(
35+
syncState = syncState,
3136
permissionsState = createDummyPostNotificationPermissionsState(),
3237
// eventSink = {}
3338
)

appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,19 @@
1616

1717
package io.element.android.appnav.loggedin
1818

19+
import androidx.compose.foundation.layout.Box
20+
import androidx.compose.foundation.layout.fillMaxSize
21+
import androidx.compose.foundation.layout.padding
22+
import androidx.compose.foundation.layout.systemBarsPadding
1923
import androidx.compose.runtime.Composable
24+
import androidx.compose.ui.Alignment
2025
import androidx.compose.ui.Modifier
2126
import androidx.compose.ui.platform.LocalContext
22-
import androidx.compose.ui.tooling.preview.Preview
2327
import androidx.compose.ui.tooling.preview.PreviewParameter
28+
import androidx.compose.ui.unit.dp
2429
import io.element.android.libraries.androidutils.system.openAppSettingsPage
25-
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
26-
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
30+
import io.element.android.libraries.designsystem.preview.DayNightPreviews
31+
import io.element.android.libraries.designsystem.preview.ElementPreview
2732
import io.element.android.libraries.permissions.api.PermissionsView
2833

2934
@Composable
@@ -33,25 +38,27 @@ fun LoggedInView(
3338
) {
3439
val context = LocalContext.current
3540

36-
PermissionsView(
37-
state = state.permissionsState,
38-
modifier = modifier,
39-
openSystemSettings = context::openAppSettingsPage
40-
)
41+
Box(
42+
modifier = modifier
43+
.fillMaxSize()
44+
.systemBarsPadding()
45+
) {
46+
SyncStateView(
47+
modifier = Modifier
48+
.padding(top = 8.dp)
49+
.align(Alignment.TopCenter),
50+
syncState = state.syncState,
51+
)
52+
PermissionsView(
53+
state = state.permissionsState,
54+
openSystemSettings = context::openAppSettingsPage
55+
)
56+
}
4157
}
4258

43-
@Preview
44-
@Composable
45-
fun LoggedInViewLightPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) =
46-
ElementPreviewLight { ContentToPreview(state) }
47-
48-
@Preview
49-
@Composable
50-
fun LoggedInViewDarkPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) =
51-
ElementPreviewDark { ContentToPreview(state) }
52-
59+
@DayNightPreviews
5360
@Composable
54-
private fun ContentToPreview(state: LoggedInState) {
61+
fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview {
5562
LoggedInView(
5663
state = state
5764
)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.appnav.loggedin
18+
19+
import androidx.compose.animation.AnimatedVisibility
20+
import androidx.compose.animation.core.spring
21+
import androidx.compose.animation.fadeIn
22+
import androidx.compose.animation.fadeOut
23+
import androidx.compose.foundation.background
24+
import androidx.compose.foundation.layout.Arrangement
25+
import androidx.compose.foundation.layout.Box
26+
import androidx.compose.foundation.layout.Row
27+
import androidx.compose.foundation.layout.padding
28+
import androidx.compose.foundation.layout.size
29+
import androidx.compose.foundation.progressSemantics
30+
import androidx.compose.foundation.shape.RoundedCornerShape
31+
import androidx.compose.runtime.Composable
32+
import androidx.compose.ui.Alignment
33+
import androidx.compose.ui.Modifier
34+
import androidx.compose.ui.res.stringResource
35+
import androidx.compose.ui.unit.dp
36+
import io.element.android.libraries.designsystem.preview.DayNightPreviews
37+
import io.element.android.libraries.designsystem.preview.ElementPreview
38+
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
39+
import io.element.android.libraries.designsystem.theme.components.Surface
40+
import io.element.android.libraries.designsystem.theme.components.Text
41+
import io.element.android.libraries.matrix.api.sync.SyncState
42+
import io.element.android.libraries.theme.ElementTheme
43+
import io.element.android.libraries.ui.strings.CommonStrings
44+
45+
@Composable
46+
fun SyncStateView(
47+
syncState: SyncState,
48+
modifier: Modifier = Modifier
49+
) {
50+
val animationSpec = spring<Float>(stiffness = 500F)
51+
AnimatedVisibility(
52+
modifier = modifier,
53+
visible = syncState.mustBeVisible(),
54+
enter = fadeIn(animationSpec = animationSpec),
55+
exit = fadeOut(animationSpec = animationSpec),
56+
) {
57+
Surface(
58+
shape = RoundedCornerShape(24.dp),
59+
shadowElevation = 8.dp,
60+
) {
61+
Row(
62+
modifier = Modifier
63+
.background(color = ElementTheme.colors.bgSubtleSecondary)
64+
.padding(horizontal = 24.dp, vertical = 10.dp),
65+
verticalAlignment = Alignment.CenterVertically,
66+
horizontalArrangement = Arrangement.spacedBy(10.dp)
67+
) {
68+
CircularProgressIndicator(
69+
modifier = Modifier
70+
.progressSemantics()
71+
.size(12.dp),
72+
color = ElementTheme.colors.textPrimary,
73+
strokeWidth = 1.5.dp,
74+
)
75+
Text(
76+
text = stringResource(id = CommonStrings.common_syncing),
77+
color = ElementTheme.colors.textPrimary,
78+
style = ElementTheme.typography.fontBodyMdMedium
79+
)
80+
}
81+
}
82+
}
83+
}
84+
85+
private fun SyncState.mustBeVisible() = when (this) {
86+
SyncState.Idle -> true /* Cold start of the app */
87+
SyncState.Running -> false
88+
SyncState.Error -> false /* In this case, the network error banner can be displayed */
89+
SyncState.Terminated -> true /* The app is resumed and the sync is started again */
90+
}
91+
92+
@DayNightPreviews
93+
@Composable
94+
fun SyncStateViewPreview() = ElementPreview {
95+
// Add a box to see the shadow
96+
Box(modifier = Modifier.padding(24.dp)) {
97+
SyncStateView(
98+
syncState = SyncState.Idle
99+
)
100+
}
101+
}

libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncState.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package io.element.android.libraries.matrix.api.sync
1818

1919
enum class SyncState {
2020
Idle,
21-
Syncing,
22-
InError,
21+
Running,
22+
Error,
2323
Terminated,
2424
}

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ class RustMatrixClient constructor(
144144
client.setDelegate(clientDelegate)
145145
rustSyncService.syncState
146146
.onEach { syncState ->
147-
if (syncState == SyncState.Syncing) {
147+
if (syncState == SyncState.Running) {
148148
onSlidingSyncUpdate()
149149
}
150150
}.launchIn(sessionCoroutineScope)

0 commit comments

Comments
 (0)