From 9fa60264838e0e3ca4f74bf5b6098e86ef350522 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 3 Nov 2023 10:36:16 +0100 Subject: [PATCH 01/12] Create UserStoryFlowPage. This will reduce some boilerplate and copy pasting we have in the codebase. --- .../atomic/pages/UserStoryFlowPage.kt | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt new file mode 100644 index 0000000000..84eb00e71e --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.designsystem.atomic.pages + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.R +import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule +import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Button +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.theme.ElementTheme + +/** + * A Page with: + * - a top bar as TobAppBar with optional back button + * - a header, as IconTitleSubtitleMolecule + * - a content. + * - a footer, as ButtonColumnMolecule + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun UserStoryFlowPage( + canGoBack: Boolean, + onBackClicked: () -> Unit, + title: String, + subTitle: String?, + iconResourceId: Int?, + modifier: Modifier = Modifier, + content: @Composable () -> Unit = {}, + buttons: @Composable ColumnScope.() -> Unit = {}, +) { + BackHandler(enabled = canGoBack) { + onBackClicked() + } + HeaderFooterPage( + modifier = modifier, + topBar = { + TopAppBar( + navigationIcon = { + if (canGoBack) { + BackButton(onClick = onBackClicked) + } + }, + title = {}, + ) + }, + header = { + IconTitleSubtitleMolecule( + modifier = modifier.padding(top = 0.dp), + iconResourceId = iconResourceId, + title = title, + subTitle = subTitle, + ) + }, + content = content, + footer = { + ButtonColumnMolecule( + modifier = Modifier.padding(bottom = 20.dp) + ) { + buttons() + } + } + ) +} + +@PreviewsDayNight +@Composable +internal fun UserStoryFlowPagePreview() = ElementPreview { + UserStoryFlowPage( + canGoBack = true, + onBackClicked = {}, + title = "Title", + subTitle = "Subtitle", + iconResourceId = R.drawable.ic_compound_computer, + content = { + Box( + Modifier + .fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text( + text = "Content", + style = ElementTheme.typography.fontHeadingXlBold + ) + } + }, + buttons = { + TextButton(text = "A button", onClick = { }) + Button(text = "Continue", onClick = { }) + } + ) +} From c77ebabcb25dd27d1962af9f77d317b2ed13a33d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 3 Nov 2023 10:52:59 +0100 Subject: [PATCH 02/12] Let SecureBackupSetupView use UserStoryFlowPage. --- .../impl/setup/SecureBackupSetupView.kt | 215 +++++++----------- 1 file changed, 88 insertions(+), 127 deletions(-) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt index 6c35a979e0..cf96d68bd0 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt @@ -16,10 +16,9 @@ package io.element.android.features.securebackup.impl.setup -import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -28,24 +27,18 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.securebackup.impl.R import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyView -import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyViewState import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.androidutils.system.startSharePlainTextIntent -import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule -import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule -import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage -import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.atomic.pages.UserStoryFlowPage import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.OutlinedButton -import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.ui.strings.CommonStrings -@OptIn(ExperimentalMaterial3Api::class) @Composable fun SecureBackupSetupView( state: SecureBackupSetupState, @@ -53,68 +46,16 @@ fun SecureBackupSetupView( onBackClicked: () -> Unit, modifier: Modifier = Modifier, ) { - val context = LocalContext.current - val canGoBack = state.canGoBack() - BackHandler(enabled = canGoBack) { - onBackClicked() - } - HeaderFooterPage( + UserStoryFlowPage( modifier = modifier, - topBar = { - TopAppBar( - navigationIcon = { - if (canGoBack) { - BackButton(onClick = onBackClicked) - } - }, - title = {}, - ) - }, - header = { - HeaderContent(state = state) - }, - footer = { - val chooserTitle = stringResource(id = R.string.screen_recovery_key_save_action) - BottomMenu( - state = state, - onSaveClicked = { key -> - context.startSharePlainTextIntent( - activityResultLauncher = null, - chooserTitle = chooserTitle, - text = key, - ) - state.eventSink.invoke(SecureBackupSetupEvents.RecoveryKeyHasBeenSaved) - }, - onDone = { - if (state.setupState is SetupState.CreatedAndSaved) { - onDone() - } else { - state.eventSink.invoke(SecureBackupSetupEvents.Done) - } - }, - ) - } - ) { - val formattedRecoveryKey = state.recoveryKeyViewState.formattedRecoveryKey - val clickLambda = if (formattedRecoveryKey != null) { - { - context.copyToClipboard( - formattedRecoveryKey, - context.getString(R.string.screen_recovery_key_copied_to_clipboard) - ) - state.eventSink.invoke(SecureBackupSetupEvents.RecoveryKeyHasBeenSaved) - } - } else { - if (!state.recoveryKeyViewState.inProgress) { - { - state.eventSink.invoke(SecureBackupSetupEvents.CreateRecoveryKey) - } - } else { - null - } - } - Content(state = state.recoveryKeyViewState, onClick = clickLambda) - } + canGoBack = state.canGoBack(), + onBackClicked = onBackClicked, + title = title(state), + subTitle = subtitle(state), + iconResourceId = CommonDrawables.ic_key, + content = { Content(state) }, + buttons = { Buttons(state, onDone = onDone) }, + ) if (state.showSaveConfirmationDialog) { ConfirmationDialog( @@ -134,12 +75,8 @@ private fun SecureBackupSetupState.canGoBack(): Boolean { } @Composable -private fun HeaderContent( - state: SecureBackupSetupState, - modifier: Modifier = Modifier, -) { - val setupState = state.setupState - val title = when (setupState) { +private fun title(state: SecureBackupSetupState): String { + return when (state.setupState) { SetupState.Init, SetupState.Creating -> if (state.isChangeRecoveryKeyUserStory) stringResource(id = R.string.screen_recovery_key_change_title) @@ -149,7 +86,11 @@ private fun HeaderContent( is SetupState.CreatedAndSaved -> stringResource(id = R.string.screen_recovery_key_save_title) } - val subTitle = when (setupState) { +} + +@Composable +private fun subtitle(state: SecureBackupSetupState): String { + return when (state.setupState) { SetupState.Init, SetupState.Creating -> if (state.isChangeRecoveryKeyUserStory) stringResource(id = R.string.screen_recovery_key_change_description) @@ -159,67 +100,87 @@ private fun HeaderContent( is SetupState.CreatedAndSaved -> stringResource(id = R.string.screen_recovery_key_save_description) } - IconTitleSubtitleMolecule( - modifier = modifier.padding(top = 0.dp), - iconResourceId = CommonDrawables.ic_key, - title = title, - subTitle = subTitle, - ) -} - -@Composable -private fun BottomMenu( - state: SecureBackupSetupState, - onSaveClicked: (String) -> Unit, - onDone: () -> Unit, -) { - val setupState = state.setupState - ButtonColumnMolecule( - modifier = Modifier.padding(bottom = 20.dp) - ) { - when (setupState) { - SetupState.Init, - SetupState.Creating -> { - Button( - text = stringResource(id = CommonStrings.action_done), - enabled = false, - modifier = Modifier.fillMaxWidth(), - onClick = onDone - ) - } - is SetupState.Created, - is SetupState.CreatedAndSaved -> { - OutlinedButton( - text = stringResource(id = R.string.screen_recovery_key_save_action), - leadingIcon = IconSource.Resource(CommonDrawables.ic_compound_download), - modifier = Modifier.fillMaxWidth(), - onClick = { onSaveClicked(setupState.recoveryKey()!!) }, - ) - Button( - text = stringResource(id = CommonStrings.action_done), - modifier = Modifier.fillMaxWidth(), - onClick = onDone, - ) - } - } - } } @Composable private fun Content( - state: RecoveryKeyViewState, - onClick: (() -> Unit)?, + state: SecureBackupSetupState, ) { - val modifier = Modifier.padding(top = 52.dp) + val context = LocalContext.current + val formattedRecoveryKey = state.recoveryKeyViewState.formattedRecoveryKey + val clickLambda = if (formattedRecoveryKey != null) { + { + context.copyToClipboard( + formattedRecoveryKey, + context.getString(R.string.screen_recovery_key_copied_to_clipboard) + ) + state.eventSink.invoke(SecureBackupSetupEvents.RecoveryKeyHasBeenSaved) + } + } else { + if (!state.recoveryKeyViewState.inProgress) { + { + state.eventSink.invoke(SecureBackupSetupEvents.CreateRecoveryKey) + } + } else { + null + } + } RecoveryKeyView( - modifier = modifier, - state = state, - onClick = onClick, + modifier = Modifier.padding(top = 52.dp), + state = state.recoveryKeyViewState, + onClick = clickLambda, onChange = null, onSubmit = null, ) } +@Composable +private fun ColumnScope.Buttons( + state: SecureBackupSetupState, + onDone: () -> Unit, +) { + val context = LocalContext.current + val chooserTitle = stringResource(id = R.string.screen_recovery_key_save_action) + when (state.setupState) { + SetupState.Init, + SetupState.Creating -> { + Button( + text = stringResource(id = CommonStrings.action_done), + enabled = false, + modifier = Modifier.fillMaxWidth(), + onClick = onDone + ) + } + is SetupState.Created, + is SetupState.CreatedAndSaved -> { + OutlinedButton( + text = stringResource(id = R.string.screen_recovery_key_save_action), + leadingIcon = IconSource.Resource(CommonDrawables.ic_compound_download), + modifier = Modifier.fillMaxWidth(), + onClick = { + context.startSharePlainTextIntent( + activityResultLauncher = null, + chooserTitle = chooserTitle, + text = state.setupState.recoveryKey()!!, + ) + state.eventSink.invoke(SecureBackupSetupEvents.RecoveryKeyHasBeenSaved) + }, + ) + Button( + text = stringResource(id = CommonStrings.action_done), + modifier = Modifier.fillMaxWidth(), + onClick = { + if (state.setupState is SetupState.CreatedAndSaved) { + onDone() + } else { + state.eventSink.invoke(SecureBackupSetupEvents.Done) + } + }, + ) + } + } +} + @PreviewsDayNight @Composable internal fun SecureBackupSetupViewPreview( From 85fa899b5d4278e4b0e789c628a529ef967af050 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 3 Nov 2023 11:00:17 +0100 Subject: [PATCH 03/12] Let LogoutView use UserStoryFlowPage. --- .../features/logout/impl/LogoutView.kt | 109 +++++++----------- 1 file changed, 41 insertions(+), 68 deletions(-) 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 0b6a8e3375..1e86827c6b 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 @@ -18,9 +18,9 @@ package io.element.android.features.logout.impl import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment @@ -29,11 +29,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.libraries.architecture.Async -import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule -import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule -import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage +import io.element.android.libraries.designsystem.atomic.pages.UserStoryFlowPage import io.element.android.libraries.designsystem.components.ProgressDialog -import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -41,7 +38,6 @@ import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.encryption.BackupUploadState @@ -51,7 +47,6 @@ import io.element.android.libraries.testtags.testTag import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings -@OptIn(ExperimentalMaterial3Api::class) @Composable fun LogoutView( state: LogoutState, @@ -62,29 +57,24 @@ fun LogoutView( ) { val eventSink = state.eventSink - HeaderFooterPage( + UserStoryFlowPage( + canGoBack = true, + onBackClicked = onBackClicked, + title = title(state), + subTitle = subtitle(state), + iconResourceId = CommonDrawables.ic_key, modifier = modifier, - topBar = { - TopAppBar( - navigationIcon = { BackButton(onClick = onBackClicked) }, - title = {}, - ) - }, - header = { - HeaderContent(state = state) - }, - footer = { - BottomMenu( + content = { Content(state) }, + buttons = { + Buttons( state = state, onChangeRecoveryKeyClicked = onChangeRecoveryKeyClicked, onLogoutClicked = { eventSink(LogoutEvents.Logout(ignoreSdkError = false)) - }, + } ) - } - ) { - Content(state = state) - } + }, + ) // Log out confirmation dialog if (state.showConfirmationDialog) { @@ -92,9 +82,6 @@ fun LogoutView( title = stringResource(id = CommonStrings.action_signout), content = stringResource(id = R.string.screen_signout_confirmation_dialog_content), submitText = stringResource(id = CommonStrings.action_signout), - onCancelClicked = { - eventSink(LogoutEvents.CloseDialogs) - }, onSubmitClicked = { eventSink(LogoutEvents.Logout(ignoreSdkError = false)) }, @@ -112,9 +99,6 @@ fun LogoutView( title = stringResource(id = CommonStrings.dialog_title_error), content = stringResource(id = CommonStrings.error_unknown), submitText = stringResource(id = CommonStrings.action_signout_anyway), - onCancelClicked = { - eventSink(LogoutEvents.CloseDialogs) - }, onSubmitClicked = { eventSink(LogoutEvents.Logout(ignoreSdkError = true)) }, @@ -132,30 +116,23 @@ fun LogoutView( } @Composable -private fun HeaderContent( - state: LogoutState, - modifier: Modifier = Modifier, -) { - val title = when { +private fun title(state: LogoutState): String { + return when { state.backupUploadState.isBackingUp() -> stringResource(id = R.string.screen_signout_key_backup_ongoing_title) state.isLastSession -> stringResource(id = R.string.screen_signout_key_backup_disabled_title) else -> stringResource(CommonStrings.action_signout) } - val subtitle = when { +} + +@Composable +private fun subtitle(state: LogoutState): String? { + return when { (state.backupUploadState as? BackupUploadState.SteadyException)?.exception is SteadyStateException.Connection -> stringResource(id = R.string.screen_signout_key_backup_offline_subtitle) state.backupUploadState.isBackingUp() -> stringResource(id = R.string.screen_signout_key_backup_ongoing_subtitle) state.isLastSession -> stringResource(id = R.string.screen_signout_key_backup_disabled_subtitle) else -> null } - - val paddingTop = 0.dp - IconTitleSubtitleMolecule( - modifier = modifier.padding(top = paddingTop), - iconResourceId = CommonDrawables.ic_key, - title = title, - subTitle = subtitle, - ) } private fun BackupUploadState.isBackingUp(): Boolean { @@ -171,37 +148,33 @@ private fun BackupUploadState.isBackingUp(): Boolean { } @Composable -private fun BottomMenu( +private fun ColumnScope.Buttons( state: LogoutState, onLogoutClicked: () -> Unit, onChangeRecoveryKeyClicked: () -> Unit, ) { val logoutAction = state.logoutAction - ButtonColumnMolecule( - modifier = Modifier.padding(bottom = 20.dp) - ) { - if (state.isLastSession) { - OutlinedButton( - text = stringResource(id = CommonStrings.common_settings), - modifier = Modifier.fillMaxWidth(), - onClick = onChangeRecoveryKeyClicked, - ) - } - val signOutSubmitRes = when { - logoutAction is Async.Loading -> R.string.screen_signout_in_progress_dialog_content - state.backupUploadState.isBackingUp() -> CommonStrings.action_signout_anyway - else -> CommonStrings.action_signout - } - Button( - text = stringResource(id = signOutSubmitRes), - showProgress = logoutAction is Async.Loading, - destructive = true, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.signOut), - onClick = onLogoutClicked, + if (state.isLastSession) { + OutlinedButton( + text = stringResource(id = CommonStrings.common_settings), + modifier = Modifier.fillMaxWidth(), + onClick = onChangeRecoveryKeyClicked, ) } + val signOutSubmitRes = when { + logoutAction is Async.Loading -> R.string.screen_signout_in_progress_dialog_content + state.backupUploadState.isBackingUp() -> CommonStrings.action_signout_anyway + else -> CommonStrings.action_signout + } + Button( + text = stringResource(id = signOutSubmitRes), + showProgress = logoutAction is Async.Loading, + destructive = true, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.signOut), + onClick = onLogoutClicked, + ) } @Composable From 310bd5bb49dbe86383437beea3410c0b3dd6f2f3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 3 Nov 2023 14:44:44 +0100 Subject: [PATCH 04/12] Let SecureBackupDisableView use UserStoryFlowPage. --- .../impl/disable/SecureBackupDisableView.kt | 67 ++++++------------- 1 file changed, 20 insertions(+), 47 deletions(-) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt index 133043d064..037a9c6bd2 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt @@ -18,11 +18,11 @@ package io.element.android.features.securebackup.impl.disable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier @@ -31,10 +31,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.securebackup.impl.R import io.element.android.libraries.architecture.Async -import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule -import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule -import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage -import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.atomic.pages.UserStoryFlowPage import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreview @@ -42,11 +39,9 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.theme.ElementTheme -@OptIn(ExperimentalMaterial3Api::class) @Composable fun SecureBackupDisableView( state: SecureBackupDisableState, @@ -59,23 +54,17 @@ fun SecureBackupDisableView( onDone() } } - HeaderFooterPage( + UserStoryFlowPage( modifier = modifier, - topBar = { - TopAppBar( - navigationIcon = { BackButton(onClick = onBackClicked) }, - title = {}, - ) - }, - header = { - HeaderContent() - }, - footer = { - BottomMenu(state = state) - } - ) { - Content(state = state) - } + canGoBack = true, + onBackClicked = onBackClicked, + title = stringResource(id = R.string.screen_key_backup_disable_title), + subTitle = stringResource(id = R.string.screen_key_backup_disable_description), + iconResourceId = CommonDrawables.ic_key_off, + content = { Content(state = state) }, + buttons = { Buttons(state = state) }, + ) + if (state.showConfirmationDialog) { SecureBackupDisableConfirmationDialog( onConfirm = { state.eventSink.invoke(SecureBackupDisableEvents.DisableBackup(force = true)) }, @@ -102,32 +91,16 @@ private fun SecureBackupDisableConfirmationDialog(onConfirm: () -> Unit, onDismi } @Composable -private fun HeaderContent( - modifier: Modifier = Modifier, -) { - IconTitleSubtitleMolecule( - modifier = modifier.padding(top = 0.dp), - iconResourceId = CommonDrawables.ic_key_off, - title = stringResource(id = R.string.screen_key_backup_disable_title), - subTitle = stringResource(id = R.string.screen_key_backup_disable_description), - ) -} - -@Composable -private fun BottomMenu( +private fun ColumnScope.Buttons( state: SecureBackupDisableState, ) { - ButtonColumnMolecule( - modifier = Modifier.padding(bottom = 20.dp) - ) { - Button( - text = stringResource(id = R.string.screen_chat_backup_key_backup_action_disable), - showProgress = state.disableAction.isLoading(), - destructive = true, - modifier = Modifier.fillMaxWidth(), - onClick = { state.eventSink.invoke(SecureBackupDisableEvents.DisableBackup(force = false)) } - ) - } + Button( + text = stringResource(id = R.string.screen_chat_backup_key_backup_action_disable), + showProgress = state.disableAction.isLoading(), + destructive = true, + modifier = Modifier.fillMaxWidth(), + onClick = { state.eventSink.invoke(SecureBackupDisableEvents.DisableBackup(force = false)) } + ) } @Composable From 7e63f196d91f2d67549820241d4ac450197fd698 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 3 Nov 2023 14:47:50 +0100 Subject: [PATCH 05/12] Use ButtonColumnMolecule --- .../impl/setup/biometric/SetupBiometricView.kt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt index e1bdabb114..8b2a4fce4f 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt @@ -17,19 +17,16 @@ package io.element.android.features.lockscreen.impl.setup.biometric import androidx.activity.compose.BackHandler -import androidx.compose.foundation.layout.Arrangement.spacedBy -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Fingerprint import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.lockscreen.impl.R +import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.preview.ElementPreview @@ -76,10 +73,8 @@ private fun SetupBiometricFooter( onSkipClicked: () -> Unit, modifier: Modifier = Modifier ) { - Column( - modifier = modifier.fillMaxWidth(), - verticalArrangement = spacedBy(16.dp), - horizontalAlignment = Alignment.CenterHorizontally + ButtonColumnMolecule( + modifier = modifier, ) { val biometricAuth = stringResource(id = R.string.screen_app_lock_biometric_authentication) Button( From 256eb23acf2542a5bc9911e887cccd50107441d0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 3 Nov 2023 14:52:08 +0100 Subject: [PATCH 06/12] Let SecureBackupEnableView use UserStoryFlowPage. --- .../impl/enable/SecureBackupEnableView.kt | 60 +++++-------------- .../atomic/pages/UserStoryFlowPage.kt | 4 +- 2 files changed, 17 insertions(+), 47 deletions(-) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt index 12e02663ec..ad844a7a68 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt @@ -16,29 +16,22 @@ package io.element.android.features.securebackup.impl.enable +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp import io.element.android.features.securebackup.impl.R import io.element.android.libraries.architecture.Async -import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule -import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule -import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage -import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.atomic.pages.UserStoryFlowPage import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button -import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.CommonDrawables -@OptIn(ExperimentalMaterial3Api::class) @Composable fun SecureBackupEnableView( state: SecureBackupEnableState, @@ -51,20 +44,13 @@ fun SecureBackupEnableView( onDone() } } - HeaderFooterPage( + UserStoryFlowPage( modifier = modifier, - topBar = { - TopAppBar( - navigationIcon = { BackButton(onClick = onBackClicked) }, - title = {}, - ) - }, - header = { - HeaderContent() - }, - footer = { - BottomMenu(state = state) - } + canGoBack = true, + onBackClicked = onBackClicked, + title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), + iconResourceId = CommonDrawables.ic_key, + buttons = { Buttons(state = state) } ) if (state.enableAction is Async.Failure) { ErrorDialog( @@ -75,31 +61,15 @@ fun SecureBackupEnableView( } @Composable -private fun HeaderContent( - modifier: Modifier = Modifier, -) { - IconTitleSubtitleMolecule( - modifier = modifier.padding(top = 0.dp), - iconResourceId = CommonDrawables.ic_key, - title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), - subTitle = null, - ) -} - -@Composable -private fun BottomMenu( +private fun ColumnScope.Buttons( state: SecureBackupEnableState, ) { - ButtonColumnMolecule( - modifier = Modifier.padding(bottom = 20.dp) - ) { - Button( - text = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), - showProgress = state.enableAction.isLoading(), - modifier = Modifier.fillMaxWidth(), - onClick = { state.eventSink.invoke(SecureBackupEnableEvents.EnableBackup) } - ) - } + Button( + text = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), + showProgress = state.enableAction.isLoading(), + modifier = Modifier.fillMaxWidth(), + onClick = { state.eventSink.invoke(SecureBackupEnableEvents.EnableBackup) } + ) } @PreviewsDayNight diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt index 84eb00e71e..1970870fce 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt @@ -50,10 +50,10 @@ import io.element.android.libraries.theme.ElementTheme fun UserStoryFlowPage( canGoBack: Boolean, onBackClicked: () -> Unit, - title: String, - subTitle: String?, iconResourceId: Int?, + title: String, modifier: Modifier = Modifier, + subTitle: String? = null, content: @Composable () -> Unit = {}, buttons: @Composable ColumnScope.() -> Unit = {}, ) { From 3e3a7c55168c2631c36400266c82533edd0c9b4f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 3 Nov 2023 15:00:42 +0100 Subject: [PATCH 07/12] Let SecureBackupEnterRecoveryKeyView use UserStoryFlowPage. --- .../enter/SecureBackupEnterRecoveryKeyView.kt | 93 ++++++------------- 1 file changed, 28 insertions(+), 65 deletions(-) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt index b40305e0d5..f9b10a3686 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt @@ -16,9 +16,9 @@ package io.element.android.features.securebackup.impl.enter +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -26,19 +26,14 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.securebackup.impl.R import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyView -import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule -import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule -import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage +import io.element.android.libraries.designsystem.atomic.pages.UserStoryFlowPage import io.element.android.libraries.designsystem.components.async.AsyncView -import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button -import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.ui.strings.CommonStrings -@OptIn(ExperimentalMaterial3Api::class) @Composable fun SecureBackupEnterRecoveryKeyView( state: SecureBackupEnterRecoveryKeyState, @@ -53,79 +48,47 @@ fun SecureBackupEnterRecoveryKeyView( onErrorDismiss = { state.eventSink(SecureBackupEnterRecoveryKeyEvents.ClearDialog) }, ) - HeaderFooterPage( + UserStoryFlowPage( modifier = modifier, - topBar = { - TopAppBar( - navigationIcon = { BackButton(onClick = onBackClicked) }, - title = {}, - ) - }, - header = { - HeaderContent() - }, - footer = { - BottomMenu( - state = state, - onSubmit = { - state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.Submit) - }, - ) - } - ) { - Content( - state = state, - onChange = { - state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange(it)) - }, - onSubmit = { - state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.Submit) - }) - } -} - -@Composable -private fun HeaderContent( - modifier: Modifier = Modifier, -) { - IconTitleSubtitleMolecule( - modifier = modifier.padding(top = 0.dp), + canGoBack = true, + onBackClicked = onBackClicked, iconResourceId = CommonDrawables.ic_key, title = stringResource(id = R.string.screen_recovery_key_confirm_title), subTitle = stringResource(id = R.string.screen_recovery_key_confirm_description), + content = { Content(state = state) }, + buttons = { Buttons(state = state) } ) } -@Composable -private fun BottomMenu( - state: SecureBackupEnterRecoveryKeyState, - onSubmit: () -> Unit, -) { - ButtonColumnMolecule( - modifier = Modifier.padding(bottom = 20.dp) - ) { - Button( - text = stringResource(id = CommonStrings.action_confirm), - enabled = state.isSubmitEnabled, - showProgress = state.submitAction.isLoading(), - modifier = Modifier.fillMaxWidth(), - onClick = onSubmit - ) - } -} - @Composable private fun Content( state: SecureBackupEnterRecoveryKeyState, - onChange: (String) -> Unit, - onSubmit: () -> Unit, ) { RecoveryKeyView( modifier = Modifier.padding(top = 52.dp), state = state.recoveryKeyViewState, onClick = null, - onChange = onChange, - onSubmit = onSubmit, + onChange = { + state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange(it)) + }, + onSubmit = { + state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.Submit) + }, + ) +} + +@Composable +private fun ColumnScope.Buttons( + state: SecureBackupEnterRecoveryKeyState, +) { + Button( + text = stringResource(id = CommonStrings.action_confirm), + enabled = state.isSubmitEnabled, + showProgress = state.submitAction.isLoading(), + modifier = Modifier.fillMaxWidth(), + onClick = { + state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.Submit) + } ) } From 993f08e4275696eaf9ca882eb497a89ecaa55197 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 3 Nov 2023 14:22:22 +0000 Subject: [PATCH 08/12] Update screenshots --- ...c.pages_null_UserStoryFlowPage-D_0_null,NEXUS_5,1.0,en].png | 3 +++ ...c.pages_null_UserStoryFlowPage-N_1_null,NEXUS_5,1.0,en].png | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-D_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-N_1_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-D_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..94df7fad03 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-D_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:735eecae838f9ea7413d44e33d1ff7b7ce8524a7ee9c601d889f2a23cd06dbee +size 18530 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-N_1_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f8b715c693 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-N_1_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1ec13019a59463953135a6b469bcaeed3e61f868efd45c8f7b612a6d96ba711 +size 17672 From 3483eaaa00da18b4276e19ae36c8eeb93441a4e0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 3 Nov 2023 17:26:26 +0100 Subject: [PATCH 09/12] Fix issue about modifier. --- .../libraries/designsystem/atomic/pages/UserStoryFlowPage.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt index 1970870fce..e378a249ac 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt @@ -74,7 +74,6 @@ fun UserStoryFlowPage( }, header = { IconTitleSubtitleMolecule( - modifier = modifier.padding(top = 0.dp), iconResourceId = iconResourceId, title = title, subTitle = subTitle, From 163bc8703f82b39f06b10f2205ba914ec1679ef4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 7 Nov 2023 10:08:11 +0100 Subject: [PATCH 10/12] Rename UserStoryFlowPage to FlowStepPage Rename UserStoryFlowPage to FlowStepPage --- .../io/element/android/features/logout/impl/LogoutView.kt | 4 ++-- .../securebackup/impl/disable/SecureBackupDisableView.kt | 4 ++-- .../securebackup/impl/enable/SecureBackupEnableView.kt | 4 ++-- .../impl/enter/SecureBackupEnterRecoveryKeyView.kt | 4 ++-- .../securebackup/impl/setup/SecureBackupSetupView.kt | 4 ++-- .../atomic/pages/{UserStoryFlowPage.kt => FlowStepPage.kt} | 6 +++--- ...ic.pages_null_FlowStepPage-D_0_null,NEXUS_5,1.0,en].png} | 0 ...ic.pages_null_FlowStepPage-N_1_null,NEXUS_5,1.0,en].png} | 0 8 files changed, 13 insertions(+), 13 deletions(-) rename libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/{UserStoryFlowPage.kt => FlowStepPage.kt} (97%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-D_0_null,NEXUS_5,1.0,en].png => ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-D_0_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-N_1_null,NEXUS_5,1.0,en].png => ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-N_1_null,NEXUS_5,1.0,en].png} (100%) 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 1e86827c6b..88441f0e0c 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 @@ -29,7 +29,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.libraries.architecture.Async -import io.element.android.libraries.designsystem.atomic.pages.UserStoryFlowPage +import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.ElementPreview @@ -57,7 +57,7 @@ fun LogoutView( ) { val eventSink = state.eventSink - UserStoryFlowPage( + FlowStepPage( canGoBack = true, onBackClicked = onBackClicked, title = title(state), diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt index 037a9c6bd2..75768faae3 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt @@ -31,7 +31,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.securebackup.impl.R import io.element.android.libraries.architecture.Async -import io.element.android.libraries.designsystem.atomic.pages.UserStoryFlowPage +import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreview @@ -54,7 +54,7 @@ fun SecureBackupDisableView( onDone() } } - UserStoryFlowPage( + FlowStepPage( modifier = modifier, canGoBack = true, onBackClicked = onBackClicked, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt index ad844a7a68..c6ce87babe 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.securebackup.impl.R import io.element.android.libraries.architecture.Async -import io.element.android.libraries.designsystem.atomic.pages.UserStoryFlowPage +import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -44,7 +44,7 @@ fun SecureBackupEnableView( onDone() } } - UserStoryFlowPage( + FlowStepPage( modifier = modifier, canGoBack = true, onBackClicked = onBackClicked, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt index f9b10a3686..97a8fe4aaa 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt @@ -26,7 +26,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.securebackup.impl.R import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyView -import io.element.android.libraries.designsystem.atomic.pages.UserStoryFlowPage +import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage import io.element.android.libraries.designsystem.components.async.AsyncView import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -48,7 +48,7 @@ fun SecureBackupEnterRecoveryKeyView( onErrorDismiss = { state.eventSink(SecureBackupEnterRecoveryKeyEvents.ClearDialog) }, ) - UserStoryFlowPage( + FlowStepPage( modifier = modifier, canGoBack = true, onBackClicked = onBackClicked, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt index cf96d68bd0..67b3ab92b1 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt @@ -29,7 +29,7 @@ import io.element.android.features.securebackup.impl.R import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyView import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.androidutils.system.startSharePlainTextIntent -import io.element.android.libraries.designsystem.atomic.pages.UserStoryFlowPage +import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -46,7 +46,7 @@ fun SecureBackupSetupView( onBackClicked: () -> Unit, modifier: Modifier = Modifier, ) { - UserStoryFlowPage( + FlowStepPage( modifier = modifier, canGoBack = state.canGoBack(), onBackClicked = onBackClicked, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt similarity index 97% rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt index e378a249ac..ed1fbc2c61 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/UserStoryFlowPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt @@ -47,7 +47,7 @@ import io.element.android.libraries.theme.ElementTheme */ @OptIn(ExperimentalMaterial3Api::class) @Composable -fun UserStoryFlowPage( +fun FlowStepPage( canGoBack: Boolean, onBackClicked: () -> Unit, iconResourceId: Int?, @@ -92,8 +92,8 @@ fun UserStoryFlowPage( @PreviewsDayNight @Composable -internal fun UserStoryFlowPagePreview() = ElementPreview { - UserStoryFlowPage( +internal fun FlowStepPagePreview() = ElementPreview { + FlowStepPage( canGoBack = true, onBackClicked = {}, title = "Title", diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-D_0_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-D_0_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-D_0_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-N_1_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_UserStoryFlowPage-N_1_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-N_1_null,NEXUS_5,1.0,en].png From 7bd2ccd2a94cd213fb8526ddb5b33659c32caf87 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 7 Nov 2023 10:52:12 +0100 Subject: [PATCH 11/12] FlowStepPage: make `onBackClicked` nullable and remove `canGoBack`. --- .../android/features/logout/impl/LogoutView.kt | 1 - .../impl/disable/SecureBackupDisableView.kt | 1 - .../impl/enable/SecureBackupEnableView.kt | 1 - .../impl/enter/SecureBackupEnterRecoveryKeyView.kt | 1 - .../securebackup/impl/setup/SecureBackupSetupView.kt | 3 +-- .../designsystem/atomic/pages/FlowStepPage.kt | 12 +++++------- 6 files changed, 6 insertions(+), 13 deletions(-) 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 88441f0e0c..5976ed47a0 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 @@ -58,7 +58,6 @@ fun LogoutView( val eventSink = state.eventSink FlowStepPage( - canGoBack = true, onBackClicked = onBackClicked, title = title(state), subTitle = subtitle(state), diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt index 75768faae3..cf3e6c48c5 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt @@ -56,7 +56,6 @@ fun SecureBackupDisableView( } FlowStepPage( modifier = modifier, - canGoBack = true, onBackClicked = onBackClicked, title = stringResource(id = R.string.screen_key_backup_disable_title), subTitle = stringResource(id = R.string.screen_key_backup_disable_description), diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt index c6ce87babe..e90832c5be 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt @@ -46,7 +46,6 @@ fun SecureBackupEnableView( } FlowStepPage( modifier = modifier, - canGoBack = true, onBackClicked = onBackClicked, title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), iconResourceId = CommonDrawables.ic_key, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt index 97a8fe4aaa..b5d7c9c349 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt @@ -50,7 +50,6 @@ fun SecureBackupEnterRecoveryKeyView( FlowStepPage( modifier = modifier, - canGoBack = true, onBackClicked = onBackClicked, iconResourceId = CommonDrawables.ic_key, title = stringResource(id = R.string.screen_recovery_key_confirm_title), diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt index 67b3ab92b1..83b1ddba9c 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt @@ -48,8 +48,7 @@ fun SecureBackupSetupView( ) { FlowStepPage( modifier = modifier, - canGoBack = state.canGoBack(), - onBackClicked = onBackClicked, + onBackClicked = onBackClicked.takeIf { state.canGoBack() }, title = title(state), subTitle = subtitle(state), iconResourceId = CommonDrawables.ic_key, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt index ed1fbc2c61..804e08ab2b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt @@ -40,7 +40,7 @@ import io.element.android.libraries.theme.ElementTheme /** * A Page with: - * - a top bar as TobAppBar with optional back button + * - a top bar as TobAppBar with optional back button (displayed if [onBackClicked] is not null) * - a header, as IconTitleSubtitleMolecule * - a content. * - a footer, as ButtonColumnMolecule @@ -48,24 +48,23 @@ import io.element.android.libraries.theme.ElementTheme @OptIn(ExperimentalMaterial3Api::class) @Composable fun FlowStepPage( - canGoBack: Boolean, - onBackClicked: () -> Unit, iconResourceId: Int?, title: String, modifier: Modifier = Modifier, + onBackClicked: (() -> Unit)? = null, subTitle: String? = null, content: @Composable () -> Unit = {}, buttons: @Composable ColumnScope.() -> Unit = {}, ) { - BackHandler(enabled = canGoBack) { - onBackClicked() + BackHandler(enabled = onBackClicked != null) { + onBackClicked?.invoke() } HeaderFooterPage( modifier = modifier, topBar = { TopAppBar( navigationIcon = { - if (canGoBack) { + if (onBackClicked != null) { BackButton(onClick = onBackClicked) } }, @@ -94,7 +93,6 @@ fun FlowStepPage( @Composable internal fun FlowStepPagePreview() = ElementPreview { FlowStepPage( - canGoBack = true, onBackClicked = {}, title = "Title", subTitle = "Subtitle", From b2d0504ec77d597d39cee3ebaa2c70a41ed505f8 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 7 Nov 2023 12:08:26 +0000 Subject: [PATCH 12/12] Update screenshots --- ...FlowStepPage_null_FlowStepPage-Day_0_null,NEXUS_5,1.0,en].png} | 0 ...owStepPage_null_FlowStepPage-Night_1_null,NEXUS_5,1.0,en].png} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-D_0_null,NEXUS_5,1.0,en].png => ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Day_0_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-N_1_null,NEXUS_5,1.0,en].png => ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Night_1_null,NEXUS_5,1.0,en].png} (100%) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Day_0_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-D_0_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Day_0_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Night_1_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_null_FlowStepPage-N_1_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.pages_FlowStepPage_null_FlowStepPage-Night_1_null,NEXUS_5,1.0,en].png