Skip to content

Commit 5728d62

Browse files
authored
Merge pull request #1648 from vector-im/feature/bma/secureBackup
Secure backup
2 parents ebc33e7 + 6258f34 commit 5728d62

File tree

77 files changed

+284
-158
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+284
-158
lines changed
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17-
package io.element.android.features.securebackup.impl
17+
package io.element.android.appconfig
1818

19-
// TODO Move to appconfig module when it will be available
2019
object SecureBackupConfig {
2120
const val LearnMoreUrl: String = "https://element.io/help#encryption5"
2221
}

features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class LogoutNode @AssistedInject constructor(
5858
state = state,
5959
onChangeRecoveryKeyClicked = ::onChangeRecoveryKeyClicked,
6060
onSuccessLogout = { onSuccessLogout(activity, it) },
61+
onBackClicked = ::navigateUp,
6162
modifier = modifier,
6263
)
6364
}

features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import io.element.android.libraries.architecture.Async
2929
import io.element.android.libraries.architecture.Presenter
3030
import io.element.android.libraries.architecture.runCatchingUpdatingState
3131
import io.element.android.libraries.matrix.api.MatrixClient
32+
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
3233
import io.element.android.libraries.matrix.api.encryption.EncryptionService
3334
import kotlinx.coroutines.CoroutineScope
3435
import kotlinx.coroutines.launch
@@ -46,13 +47,15 @@ class LogoutPresenter @Inject constructor(
4647
mutableStateOf(Async.Uninitialized)
4748
}
4849

49-
val backupUploadState by encryptionService.backupUploadStateStateFlow.collectAsState()
50+
val backupUploadState: BackupUploadState by remember {
51+
encryptionService.waitForBackupUploadSteadyState()
52+
}
53+
.collectAsState(initial = BackupUploadState.Unknown)
5054

5155
var showLogoutDialog by remember { mutableStateOf(false) }
5256
var isLastSession by remember { mutableStateOf(false) }
5357
LaunchedEffect(Unit) {
5458
isLastSession = encryptionService.isLastDevice().getOrNull() ?: false
55-
encryptionService.waitForBackupUploadSteadyState()
5659
}
5760

