Skip to content

Commit 0412040

Browse files
committed
[Link new device] Improve UI transition between QRCodes.
1 parent 289d4d0 commit 0412040

4 files changed

Lines changed: 99 additions & 28 deletions

File tree

features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import io.element.android.libraries.architecture.Presenter
2222
import io.element.android.libraries.core.log.logger.LoggerTag
2323
import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileStep
2424
import io.element.android.libraries.matrix.api.logs.LoggerTags
25+
import kotlinx.coroutines.Job
26+
import kotlinx.coroutines.delay
27+
import kotlinx.coroutines.launch
2528
import timber.log.Timber
2629

2730
private val tag = LoggerTag("ShowQrCodePresenter", LoggerTags.linkNewDevice)
@@ -36,20 +39,54 @@ class ShowQrCodePresenter(
3639
fun create(initialData: String): ShowQrCodePresenter
3740
}
3841

42+
private var loadingJob: Job? = null
43+
3944
@Composable
4045
override fun present(): ShowQrCodeState {
4146
var qrCodeRotationCounter by remember { mutableIntStateOf(MAX_QR_CODE_ROTATION) }
42-
val data by produceState<AsyncData<String>>(AsyncData.Success(initialData)) {
47+
val state by produceState(
48+
initialValue = ShowQrCodeState(
49+
data1 = AsyncData.Success(initialData),
50+
data2 = AsyncData.Uninitialized,
51+
dataToRender = 1,
52+
)
53+
) {
4354
linkNewMobileHandler.stepFlow.collect { step ->
55+
val currentValue = value
4456
when (step) {
4557
is LinkMobileStep.QrReady -> {
46-
value = AsyncData.Success(step.data)
58+
loadingJob?.cancel()
59+
if (currentValue.dataToRender == 1) {
60+
value = currentValue.copy(
61+
data2 = AsyncData.Success(step.data),
62+
dataToRender = 2,
63+
)
64+
} else {
65+
value = currentValue.copy(
66+
data1 = AsyncData.Success(step.data),
67+
dataToRender = 1,
68+
)
69+
}
4770
}
4871
is LinkMobileStep.QrRotating -> {
4972
if (qrCodeRotationCounter-- > 0) {
5073
Timber.tag(tag.value).d("Rotating QrCode")
5174
linkNewMobileHandler.rotateQrCode()
52-
value = AsyncData.Loading()
75+
// Ensure that outdated data is not rendered too long while rotating QR code
76+
loadingJob = launch {
77+
delay(1000)
78+
if (currentValue.dataToRender == 1) {
79+
value = currentValue.copy(
80+
data2 = AsyncData.Loading(),
81+
dataToRender = 2,
82+
)
83+
} else {
84+
value = currentValue.copy(
85+
data1 = AsyncData.Loading(),
86+
dataToRender = 1,
87+
)
88+
}
89+
}
5390
} else {
5491
Timber.tag(tag.value).w("Max QR code rotation reached, not rotating anymore")
5592
linkNewMobileHandler.onTooManyRotation()
@@ -60,9 +97,7 @@ class ShowQrCodePresenter(
6097
}
6198
}
6299

63-
return ShowQrCodeState(
64-
data = data,
65-
)
100+
return state
66101
}
67102

68103
companion object {

features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@ package io.element.android.features.linknewdevice.impl.screens.qrcode
1010
import io.element.android.libraries.architecture.AsyncData
1111

1212
data class ShowQrCodeState(
13-
val data: AsyncData<String>,
13+
val data1: AsyncData<String>,
14+
val data2: AsyncData<String>,
15+
val dataToRender: Int,
1416
)

features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,23 @@ class ShowQrCodeStateProvider : PreviewParameterProvider<ShowQrCodeState> {
1414
override val values: Sequence<ShowQrCodeState>
1515
get() = sequenceOf(
1616
aShowQrCodeState(),
17-
ShowQrCodeState(
18-
data = AsyncData.Loading(),
17+
aShowQrCodeState(
18+
data1 = AsyncData.Loading(),
19+
),
20+
aShowQrCodeState(
21+
data1 = AsyncData.Success("DATA"),
22+
data2 = AsyncData.Success("DATA2"),
23+
dataToRender = 2,
1924
),
2025
)
2126
}
2227

2328
private fun aShowQrCodeState(
24-
data: AsyncData.Success<String> = AsyncData.Success("DATA"),
29+
data1: AsyncData<String> = AsyncData.Success("DATA"),
30+
data2: AsyncData<String> = AsyncData.Uninitialized,
31+
dataToRender: Int = 1,
2532
) = ShowQrCodeState(
26-
data = data,
33+
data1 = data1,
34+
data2 = data2,
35+
dataToRender = dataToRender,
2736
)

features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
package io.element.android.features.linknewdevice.impl.screens.qrcode
1111

12+
import androidx.compose.animation.AnimatedVisibility
13+
import androidx.compose.animation.fadeIn
14+
import androidx.compose.animation.fadeOut
1215
import androidx.compose.foundation.layout.Box
1316
import androidx.compose.foundation.layout.Column
1417
import androidx.compose.foundation.layout.Spacer
@@ -58,23 +61,15 @@ fun ShowQrCodeView(
5861
Modifier.fillMaxWidth(),
5962
horizontalAlignment = Alignment.CenterHorizontally,
6063
) {
61-
when (val str = state.data.dataOrNull()) {
62-
null -> {
63-
Box(
64-
modifier = Modifier
65-
.size(220.dp),
66-
contentAlignment = Alignment.Center,
67-
) {
68-
CircularProgressIndicator()
69-
}
70-
}
71-
else -> {
72-
QrCodeImage(
73-
data = str,
74-
modifier = Modifier
75-
.size(220.dp)
76-
)
77-
}
64+
Box {
65+
QrCodeOrLoading(
66+
isVisible = state.dataToRender == 1,
67+
data = state.data1.dataOrNull(),
68+
)
69+
QrCodeOrLoading(
70+
isVisible = state.dataToRender == 2,
71+
data = state.data2.dataOrNull(),
72+
)
7873
}
7974
Spacer(modifier = Modifier.height(32.dp))
8075
NumberedListOrganism(
@@ -95,6 +90,36 @@ fun ShowQrCodeView(
9590
}
9691
}
9792

93+
@Composable
94+
private fun QrCodeOrLoading(
95+
isVisible: Boolean,
96+
data: String?,
97+
modifier: Modifier = Modifier,
98+
) {
99+
AnimatedVisibility(
100+
modifier = modifier,
101+
visible = isVisible,
102+
enter = fadeIn(),
103+
exit = fadeOut(),
104+
) {
105+
if (data == null) {
106+
Box(
107+
modifier = Modifier
108+
.size(220.dp),
109+
contentAlignment = Alignment.Center,
110+
) {
111+
CircularProgressIndicator()
112+
}
113+
} else {
114+
QrCodeImage(
115+
data = data,
116+
modifier = Modifier
117+
.size(220.dp)
118+
)
119+
}
120+
}
121+
}
122+
98123
@PreviewsDayNight
99124
@Composable
100125
internal fun ShowQrCodeViewPreview(

0 commit comments

Comments
 (0)