From 5a169e6211756916c448a899eb7797ce6136bb76 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Sep 2023 10:00:14 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../appsettings/AppSettingsAlertsFactory.kt | 28 ++ .../ui/appsettings/AppSettingsFragment.kt | 8 +- .../ui/appsettings/AppSettingsItemsFactory.kt | 48 ++++ .../ui/appsettings/AppSettingsScreen.kt | 253 +++++------------- .../ui/appsettings/AppSettingsScreenState.kt | 53 +++- .../ui/appsettings/AppSettingsViewModel.kt | 117 ++++++-- .../{EnrollBiometricsCard.kt => CardItem.kt} | 57 ++-- .../components/SettingsAlertDialog.kt | 107 +++----- .../ui/appsettings/components/SwitchItem.kt | 113 ++++++++ .../core/ui/extensions/TextReference.kt | 47 ++++ 10 files changed, 513 insertions(+), 318 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsAlertsFactory.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsItemsFactory.kt rename app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/{EnrollBiometricsCard.kt => CardItem.kt} (57%) create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SwitchItem.kt diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsAlertsFactory.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsAlertsFactory.kt new file mode 100644 index 0000000000..51cfcc0ad3 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsAlertsFactory.kt @@ -0,0 +1,28 @@ +package com.tangem.tap.features.details.ui.appsettings + +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Alert +import com.tangem.wallet.R + +internal class AppSettingsAlertsFactory { + + fun createDeleteSavedWalletsAlert(onDelete: () -> Unit, onDismiss: () -> Unit): Alert { + return Alert( + title = resourceReference(R.string.common_attention), + description = resourceReference(R.string.app_settings_off_saved_wallet_alert_message), + confirmText = resourceReference(R.string.common_delete), + onConfirm = onDelete, + onDismiss = onDismiss, + ) + } + + fun createDeleteSavedAccessCodesAlert(onDelete: () -> Unit, onDismiss: () -> Unit): Alert { + return Alert( + title = resourceReference(R.string.common_attention), + description = resourceReference(R.string.app_settings_off_saved_access_code_alert_message), + confirmText = resourceReference(R.string.common_delete), + onConfirm = onDelete, + onDismiss = onDismiss, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt index 8a7e4987df..ef5d881910 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt @@ -2,8 +2,6 @@ package com.tangem.tap.features.details.ui.appsettings import android.os.Bundle import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier import androidx.transition.TransitionInflater import com.tangem.core.navigation.NavigationAction @@ -24,14 +22,12 @@ internal class AppSettingsFragment : ComposeFragment(), StoreSubscriber = - mutableStateOf(viewModel.updateState(store.state.detailsState)) @Composable override fun ScreenContent(modifier: Modifier) { AppSettingsScreen( modifier = modifier, - state = screenState.value, + state = viewModel.uiState, onBackClick = { store.dispatch(DetailsAction.ResetCardSettingsData) store.dispatch(NavigationAction.PopBackTo()) @@ -73,6 +69,6 @@ internal class AppSettingsFragment : ComposeFragment(), StoreSubscriber Unit): Item.Card { + return Item.Card( + id = "enroll_biometrics_card", + title = resourceReference(R.string.app_settings_enable_biometrics_title), + description = resourceReference(R.string.app_settings_enable_biometrics_description), + iconResId = R.drawable.ic_alert_circle_24, + onClick = onClick, + ) + } + + fun createSaveWalletsSwitch( + isChecked: Boolean, + isEnabled: Boolean, + onCheckedChange: (Boolean) -> Unit, + ): Item.Switch { + return Item.Switch( + id = "save_wallets_switch", + title = resourceReference(R.string.app_settings_saved_wallet), + description = resourceReference(R.string.app_settings_saved_wallet_footer), + isEnabled = isEnabled, + isChecked = isChecked, + onCheckedChange = onCheckedChange, + ) + } + + fun createSaveAccessCodeSwitch( + isChecked: Boolean, + isEnabled: Boolean, + onCheckedChange: (Boolean) -> Unit, + ): Item.Switch { + return Item.Switch( + id = "save_access_codes_switch", + title = resourceReference(R.string.app_settings_saved_access_codes), + description = resourceReference(R.string.app_settings_saved_access_codes_footer), + isEnabled = isEnabled, + isChecked = isChecked, + onCheckedChange = onCheckedChange, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt index 327dcbe8c4..05405f7d3d 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt @@ -1,224 +1,113 @@ package com.tangem.tap.features.details.ui.appsettings -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.material.Text -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.components.SpacerH24 -import com.tangem.core.ui.components.SpacerH32 -import com.tangem.core.ui.components.SpacerH4 -import com.tangem.core.ui.components.SpacerW32 +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.features.details.redux.AppSetting -import com.tangem.tap.features.details.ui.appsettings.components.EnrollBiometricsCard +import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item +import com.tangem.tap.features.details.ui.appsettings.components.CardItem import com.tangem.tap.features.details.ui.appsettings.components.SettingsAlertDialog +import com.tangem.tap.features.details.ui.appsettings.components.SwitchItem import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold -import com.tangem.tap.features.details.ui.common.TangemSwitch import com.tangem.wallet.R +import kotlinx.collections.immutable.persistentListOf @Composable internal fun AppSettingsScreen(state: AppSettingsScreenState, onBackClick: () -> Unit, modifier: Modifier = Modifier) { SettingsScreensScaffold( modifier = modifier, - content = { AppSettings(state = state) }, + content = { + when (state) { + is AppSettingsScreenState.Content -> AppSettings(state = state) + is AppSettingsScreenState.Loading -> Unit + } + }, titleRes = R.string.app_settings_title, onBackClick = onBackClick, ) } @Composable -private fun AppSettings(state: AppSettingsScreenState) { - var dialogType by remember { mutableStateOf(null) } - val onDialogStateChange: (AppSetting?) -> Unit = { dialogType = it } - - dialogType?.let { - SettingsAlertDialog( - element = it, - onDialogStateChange = onDialogStateChange, - onSettingToggle = { state.onSettingToggled(it, false) }, - ) +private fun AppSettings(state: AppSettingsScreenState.Content) { + val alert by rememberUpdatedState(newValue = state.alert) + alert?.let { safeAlert -> + SettingsAlertDialog(alert = safeAlert) } - Column(modifier = Modifier.fillMaxSize()) { - if (state.showEnrollBiometricsCard) { - EnrollBiometricsCard(onClick = state.onEnrollBiometrics) - SpacerH24() - } - - AppSettingsElement( - state = state, - setting = AppSetting.SaveWallets, - onDialogStateChange = onDialogStateChange, - ) - SpacerH32() - AppSettingsElement( - state = state, - setting = AppSetting.SaveAccessCode, - onDialogStateChange = onDialogStateChange, - ) - } -} - -@Suppress("LongMethod") -@Composable -private fun AppSettingsElement( - state: AppSettingsScreenState, - setting: AppSetting, - onDialogStateChange: (AppSetting?) -> Unit, -) { - val titleRes = when (setting) { - AppSetting.SaveWallets -> R.string.app_settings_saved_wallet - AppSetting.SaveAccessCode -> R.string.app_settings_saved_access_codes - } - val subtitleRes = when (setting) { - AppSetting.SaveWallets -> R.string.app_settings_saved_wallet_footer - AppSetting.SaveAccessCode -> R.string.app_settings_saved_access_codes_footer - } - val checked = state.settings[setting] ?: false - - val titleTextColor by rememberUpdatedState( - newValue = if (state.isTogglesEnabled) { - TangemTheme.colors.text.primary1 - } else { - TangemTheme.colors.text.secondary - }, - ) - val descriptionTextColor by rememberUpdatedState( - newValue = if (state.isTogglesEnabled) { - TangemTheme.colors.text.secondary - } else { - TangemTheme.colors.text.tertiary - }, - ) - - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = TangemTheme.dimens.spacing20), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - ) { - Column( - modifier = Modifier.weight(weight = .9f), - verticalArrangement = Arrangement.Center, - ) { - Text( - text = stringResource(id = titleRes), - style = TangemTheme.typography.subtitle1, - color = titleTextColor, - ) - SpacerH4() - Text( - text = stringResource(id = subtitleRes), - style = TangemTheme.typography.body2, - color = descriptionTextColor, - ) - } - SpacerW32() - TangemSwitch( - checked = checked, - enabled = state.isTogglesEnabled, - onCheckedChange = { isChecked -> - onCheckedChange( - element = setting, - enabled = isChecked, - onSettingToggled = state.onSettingToggled, - onDialogStateChange = onDialogStateChange, + LazyColumn { + items( + items = state.items, + key = Item::id, + ) { item -> + when (item) { + is Item.Card -> CardItem( + modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16), + item = item, ) - }, - ) - } -} - -private fun onCheckedChange( - element: AppSetting, - enabled: Boolean, - onSettingToggled: (AppSetting, Boolean) -> Unit, - onDialogStateChange: (AppSetting?) -> Unit, -) { - // Show warning if user wants to disable the switch - if (!enabled) { - onDialogStateChange(element) - } else { - onSettingToggled(element, true) + is Item.Switch -> SwitchItem( + modifier = Modifier.padding( + vertical = TangemTheme.dimens.spacing16, + horizontal = TangemTheme.dimens.spacing20, + ), + item = item, + ) + } + } } } // region Preview -@Composable -private fun AppSettingsScreenSample(modifier: Modifier = Modifier) { - Column( - modifier = modifier - .background(TangemTheme.colors.background.primary), - ) { - AppSettingsScreen( - state = AppSettingsScreenState( - settings = mapOf( - AppSetting.SaveWallets to true, - AppSetting.SaveAccessCode to false, - ), - showEnrollBiometricsCard = false, - isTogglesEnabled = true, - onSettingToggled = { _, _ -> }, - onEnrollBiometrics = {}, - ), - onBackClick = { }, - ) - } -} - @Preview(showBackground = true, widthDp = 360) @Composable -private fun AppSettingsScreenPreview_Light() { +private fun AppSettingsScreenPreview_Light( + @PreviewParameter(AppSettingsScreenStateProvider::class) state: AppSettingsScreenState, +) { TangemTheme { - AppSettingsScreenSample() + AppSettingsScreen(state = state, onBackClick = {}) } } @Preview(showBackground = true, widthDp = 360) @Composable -private fun AppSettingsScreenPreview_Dark() { +private fun AppSettingsScreenPreview_Dark( + @PreviewParameter(AppSettingsScreenStateProvider::class) state: AppSettingsScreenState, +) { TangemTheme(isDark = true) { - AppSettingsScreenSample() + AppSettingsScreen(state = state, onBackClick = {}) } } -@Composable -private fun AppSettingsScreen_EnrollBiometrics_Sample(modifier: Modifier = Modifier) { - Column(modifier = modifier.background(TangemTheme.colors.background.primary)) { - AppSettingsScreen( - state = AppSettingsScreenState( - settings = mapOf( - AppSetting.SaveWallets to true, - AppSetting.SaveAccessCode to false, - ), - showEnrollBiometricsCard = true, - isTogglesEnabled = false, - onSettingToggled = { _, _ -> }, - onEnrollBiometrics = {}, - ), - onBackClick = { }, +private class AppSettingsScreenStateProvider : CollectionPreviewParameterProvider( + collection = buildList { + val itemsFactory = AppSettingsItemsFactory() + val dialogsFactory = AppSettingsAlertsFactory() + val items = persistentListOf( + itemsFactory.createEnrollBiometricsCard {}, + itemsFactory.createSaveWalletsSwitch(isChecked = true, isEnabled = true, { _ -> }), + itemsFactory.createSaveAccessCodeSwitch(isChecked = false, isEnabled = true) { _ -> }, ) - } -} -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun AppSettingsScreen_EnrollBiometrics_Preview_Light() { - TangemTheme { - AppSettingsScreen_EnrollBiometrics_Sample() - } -} + AppSettingsScreenState.Content( + items = items, + alert = null, + ).let(::add) -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun AppSettingsScreen_EnrollBiometrics_Preview_Dark() { - TangemTheme(isDark = true) { - AppSettingsScreen_EnrollBiometrics_Sample() - } -} + AppSettingsScreenState.Content( + items = items, + alert = dialogsFactory.createDeleteSavedWalletsAlert({}, {}), + ).let(::add) + + AppSettingsScreenState.Content( + items = items, + alert = dialogsFactory.createDeleteSavedAccessCodesAlert({}, {}), + ).let(::add) + }, +) // endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreenState.kt index b891b7b088..bee5e3241b 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreenState.kt @@ -1,11 +1,48 @@ package com.tangem.tap.features.details.ui.appsettings -import com.tangem.tap.features.details.redux.AppSetting +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Immutable +import com.tangem.core.ui.extensions.TextReference +import kotlinx.collections.immutable.ImmutableList -internal data class AppSettingsScreenState( - val settings: Map = emptyMap(), - val showEnrollBiometricsCard: Boolean = false, - val isTogglesEnabled: Boolean = false, - val onSettingToggled: (AppSetting, Boolean) -> Unit = { _, _ -> /* no-op */ }, - val onEnrollBiometrics: () -> Unit = { /* no-op */ }, -) \ No newline at end of file +@Immutable +internal sealed class AppSettingsScreenState { + + object Loading : AppSettingsScreenState() + + data class Content( + val items: ImmutableList, + val alert: Alert?, + ) : AppSettingsScreenState() + + @Immutable + sealed class Item { + + abstract val id: String + + data class Card( + override val id: String, + @DrawableRes val iconResId: Int, + val title: TextReference, + val description: TextReference, + val onClick: () -> Unit, + ) : Item() + + data class Switch( + override val id: String, + val title: TextReference, + val description: TextReference, + val isEnabled: Boolean, + val isChecked: Boolean, + val onCheckedChange: (Boolean) -> Unit, + ) : Item() + } + + data class Alert( + val title: TextReference, + val description: TextReference, + val confirmText: TextReference, + val onConfirm: () -> Unit, + val onDismiss: () -> Unit, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt index 112d1db938..98b3cead3b 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt @@ -1,35 +1,31 @@ package com.tangem.tap.features.details.ui.appsettings +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.details.redux.AppSetting +import com.tangem.tap.features.details.redux.AppSettingsState import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import org.rekotlin.Store internal class AppSettingsViewModel(private val store: Store) { - fun updateState(state: DetailsState): AppSettingsScreenState { - return with(state.appSettingsState) { - AppSettingsScreenState( - settings = mapOf( - AppSetting.SaveWallets to saveWallets, - AppSetting.SaveAccessCode to saveAccessCodes, - ), - showEnrollBiometricsCard = needEnrollBiometrics, - isTogglesEnabled = !needEnrollBiometrics && !isInProgress, - onSettingToggled = { privacySetting, enabled -> - onSettingsToggled(privacySetting, enabled) - }, - onEnrollBiometrics = { - store.dispatchOnMain(DetailsAction.AppSettings.EnrollBiometrics) - }, - ) - } - } + private val itemsFactory = AppSettingsItemsFactory() + private val alertsFactory = AppSettingsAlertsFactory() - private fun onSettingsToggled(setting: AppSetting, enable: Boolean) { - store.dispatch(DetailsAction.AppSettings.SwitchPrivacySetting(enable = enable, setting = setting)) + var uiState: AppSettingsScreenState by mutableStateOf(AppSettingsScreenState.Loading) + private set + + fun updateState(state: DetailsState) { + uiState = AppSettingsScreenState.Content( + items = buildItems(state.appSettingsState), + alert = (uiState as? AppSettingsScreenState.Content)?.alert, + ) } fun checkBiometricsStatus() { @@ -39,4 +35,85 @@ internal class AppSettingsViewModel(private val store: Store) { fun refreshBiometricsStatus() { store.dispatch(DetailsAction.AppSettings.CheckBiometricsStatus(awaitStatusChange = true)) } + + private fun buildItems(state: AppSettingsState): ImmutableList { + val items = buildList { + if (state.needEnrollBiometrics) { + itemsFactory.createEnrollBiometricsCard(onClick = ::enrollBiometrics).let(::add) + } + + if (state.isBiometricsAvailable) { + val canUseBiometrics = !state.needEnrollBiometrics && !state.isInProgress + + itemsFactory.createSaveWalletsSwitch( + isChecked = state.saveWallets, + isEnabled = canUseBiometrics, + onCheckedChange = ::onSaveWalletsToggled, + ).let(::add) + + itemsFactory.createSaveAccessCodeSwitch( + isChecked = state.saveAccessCodes, + isEnabled = canUseBiometrics, + onCheckedChange = ::onSaveAccessCodesToggled, + ).let(::add) + } + } + + return items.toImmutableList() + } + + private fun enrollBiometrics() { + store.dispatchOnMain(DetailsAction.AppSettings.EnrollBiometrics) + } + + private fun onSaveWalletsToggled(isChecked: Boolean) { + if (isChecked) { + onSettingsToggled(AppSetting.SaveWallets, enable = true) + } else { + updateContentState { + copy( + alert = alertsFactory.createDeleteSavedWalletsAlert( + onDelete = { + onSettingsToggled(AppSetting.SaveWallets, enable = false) + dismissDialog() + }, + onDismiss = ::dismissDialog, + ), + ) + } + } + } + + private fun onSaveAccessCodesToggled(isChecked: Boolean) { + if (isChecked) { + onSettingsToggled(AppSetting.SaveAccessCode, enable = true) + } else { + updateContentState { + copy( + alert = alertsFactory.createDeleteSavedAccessCodesAlert( + onDelete = { + onSettingsToggled(AppSetting.SaveAccessCode, enable = false) + dismissDialog() + }, + onDismiss = ::dismissDialog, + ), + ) + } + } + } + + private fun onSettingsToggled(setting: AppSetting, enable: Boolean) { + store.dispatch(DetailsAction.AppSettings.SwitchPrivacySetting(enable = enable, setting = setting)) + } + + private fun dismissDialog() { + updateContentState { copy(alert = null) } + } + + private fun updateContentState(block: AppSettingsScreenState.Content.() -> AppSettingsScreenState.Content) { + uiState = when (val state = uiState) { + is AppSettingsScreenState.Content -> block(state) + is AppSettingsScreenState.Loading -> state + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/EnrollBiometricsCard.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/CardItem.kt similarity index 57% rename from app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/EnrollBiometricsCard.kt rename to app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/CardItem.kt index 1386a364d7..7643c0fd09 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/EnrollBiometricsCard.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/CardItem.kt @@ -1,11 +1,6 @@ package com.tangem.tap.features.details.ui.appsettings.components -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon import androidx.compose.material.Surface @@ -14,24 +9,25 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.SpacerH4 import com.tangem.core.ui.components.SpacerW16 +import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme -import com.tangem.wallet.R +import com.tangem.tap.features.details.ui.appsettings.AppSettingsItemsFactory +import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item @OptIn(ExperimentalMaterialApi::class) @Composable -internal fun EnrollBiometricsCard(onClick: () -> Unit) { +internal fun CardItem(item: Item.Card, modifier: Modifier = Modifier) { Surface( - modifier = Modifier - .padding(horizontal = TangemTheme.dimens.spacing8) - .fillMaxWidth(), - color = TangemTheme.colors.background.primary, + modifier = modifier.fillMaxWidth(), + color = TangemTheme.colors.button.disabled, shape = TangemTheme.shapes.roundedCornersLarge, - onClick = onClick, + onClick = item.onClick, ) { Row( modifier = Modifier.padding(all = 16.dp), @@ -39,20 +35,20 @@ internal fun EnrollBiometricsCard(onClick: () -> Unit) { horizontalArrangement = Arrangement.SpaceEvenly, ) { Icon( - painter = painterResource(id = R.drawable.ic_alert_circle_24), + painter = painterResource(id = item.iconResId), tint = TangemTheme.colors.icon.attention, contentDescription = null, ) SpacerW16() Column { Text( - text = stringResource(id = R.string.app_settings_enable_biometrics_title), + text = item.title.resolveReference(), style = TangemTheme.typography.subtitle1, color = TangemTheme.colors.text.primary1, ) SpacerH4() Text( - text = stringResource(id = R.string.app_settings_enable_biometrics_description), + text = item.description.resolveReference(), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.secondary, ) @@ -62,28 +58,29 @@ internal fun EnrollBiometricsCard(onClick: () -> Unit) { } // region Preview -@Composable -private fun EnrollBiometricsCardSample(modifier: Modifier = Modifier) { - Column( - modifier = modifier.background(TangemTheme.colors.background.secondary), - ) { - EnrollBiometricsCard(onClick = {}) - } -} - @Preview(showBackground = true, widthDp = 360) @Composable -private fun EnrollBiometricsCardPreview_Light() { +private fun CardItemPreview_Light(@PreviewParameter(CardItemProvider::class) item: Item.Card) { TangemTheme { - EnrollBiometricsCardSample() + CardItem(item = item) } } @Preview(showBackground = true, widthDp = 360) @Composable -private fun EnrollBiometricsCardPreview_Dark() { +private fun CardItemPreview_Dark(@PreviewParameter(CardItemProvider::class) item: Item.Card) { TangemTheme(isDark = true) { - EnrollBiometricsCardSample() + CardItem(item = item) } } + +private class CardItemProvider : CollectionPreviewParameterProvider( + collection = buildList { + val itemsFactory = AppSettingsItemsFactory() + + itemsFactory.createEnrollBiometricsCard( + onClick = { /* no-op */ }, + ).let(::add) + }, +) // endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsAlertDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsAlertDialog.kt index c82113133b..e592fb6cf0 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsAlertDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsAlertDialog.kt @@ -1,97 +1,60 @@ package com.tangem.tap.features.details.ui.appsettings.components -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.material.AlertDialog -import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.components.TextButton -import com.tangem.core.ui.components.WarningTextButton +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import com.tangem.core.ui.components.BasicDialog +import com.tangem.core.ui.components.DialogButton +import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.features.details.redux.AppSetting +import com.tangem.tap.features.details.ui.appsettings.AppSettingsAlertsFactory +import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Alert import com.tangem.wallet.R @Composable -internal fun SettingsAlertDialog( - element: AppSetting, - onDialogStateChange: (AppSetting?) -> Unit, - onSettingToggle: () -> Unit, -) { - val text = when (element) { - AppSetting.SaveWallets -> R.string.app_settings_off_saved_wallet_alert_message - AppSetting.SaveAccessCode -> R.string.app_settings_off_saved_access_code_alert_message - } - - AlertDialog( - onDismissRequest = { onDialogStateChange(null) }, - confirmButton = { - TextButton( - modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12), - text = stringResource(id = R.string.common_cancel), - onClick = { - onDialogStateChange(null) - }, - ) - }, - dismissButton = { - WarningTextButton( - text = stringResource(id = R.string.common_delete), - onClick = { - onDialogStateChange(null) - onSettingToggle() - }, - ) - }, - title = { - Text( - text = stringResource(id = R.string.common_attention), - color = TangemTheme.colors.text.primary1, - style = TangemTheme.typography.h2, - ) - }, - text = { - Text( - text = stringResource(id = text), - color = TangemTheme.colors.text.secondary, - style = TangemTheme.typography.body2, - ) - }, - shape = TangemTheme.shapes.roundedCornersLarge, +internal fun SettingsAlertDialog(alert: Alert) { + BasicDialog( + title = alert.title.resolveReference(), + message = alert.description.resolveReference(), + isDismissable = false, + confirmButton = DialogButton( + title = alert.confirmText.resolveReference(), + warning = true, + onClick = alert.onConfirm, + ), + dismissButton = DialogButton( + title = stringResource(id = R.string.common_cancel), + onClick = alert.onDismiss, + ), + onDismissDialog = alert.onDismiss, ) } // region Preview -@Composable -private fun SettingsAlertDialogSample(modifier: Modifier = Modifier) { - Column( - modifier = modifier - .background(TangemTheme.colors.background.primary), - ) { - SettingsAlertDialog( - element = AppSetting.SaveAccessCode, - onDialogStateChange = {}, - onSettingToggle = { }, - ) - } -} - @Preview(showBackground = true, widthDp = 360) @Composable -private fun SettingsAlertDialogPreview_Light() { +private fun AlertDialogPreview_Light(@PreviewParameter(AlertDialogProvider::class) dialog: Alert) { TangemTheme { - SettingsAlertDialogSample() + SettingsAlertDialog(alert = dialog) } } @Preview(showBackground = true, widthDp = 360) @Composable -private fun SettingsAlertDialogPreview_Dark() { +private fun AlertDialogPreview_Dark(@PreviewParameter(AlertDialogProvider::class) dialog: Alert) { TangemTheme(isDark = true) { - SettingsAlertDialogSample() + SettingsAlertDialog(alert = dialog) } } + +private class AlertDialogProvider : CollectionPreviewParameterProvider( + collection = buildList { + val alertsFactory = AppSettingsAlertsFactory() + + alertsFactory.createDeleteSavedAccessCodesAlert({}, {}).let(::add) + alertsFactory.createDeleteSavedWalletsAlert({}, {}).let(::add) + }, +) // endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SwitchItem.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SwitchItem.kt new file mode 100644 index 0000000000..8d918ced3a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SwitchItem.kt @@ -0,0 +1,113 @@ +package com.tangem.tap.features.details.ui.appsettings.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import com.tangem.core.ui.components.SpacerH4 +import com.tangem.core.ui.components.SpacerW32 +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.tap.features.details.ui.appsettings.AppSettingsItemsFactory +import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item +import com.tangem.tap.features.details.ui.common.TangemSwitch + +@Composable +internal fun SwitchItem(item: Item.Switch, modifier: Modifier = Modifier) { + val titleTextColor by rememberUpdatedState( + newValue = if (item.isEnabled) { + TangemTheme.colors.text.primary1 + } else { + TangemTheme.colors.text.secondary + }, + ) + val descriptionTextColor by rememberUpdatedState( + newValue = if (item.isEnabled) { + TangemTheme.colors.text.secondary + } else { + TangemTheme.colors.text.tertiary + }, + ) + + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Column( + modifier = Modifier.weight(weight = .9f), + verticalArrangement = Arrangement.Center, + ) { + Text( + text = item.title.resolveReference(), + style = TangemTheme.typography.subtitle1, + color = titleTextColor, + ) + SpacerH4() + Text( + text = item.description.resolveReference(), + style = TangemTheme.typography.body2, + color = descriptionTextColor, + ) + } + SpacerW32() + TangemSwitch( + checked = item.isChecked, + enabled = item.isEnabled, + onCheckedChange = item.onCheckedChange, + ) + } +} + +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun SwitchItemPreview_Light(@PreviewParameter(SwitchItemProvider::class) item: Item.Switch) { + TangemTheme { + SwitchItem(item = item) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun SwitchItemPreview_Dark(@PreviewParameter(SwitchItemProvider::class) item: Item.Switch) { + TangemTheme(isDark = true) { + SwitchItem(item = item) + } +} + +private class SwitchItemProvider : CollectionPreviewParameterProvider( + collection = buildList { + val itemsFactory = AppSettingsItemsFactory() + + itemsFactory.createSaveAccessCodeSwitch( + isChecked = true, + isEnabled = true, + onCheckedChange = { /* no-op */ }, + ).let(::add) + itemsFactory.createSaveAccessCodeSwitch( + isChecked = false, + isEnabled = true, + onCheckedChange = { /* no-op */ }, + ).let(::add) + itemsFactory.createSaveAccessCodeSwitch( + isChecked = true, + isEnabled = false, + onCheckedChange = { /* no-op */ }, + ).let(::add) + itemsFactory.createSaveAccessCodeSwitch( + isChecked = false, + isEnabled = false, + onCheckedChange = { /* no-op */ }, + ).let(::add) + }, +) +// endregion Preview \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt index bc8107d5f5..f62df1c8c3 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt @@ -50,6 +50,53 @@ sealed interface TextReference { data class Combined(val refs: WrappedList) : TextReference } +/** + * Creates a [TextReference] using a string resource ID with optional format arguments. + * + * @param id The resource ID of the string. + * @param formatArgs A list of format arguments to be applied to the string resource. + * @return A [TextReference] representing the string resource with format arguments. + */ +fun resourceReference(@StringRes id: Int, formatArgs: WrappedList = WrappedList(emptyList())): TextReference { + return TextReference.Res(id, formatArgs) +} + +/** + * Creates a [TextReference] using a plain string value. + * + * @param value The plain string value. + * @return A [TextReference] representing the provided string value. + */ +fun stringReference(value: String): TextReference { + return TextReference.Str(value) +} + +/** + * Creates a [TextReference] using a plural string resource ID with count and optional format arguments. + * + * @param id The resource ID of the plural string. + * @param count The count value to determine the plural form. + * @param formatArgs A list of format arguments to be applied to the plural string resource. + * @return A [TextReference] representing the plural string resource with count and format arguments. + */ +fun pluralReference( + @PluralsRes id: Int, + count: Int, + formatArgs: WrappedList = WrappedList(emptyList()), +): TextReference { + return TextReference.PluralRes(id, count, formatArgs) +} + +/** + * Combines multiple [TextReference] instances into a single [TextReference]. + * + * @param refs A list of [TextReference] instances to be combined. + * @return A [TextReference] representing the combined text references. + */ +fun combinedReference(refs: WrappedList): TextReference { + return TextReference.Combined(refs) +} + /** Resolve [TextReference] as [String] */ @Composable @ReadOnlyComposable