5861
fun handleEvents(event: LogoutEvents) {

features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.Arrangement
2020
import androidx.compose.foundation.layout.Column
2121
import androidx.compose.foundation.layout.fillMaxWidth
2222
import androidx.compose.foundation.layout.padding
23+
import androidx.compose.material3.ExperimentalMaterial3Api
2324
import androidx.compose.runtime.Composable
2425
import androidx.compose.runtime.LaunchedEffect
2526
import androidx.compose.ui.Alignment
@@ -32,30 +33,40 @@ import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMo
3233
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
3334
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
3435
import io.element.android.libraries.designsystem.components.ProgressDialog
36+
import io.element.android.libraries.designsystem.components.button.BackButton
3537
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
3638
import io.element.android.libraries.designsystem.preview.ElementPreview
3739
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
3840
import io.element.android.libraries.designsystem.theme.components.Button
3941
import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator
4042
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
4143
import io.element.android.libraries.designsystem.theme.components.Text
44+
import io.element.android.libraries.designsystem.theme.components.TopAppBar
4245
import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor
4346
import io.element.android.libraries.designsystem.utils.CommonDrawables
4447
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
4548
import io.element.android.libraries.theme.ElementTheme
4649
import io.element.android.libraries.ui.strings.CommonStrings
4750

51+
@OptIn(ExperimentalMaterial3Api::class)
4852
@Composable
4953
fun LogoutView(
5054
state: LogoutState,
5155
onChangeRecoveryKeyClicked: () -> Unit,
56+
onBackClicked: () -> Unit,
5257
onSuccessLogout: (logoutUrlResult: String?) -> Unit,
5358
modifier: Modifier = Modifier,
5459
) {
5560
val eventSink = state.eventSink
5661

5762
HeaderFooterPage(
5863
modifier = modifier,
64+
topBar = {
65+
TopAppBar(
66+
navigationIcon = { BackButton(onClick = onBackClicked) },
67+
title = {},
68+
)
69+
},
5970
header = {
6071
HeaderContent(state = state)
6172
},
@@ -134,7 +145,7 @@ private fun HeaderContent(
134145
else -> null
135146
}
136147

137-
val paddingTop = 60.dp
148+
val paddingTop = 0.dp
138149
IconTitleSubtitleMolecule(
139150
modifier = modifier.padding(top = paddingTop),
140151
iconResourceId = CommonDrawables.ic_key,
@@ -219,6 +230,7 @@ internal fun LogoutViewPreview(
219230
LogoutView(
220231
state,
221232
onChangeRecoveryKeyClicked = {},
222-
onSuccessLogout = {}
233+
onSuccessLogout = {},
234+
onBackClicked = {},
223235
)
224236
}

features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import io.element.android.libraries.matrix.test.A_THROWABLE
2828
import io.element.android.libraries.matrix.test.FakeMatrixClient
2929
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
3030
import io.element.android.tests.testutils.WarmUpRule
31+
import kotlinx.coroutines.delay
32+
import kotlinx.coroutines.flow.flow
3133
import kotlinx.coroutines.test.runTest
3234
import org.junit.Rule
3335
import org.junit.Test
@@ -73,6 +75,15 @@ class LogoutPresenterTest {
7375
@Test
7476
fun `present - initial state - backing up`() = runTest {
7577
val encryptionService = FakeEncryptionService()
78+
encryptionService.givenWaitForBackupUploadSteadyStateFlow(
79+
flow {
80+
emit(BackupUploadState.Waiting)
81+
delay(1)
82+
emit(BackupUploadState.Uploading(backedUpCount = 1, totalCount = 2))
83+
delay(1)
84+
emit(BackupUploadState.Done)
85+
}
86+
)
7687
val presenter = createLogoutPresenter(
7788
encryptionService = encryptionService
7889
)
@@ -84,10 +95,10 @@ class LogoutPresenterTest {
8495
assertThat(initialState.backupUploadState).isEqualTo(BackupUploadState.Unknown)
8596
assertThat(initialState.showConfirmationDialog).isFalse()
8697
assertThat(initialState.logoutAction).isEqualTo(Async.Uninitialized)
87-
encryptionService.emitBackupUploadState(BackupUploadState.Uploading(backedUpCount = 1, totalCount = 2))
88-
val state = awaitItem()
89-
assertThat(state.backupUploadState).isEqualTo(BackupUploadState.Uploading(backedUpCount = 1, totalCount = 2))
90-
encryptionService.emitBackupUploadState(BackupUploadState.Done)
98+
val waitingState = awaitItem()
99+
assertThat(waitingState.backupUploadState).isEqualTo(BackupUploadState.Waiting)
100+
val uploadingState = awaitItem()
101+
assertThat(uploadingState.backupUploadState).isEqualTo(BackupUploadState.Uploading(backedUpCount = 1, totalCount = 2))
91102
val doneState = awaitItem()
92103
assertThat(doneState.backupUploadState).isEqualTo(BackupUploadState.Done)
93104
}

features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ import io.element.android.features.roomlist.impl.components.RoomListTopBar
5252
import io.element.android.features.roomlist.impl.components.RoomSummaryRow
5353
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
5454
import io.element.android.features.roomlist.impl.search.RoomListSearchResultView
55-
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
5655
import io.element.android.libraries.designsystem.preview.ElementPreview
56+
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
5757
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
5858
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
5959
import io.element.android.libraries.designsystem.theme.components.Icon
@@ -190,19 +190,22 @@ private fun RoomListContent(
190190
.nestedScroll(nestedScrollConnection),
191191
state = lazyListState,
192192
) {
193-
if (state.displayVerificationPrompt) {
194-
item {
195-
RequestVerificationHeader(
196-
onVerifyClicked = onVerifyClicked,
197-
onDismissClicked = { state.eventSink(RoomListEvents.DismissRequestVerificationPrompt) }
198-
)
193+
when {
194+
state.displayVerificationPrompt -> {
195+
item {
196+
RequestVerificationHeader(
197+
onVerifyClicked = onVerifyClicked,
198+
onDismissClicked = { state.eventSink(RoomListEvents.DismissRequestVerificationPrompt) }
199+
)
200+
}
199201
}
200-
} else if (state.displayRecoveryKeyPrompt) {
201-
item {
202-
ConfirmRecoveryKeyBanner(
203-
onContinueClicked = onOpenSettings,
204-
onDismissClicked = { state.eventSink(RoomListEvents.DismissRecoveryKeyPrompt) }
205-
)
202+
state.displayRecoveryKeyPrompt -> {
203+
item {
204+
ConfirmRecoveryKeyBanner(
205+
onContinueClicked = onOpenSettings,
206+
onDismissClicked = { state.eventSink(RoomListEvents.DismissRecoveryKeyPrompt) }
207+
)
208+
}
206209
}
207210
}
208211

features/securebackup/impl/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ dependencies {
3333
anvil(projects.anvilcodegen)
3434
implementation(projects.anvilannotations)
3535

36+
implementation(projects.appconfig)
3637
implementation(projects.libraries.androidutils)
3738
implementation(projects.libraries.core)
3839
implementation(projects.libraries.androidutils)

features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableNode.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class SecureBackupDisableNode @AssistedInject constructor(
4040
state = state,
4141
modifier = modifier,
4242
onDone = ::navigateUp,
43+
onBackClicked = ::navigateUp,
4344
)
4445
}
4546
}

features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Row
2222
import androidx.compose.foundation.layout.fillMaxWidth
2323
import androidx.compose.foundation.layout.padding
2424
import androidx.compose.foundation.layout.size
25+
import androidx.compose.material3.ExperimentalMaterial3Api
2526
import androidx.compose.runtime.Composable
2627
import androidx.compose.runtime.LaunchedEffect
2728
import androidx.compose.ui.Modifier
@@ -33,20 +34,24 @@ import io.element.android.libraries.architecture.Async
3334
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
3435
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
3536
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
37+
import io.element.android.libraries.designsystem.components.button.BackButton
3638
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
3739
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
3840
import io.element.android.libraries.designsystem.preview.ElementPreview
3941
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
4042
import io.element.android.libraries.designsystem.theme.components.Button
4143
import io.element.android.libraries.designsystem.theme.components.Icon
4244
import io.element.android.libraries.designsystem.theme.components.Text
45+
import io.element.android.libraries.designsystem.theme.components.TopAppBar
4346
import io.element.android.libraries.designsystem.utils.CommonDrawables
4447
import io.element.android.libraries.theme.ElementTheme
4548

49+
@OptIn(ExperimentalMaterial3Api::class)
4650
@Composable
4751
fun SecureBackupDisableView(
4852
state: SecureBackupDisableState,
4953
onDone: () -> Unit,
54+
onBackClicked: () -> Unit,
5055
modifier: Modifier = Modifier,
5156
) {
5257
LaunchedEffect(state.disableAction) {
@@ -56,6 +61,12 @@ fun SecureBackupDisableView(
5661
}
5762
HeaderFooterPage(
5863
modifier = modifier,
64+
topBar = {
65+
TopAppBar(
66+
navigationIcon = { BackButton(onClick = onBackClicked) },
67+
title = {},
68+
)
69+
},
5970
header = {
6071
HeaderContent()
6172
},
@@ -95,7 +106,7 @@ private fun HeaderContent(
95106
modifier: Modifier = Modifier,
96107
) {
97108
IconTitleSubtitleMolecule(
98-
modifier = modifier.padding(top = 60.dp),
109+
modifier = modifier.padding(top = 0.dp),
99110
iconResourceId = CommonDrawables.ic_key_off,
100111
title = stringResource(id = R.string.screen_key_backup_disable_title),
101112
subTitle = stringResource(id = R.string.screen_key_backup_disable_description),
@@ -158,5 +169,6 @@ internal fun SecureBackupDisableViewPreview(
158169
SecureBackupDisableView(
159170
state = state,
160171
onDone = {},
172+
onBackClicked = {},
161173
)
162174
}

features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableNode.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class SecureBackupEnableNode @AssistedInject constructor(
4040
state = state,
4141
modifier = modifier,
4242
onDone = ::navigateUp,
43+
onBackClicked = ::navigateUp,
4344
)
4445
}
4546
}

features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package io.element.android.features.securebackup.impl.enable
1818

1919
import androidx.compose.foundation.layout.fillMaxWidth
2020
import androidx.compose.foundation.layout.padding
21+
import androidx.compose.material3.ExperimentalMaterial3Api
2122
import androidx.compose.runtime.Composable
2223
import androidx.compose.runtime.LaunchedEffect
2324
import androidx.compose.ui.Modifier
@@ -29,16 +30,20 @@ import io.element.android.libraries.architecture.Async
2930
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
3031
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
3132
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
33+
import io.element.android.libraries.designsystem.components.button.BackButton
3234
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
3335
import io.element.android.libraries.designsystem.preview.ElementPreview
3436
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
3537
import io.element.android.libraries.designsystem.theme.components.Button
38+
import io.element.android.libraries.designsystem.theme.components.TopAppBar
3639
import io.element.android.libraries.designsystem.utils.CommonDrawables
3740

41+
@OptIn(ExperimentalMaterial3Api::class)
3842
@Composable
3943
fun SecureBackupEnableView(
4044
state: SecureBackupEnableState,
4145
onDone: () -> Unit,
46+
onBackClicked: () -> Unit,
4247
modifier: Modifier = Modifier,
4348
) {
4449
LaunchedEffect(state.enableAction) {
@@ -48,6 +53,12 @@ fun SecureBackupEnableView(
4853
}
4954
HeaderFooterPage(
5055
modifier = modifier,
56+
topBar = {
57+
TopAppBar(
58+
navigationIcon = { BackButton(onClick = onBackClicked) },
59+
title = {},
60+
)
61+
},
5162
header = {
5263
HeaderContent()
5364
},
@@ -68,7 +79,7 @@ private fun HeaderContent(
6879
modifier: Modifier = Modifier,
6980
) {
7081
IconTitleSubtitleMolecule(
71-
modifier = modifier.padding(top = 60.dp),
82+
modifier = modifier.padding(top = 0.dp),
7283
iconResourceId = CommonDrawables.ic_key,
7384
title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable),
7485
subTitle = null,
@@ -99,5 +110,6 @@ internal fun SecureBackupEnableViewPreview(
99110
SecureBackupEnableView(
100111
state = state,
101112
onDone = {},
113+
onBackClicked = {},
102114
)
103115
}

features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ class SecureBackupEnterRecoveryKeyNode @AssistedInject constructor(
5050
onDone = {
5151
coroutineScope.postSuccessSnackbar()
5252
navigateUp()
53-
}
53+
},
54+
onBackClicked = ::navigateUp,
5455
)
5556
}
5657

0 commit comments

Comments
 (0)