diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 47d99024bd..99a413b1af 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -23,6 +23,7 @@ import com.tangem.datasource.config.FeaturesLocalLoader import com.tangem.datasource.config.models.Config import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.domain.appcurrency.repository.AppCurrencyRepository +import com.tangem.domain.apptheme.repository.AppThemeModeRepository import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.common.LogConfig import com.tangem.domain.tokens.repository.CurrenciesRepository @@ -173,6 +174,9 @@ class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var currenciesRepository: CurrenciesRepository + @Inject + lateinit var appThemeModeRepository: AppThemeModeRepository + override fun onCreate() { super.onCreate() @@ -195,6 +199,7 @@ class TapApplication : Application(), ImageLoaderFactory { walletManagersFacade = walletManagersFacade, appStateHolder = appStateHolder, currenciesRepository = currenciesRepository, + appThemeModeRepository = appThemeModeRepository, ), ), ) diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index 2d75cf1723..5e159ae358 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -6,6 +6,7 @@ import com.tangem.common.CompletionResult import com.tangem.common.core.TangemError import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.config.models.ChatConfig +import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.tap.common.analytics.topup.TopUpController @@ -103,4 +104,5 @@ sealed class GlobalAction : Action { } data class UpdateUserWalletsListManager(val manager: UserWalletsListManager) : GlobalAction() + data class ChangeAppThemeMode(val appThemeMode: AppThemeMode) : GlobalAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index d26e36149c..a380ee16b0 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -91,6 +91,9 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde userWalletsListManager = action.manager, ) } + is GlobalAction.ChangeAppThemeMode -> globalState.copy( + appThemeMode = action.appThemeMode, + ) else -> globalState } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 1e5a3cfce0..f5a30ba673 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -1,6 +1,7 @@ package com.tangem.tap.common.redux.global import com.tangem.datasource.config.ConfigManager +import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.tap.common.analytics.topup.TopUpController @@ -29,6 +30,7 @@ data class GlobalState( val userCountryCode: String? = null, val userWalletsListManager: UserWalletsListManager? = null, val topUpController: TopUpController? = null, + val appThemeMode: AppThemeMode = AppThemeMode.DEFAULT, ) : StateType typealias CryptoCurrencyName = String diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index c2112f12fe..1e719df7c7 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.details.redux +import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse @@ -68,6 +69,10 @@ sealed class DetailsAction : Action { data class BiometricsStatusChanged( val needEnrollBiometrics: Boolean, ) : AppSettings() + + data class ChangeAppThemeMode( + val appThemeMode: AppThemeMode, + ) : AppSettings() } data class ChangeAppCurrency(val fiatCurrency: FiatCurrency) : DetailsAction() diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 5778b35562..2f6f8ec7cc 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -9,6 +9,7 @@ import com.tangem.common.flatMap import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction +import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.ScanResponse @@ -221,6 +222,9 @@ class DetailsMiddleware { is DetailsAction.AppSettings.EnrollBiometrics -> { enrollBiometrics() } + is DetailsAction.AppSettings.ChangeAppThemeMode -> { + changeAppThemeMode(action.appThemeMode) + } is DetailsAction.AppSettings.SwitchPrivacySetting.Success, is DetailsAction.AppSettings.SwitchPrivacySetting.Failure, is DetailsAction.AppSettings.BiometricsStatusChanged, @@ -252,6 +256,14 @@ class DetailsMiddleware { store.dispatchOnMain(NavigationAction.OpenBiometricsSettings) } + private fun changeAppThemeMode(appThemeMode: AppThemeMode) { + val repository = store.state.daggerGraphState.get(DaggerGraphState::appThemeModeRepository) + + scope.launch { + repository.changeAppThemeMode(appThemeMode) + } + } + private fun toggleSaveWallets(state: DetailsState, enable: Boolean) = scope.launch { // Nothing to change if (preferencesStorage.shouldSaveUserWallets == enable) { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 77c3972fac..1b70773cc1 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -58,6 +58,7 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen): DetailsSta saveWallets = preferencesStorage.shouldSaveUserWallets, saveAccessCodes = preferencesStorage.shouldSaveAccessCodes, selectedFiatCurrency = store.state.globalState.appCurrency, + selectedThemeMode = store.state.globalState.appThemeMode, ), ) } @@ -196,6 +197,11 @@ private fun handlePrivacyAction(action: DetailsAction.AppSettings, state: Detail needEnrollBiometrics = action.needEnrollBiometrics, ), ) + is DetailsAction.AppSettings.ChangeAppThemeMode -> state.copy( + appSettingsState = state.appSettingsState.copy( + selectedThemeMode = action.appThemeMode, + ), + ) is DetailsAction.AppSettings.EnrollBiometrics, is DetailsAction.AppSettings.CheckBiometricsStatus, -> state diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index 3dcf9ddd33..14e533ed54 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.details.redux +import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.tap.common.entities.Button @@ -55,6 +56,7 @@ data class AppSettingsState( val needEnrollBiometrics: Boolean = false, val isInProgress: Boolean = false, val selectedFiatCurrency: FiatCurrency = FiatCurrency.Default, + val selectedThemeMode: AppThemeMode = AppThemeMode.DEFAULT, ) enum class SecurityOption { LongTap, PassCode, AccessCode } 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 deleted file mode 100644 index 51cfcc0ad3..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsAlertsFactory.kt +++ /dev/null @@ -1,28 +0,0 @@ -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/AppSettingsDialogsFactory.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsDialogsFactory.kt new file mode 100644 index 0000000000..43e66b065a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsDialogsFactory.kt @@ -0,0 +1,58 @@ +package com.tangem.tap.features.details.ui.appsettings + +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.domain.apptheme.model.AppThemeMode +import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Dialog +import com.tangem.wallet.R +import kotlinx.collections.immutable.toImmutableList + +internal class AppSettingsDialogsFactory { + + fun createDeleteSavedWalletsAlert(onDelete: () -> Unit, onDismiss: () -> Unit): Dialog.Alert { + return Dialog.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): Dialog.Alert { + return Dialog.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, + ) + } + + fun createThemeModeSelectorDialog( + selectedModeIndex: Int, + onSelect: (AppThemeMode) -> Unit, + onDismiss: () -> Unit, + ): Dialog.Selector { + val modes = AppThemeMode.available + + return Dialog.Selector( + title = resourceReference(R.string.app_settings_theme_selector_title), + selectedItemIndex = selectedModeIndex, + items = modes.map { mode -> + resourceReference( + id = when (mode) { + AppThemeMode.FORCE_DARK -> R.string.app_settings_theme_mode_dark + AppThemeMode.FORCE_LIGHT -> R.string.app_settings_theme_mode_light + AppThemeMode.FOLLOW_SYSTEM -> R.string.app_settings_theme_mode_system + }, + ) + }.toImmutableList(), + onSelect = { index -> + val mode = AppThemeMode.available[index] + + onSelect(mode) + }, + onDismiss = onDismiss, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsItemsFactory.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsItemsFactory.kt index a6a23da4b5..783325f318 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsItemsFactory.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsItemsFactory.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.details.ui.appsettings import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference +import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item import com.tangem.wallet.R @@ -56,4 +57,20 @@ internal class AppSettingsItemsFactory { onClick = onClick, ) } + + fun createSelectThemeModeButton(currentThemeMode: AppThemeMode, onClick: () -> Unit): Item.Button { + return Item.Button( + id = "select_theme_mode_button", + title = resourceReference(R.string.app_settings_theme_selector_title), + description = resourceReference( + id = when (currentThemeMode) { + AppThemeMode.FORCE_DARK -> R.string.app_settings_theme_mode_dark + AppThemeMode.FORCE_LIGHT -> R.string.app_settings_theme_mode_light + AppThemeMode.FOLLOW_SYSTEM -> R.string.app_settings_theme_mode_system + }, + ), + isEnabled = true, + onClick = onClick, + ) + } } \ 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 aff04f20a9..314d4634b3 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 @@ -11,11 +11,9 @@ 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.res.TangemTheme +import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item -import com.tangem.tap.features.details.ui.appsettings.components.ButtonItem -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.appsettings.components.* import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold import com.tangem.wallet.R import kotlinx.collections.immutable.persistentListOf @@ -37,9 +35,11 @@ internal fun AppSettingsScreen(state: AppSettingsScreenState, onBackClick: () -> @Composable private fun AppSettings(state: AppSettingsScreenState.Content) { - val alert by rememberUpdatedState(newValue = state.alert) - alert?.let { safeAlert -> - SettingsAlertDialog(alert = safeAlert) + val dialog by rememberUpdatedState(newValue = state.dialog) + when (val safeDialog = dialog) { + is AppSettingsScreenState.Dialog.Alert -> SettingsAlertDialog(dialog = safeDialog) + is AppSettingsScreenState.Dialog.Selector -> SettingsSelectorDialog(dialog = safeDialog) + null -> Unit } LazyColumn { @@ -48,15 +48,15 @@ private fun AppSettings(state: AppSettingsScreenState.Content) { key = Item::id, ) { item -> when (item) { - is Item.Card -> CardItem( + is Item.Card -> SettingsCardItem( modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16), item = item, ) - is Item.Button -> ButtonItem( + is Item.Button -> SettingsButtonItem( modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8), item = item, ) - is Item.Switch -> SwitchItem( + is Item.Switch -> SettingsSwitchItem( modifier = Modifier.padding( vertical = TangemTheme.dimens.spacing16, horizontal = TangemTheme.dimens.spacing20, @@ -92,27 +92,17 @@ private fun AppSettingsScreenPreview_Dark( private class AppSettingsScreenStateProvider : CollectionPreviewParameterProvider( collection = buildList { val itemsFactory = AppSettingsItemsFactory() - val dialogsFactory = AppSettingsAlertsFactory() val items = persistentListOf( itemsFactory.createEnrollBiometricsCard {}, itemsFactory.createSelectAppCurrencyButton(currentAppCurrencyName = "US Dollar") {}, itemsFactory.createSaveWalletsSwitch(isChecked = true, isEnabled = true, { _ -> }), itemsFactory.createSaveAccessCodeSwitch(isChecked = false, isEnabled = true) { _ -> }, + itemsFactory.createSelectThemeModeButton(AppThemeMode.DEFAULT, {}), ) AppSettingsScreenState.Content( items = items, - alert = null, - ).let(::add) - - AppSettingsScreenState.Content( - items = items, - alert = dialogsFactory.createDeleteSavedWalletsAlert({}, {}), - ).let(::add) - - AppSettingsScreenState.Content( - items = items, - alert = dialogsFactory.createDeleteSavedAccessCodesAlert({}, {}), + dialog = null, ).let(::add) }, ) 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 4802004976..108b30fec8 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 @@ -12,7 +12,7 @@ internal sealed class AppSettingsScreenState { data class Content( val items: ImmutableList, - val alert: Alert?, + val dialog: Dialog?, ) : AppSettingsScreenState() @Immutable @@ -46,11 +46,25 @@ internal sealed class AppSettingsScreenState { ) : Item() } - data class Alert( - val title: TextReference, - val description: TextReference, - val confirmText: TextReference, - val onConfirm: () -> Unit, - val onDismiss: () -> Unit, - ) + @Immutable + sealed class Dialog { + + abstract val onDismiss: () -> Unit + + data class Alert( + val title: TextReference, + val description: TextReference, + val confirmText: TextReference, + val onConfirm: () -> Unit, + override val onDismiss: () -> Unit, + ) : Dialog() + + data class Selector( + val title: TextReference, + val selectedItemIndex: Int, + val items: ImmutableList, + val onSelect: (Int) -> Unit, + override val onDismiss: () -> Unit, + ) : Dialog() + } } \ 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 5aedd6f7b7..10e6eeaf51 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 @@ -3,6 +3,7 @@ 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.domain.apptheme.model.AppThemeMode import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.details.redux.AppSetting @@ -17,7 +18,7 @@ import org.rekotlin.Store internal class AppSettingsViewModel(private val store: Store) { private val itemsFactory = AppSettingsItemsFactory() - private val alertsFactory = AppSettingsAlertsFactory() + private val dialogsFactory = AppSettingsDialogsFactory() var uiState: AppSettingsScreenState by mutableStateOf(AppSettingsScreenState.Loading) private set @@ -25,7 +26,7 @@ internal class AppSettingsViewModel(private val store: Store) { fun updateState(state: DetailsState) { uiState = AppSettingsScreenState.Content( items = buildItems(state.appSettingsState), - alert = (uiState as? AppSettingsScreenState.Content)?.alert, + dialog = (uiState as? AppSettingsScreenState.Content)?.dialog, ) } @@ -63,6 +64,10 @@ internal class AppSettingsViewModel(private val store: Store) { onCheckedChange = ::onSaveAccessCodesToggled, ).let(::add) } + + itemsFactory.createSelectThemeModeButton(state.selectedThemeMode) { + showThemeModeSelector(state.selectedThemeMode) + }.let(::add) } return items.toImmutableList() @@ -76,13 +81,28 @@ internal class AppSettingsViewModel(private val store: Store) { store.dispatchOnMain(WalletAction.AppCurrencyAction.ChooseAppCurrency) } + private fun showThemeModeSelector(selectedMode: AppThemeMode) { + updateContentState { + copy( + dialog = dialogsFactory.createThemeModeSelectorDialog( + selectedModeIndex = selectedMode.ordinal, + onSelect = { mode -> + store.dispatchOnMain(DetailsAction.AppSettings.ChangeAppThemeMode(mode)) + dismissDialog() + }, + onDismiss = ::dismissDialog, + ), + ) + } + } + private fun onSaveWalletsToggled(isChecked: Boolean) { if (isChecked) { onSettingsToggled(AppSetting.SaveWallets, enable = true) } else { updateContentState { copy( - alert = alertsFactory.createDeleteSavedWalletsAlert( + dialog = dialogsFactory.createDeleteSavedWalletsAlert( onDelete = { onSettingsToggled(AppSetting.SaveWallets, enable = false) dismissDialog() @@ -100,7 +120,7 @@ internal class AppSettingsViewModel(private val store: Store) { } else { updateContentState { copy( - alert = alertsFactory.createDeleteSavedAccessCodesAlert( + dialog = dialogsFactory.createDeleteSavedAccessCodesAlert( onDelete = { onSettingsToggled(AppSetting.SaveAccessCode, enable = false) dismissDialog() @@ -117,7 +137,7 @@ internal class AppSettingsViewModel(private val store: Store) { } private fun dismissDialog() { - updateContentState { copy(alert = null) } + updateContentState { copy(dialog = null) } } private fun updateContentState(block: AppSettingsScreenState.Content.() -> AppSettingsScreenState.Content) { 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 e592fb6cf0..1dce42e622 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 @@ -9,52 +9,52 @@ 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.ui.appsettings.AppSettingsAlertsFactory -import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Alert +import com.tangem.tap.features.details.ui.appsettings.AppSettingsDialogsFactory +import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Dialog import com.tangem.wallet.R @Composable -internal fun SettingsAlertDialog(alert: Alert) { +internal fun SettingsAlertDialog(dialog: Dialog.Alert) { BasicDialog( - title = alert.title.resolveReference(), - message = alert.description.resolveReference(), + title = dialog.title.resolveReference(), + message = dialog.description.resolveReference(), isDismissable = false, confirmButton = DialogButton( - title = alert.confirmText.resolveReference(), + title = dialog.confirmText.resolveReference(), warning = true, - onClick = alert.onConfirm, + onClick = dialog.onConfirm, ), dismissButton = DialogButton( title = stringResource(id = R.string.common_cancel), - onClick = alert.onDismiss, + onClick = dialog.onDismiss, ), - onDismissDialog = alert.onDismiss, + onDismissDialog = dialog.onDismiss, ) } // region Preview @Preview(showBackground = true, widthDp = 360) @Composable -private fun AlertDialogPreview_Light(@PreviewParameter(AlertDialogProvider::class) dialog: Alert) { +private fun AlertDialogPreview_Light(@PreviewParameter(AlertDialogProvider::class) dialog: Dialog.Alert) { TangemTheme { - SettingsAlertDialog(alert = dialog) + SettingsAlertDialog(dialog = dialog) } } @Preview(showBackground = true, widthDp = 360) @Composable -private fun AlertDialogPreview_Dark(@PreviewParameter(AlertDialogProvider::class) dialog: Alert) { +private fun AlertDialogPreview_Dark(@PreviewParameter(AlertDialogProvider::class) dialog: Dialog.Alert) { TangemTheme(isDark = true) { - SettingsAlertDialog(alert = dialog) + SettingsAlertDialog(dialog = dialog) } } -private class AlertDialogProvider : CollectionPreviewParameterProvider( +private class AlertDialogProvider : CollectionPreviewParameterProvider( collection = buildList { - val alertsFactory = AppSettingsAlertsFactory() + val dialogsFactory = AppSettingsDialogsFactory() - alertsFactory.createDeleteSavedAccessCodesAlert({}, {}).let(::add) - alertsFactory.createDeleteSavedWalletsAlert({}, {}).let(::add) + dialogsFactory.createDeleteSavedAccessCodesAlert({}, {}).let(::add) + dialogsFactory.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/ButtonItem.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsButtonItem.kt similarity index 94% rename from app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/ButtonItem.kt rename to app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsButtonItem.kt index 4010f633ed..946c62d2f2 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/ButtonItem.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsButtonItem.kt @@ -20,7 +20,7 @@ import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Ite @OptIn(ExperimentalMaterialApi::class) @Composable -internal fun ButtonItem(item: Item.Button, modifier: Modifier = Modifier) { +internal fun SettingsButtonItem(item: Item.Button, modifier: Modifier = Modifier) { Surface( modifier = modifier.fillMaxWidth(), color = TangemTheme.colors.background.secondary, @@ -55,7 +55,7 @@ internal fun ButtonItem(item: Item.Button, modifier: Modifier = Modifier) { @Composable private fun ButtonItemPreview_Light(@PreviewParameter(ButtonItemProvider::class) item: Item.Button) { TangemTheme { - ButtonItem(item = item) + SettingsButtonItem(item = item) } } @@ -63,7 +63,7 @@ private fun ButtonItemPreview_Light(@PreviewParameter(ButtonItemProvider::class) @Composable private fun ButtonItemPreview_Dark(@PreviewParameter(ButtonItemProvider::class) item: Item.Button) { TangemTheme(isDark = true) { - ButtonItem(item = item) + SettingsButtonItem(item = item) } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/CardItem.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsCardItem.kt similarity index 94% rename from app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/CardItem.kt rename to app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsCardItem.kt index 7643c0fd09..1560b14a43 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/CardItem.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsCardItem.kt @@ -22,7 +22,7 @@ import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Ite @OptIn(ExperimentalMaterialApi::class) @Composable -internal fun CardItem(item: Item.Card, modifier: Modifier = Modifier) { +internal fun SettingsCardItem(item: Item.Card, modifier: Modifier = Modifier) { Surface( modifier = modifier.fillMaxWidth(), color = TangemTheme.colors.button.disabled, @@ -62,7 +62,7 @@ internal fun CardItem(item: Item.Card, modifier: Modifier = Modifier) { @Composable private fun CardItemPreview_Light(@PreviewParameter(CardItemProvider::class) item: Item.Card) { TangemTheme { - CardItem(item = item) + SettingsCardItem(item = item) } } @@ -70,7 +70,7 @@ private fun CardItemPreview_Light(@PreviewParameter(CardItemProvider::class) ite @Composable private fun CardItemPreview_Dark(@PreviewParameter(CardItemProvider::class) item: Item.Card) { TangemTheme(isDark = true) { - CardItem(item = item) + SettingsCardItem(item = item) } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsSelectorDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsSelectorDialog.kt new file mode 100644 index 0000000000..8c1a93e307 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsSelectorDialog.kt @@ -0,0 +1,58 @@ +package com.tangem.tap.features.details.ui.appsettings.components + +import androidx.compose.runtime.Composable +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 com.tangem.core.ui.components.DialogButton +import com.tangem.core.ui.components.SelectorDialog +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.tap.features.details.ui.appsettings.AppSettingsDialogsFactory +import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Dialog +import com.tangem.wallet.R +import kotlinx.collections.immutable.toImmutableList + +@Composable +internal fun SettingsSelectorDialog(dialog: Dialog.Selector) { + SelectorDialog( + title = dialog.title.resolveReference(), + selectedItemIndex = dialog.selectedItemIndex, + items = dialog.items.map { it.resolveReference() }.toImmutableList(), + confirmButton = DialogButton( + title = stringResource(R.string.common_cancel), + onClick = dialog.onDismiss, + ), + onSelect = dialog.onSelect, + onDismissDialog = dialog.onDismiss, + ) +} + +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun SettingsSelectorDialogPreview_Light(@PreviewParameter(DialogProvider::class) param: Dialog.Selector) { + TangemTheme(isDark = false) { + SettingsSelectorDialog(param) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun SettingsSelectorDialogPreview_Dark(@PreviewParameter(DialogProvider::class) param: Dialog.Selector) { + TangemTheme(isDark = true) { + SettingsSelectorDialog(param) + } +} + +private class DialogProvider : CollectionPreviewParameterProvider( + collection = listOf( + AppSettingsDialogsFactory().createThemeModeSelectorDialog( + selectedModeIndex = 0, + onSelect = {}, + onDismiss = {}, + ), + ), +) +// 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/SettingsSwitchItem.kt similarity index 95% rename from app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SwitchItem.kt rename to app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsSwitchItem.kt index 8d918ced3a..78084743b7 100644 --- 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/SettingsSwitchItem.kt @@ -21,7 +21,7 @@ import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Ite import com.tangem.tap.features.details.ui.common.TangemSwitch @Composable -internal fun SwitchItem(item: Item.Switch, modifier: Modifier = Modifier) { +internal fun SettingsSwitchItem(item: Item.Switch, modifier: Modifier = Modifier) { val titleTextColor by rememberUpdatedState( newValue = if (item.isEnabled) { TangemTheme.colors.text.primary1 @@ -72,7 +72,7 @@ internal fun SwitchItem(item: Item.Switch, modifier: Modifier = Modifier) { @Composable private fun SwitchItemPreview_Light(@PreviewParameter(SwitchItemProvider::class) item: Item.Switch) { TangemTheme { - SwitchItem(item = item) + SettingsSwitchItem(item = item) } } @@ -80,7 +80,7 @@ private fun SwitchItemPreview_Light(@PreviewParameter(SwitchItemProvider::class) @Composable private fun SwitchItemPreview_Dark(@PreviewParameter(SwitchItemProvider::class) item: Item.Switch) { TangemTheme(isDark = true) { - SwitchItem(item = item) + SettingsSwitchItem(item = item) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/ResetBackupCardDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/ResetBackupCardDialog.kt index 0da9a7063a..0e7acbaa00 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/ResetBackupCardDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/ResetBackupCardDialog.kt @@ -15,7 +15,7 @@ object ResetBackupCardDialog { setPositiveButton(R.string.common_cancel) { _, _ -> /* no-op */ } - setNegativeButton(R.string.common_reset) { _, _ -> + setNegativeButton(R.string.card_settings_action_sheet_reset) { _, _ -> store.dispatch(BackupAction.ResetBackupCard(cardId)) } setOnDismissListener { diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 7a479780e6..7be3ae88ba 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -3,6 +3,7 @@ package com.tangem.tap.proxy.redux import com.tangem.datasource.asset.AssetReader import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.domain.appcurrency.repository.AppCurrencyRepository +import com.tangem.domain.apptheme.repository.AppThemeModeRepository import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository @@ -38,6 +39,7 @@ data class DaggerGraphState( val appCurrencyRepository: AppCurrencyRepository? = null, val walletManagersFacade: WalletManagersFacade? = null, val appStateHolder: AppStateHolder? = null, + val appThemeModeRepository: AppThemeModeRepository? = null, // FIXME: It is used only for TokensList screen. Remove after refactoring of TokensList val currenciesRepository: CurrenciesRepository? = null, diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 6277622e78..c7cf8aeddc 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -28,11 +28,16 @@ Подключите функцию хранения кодов доступа от карт на телефоне в зашифрованном виде, и при работе с картой вместо кода доступа будет запрашиваться биометрическая аутентификация. Cохранение кошелька Подключите функцию привязки карты в приложении, а также возможность биометрической аутентификации. Подпись транзакции все так же потребует карту. + Тёмная + Светлая + Как в системе + Тема Настройки приложения Пожалуйста, отсканируйте карту Пожалуйста, попробуйте снова через 30 секунд или отсканируйте карту Слишком много попыток Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона. + Начать резервное копирование %d карта %d карты @@ -43,6 +48,7 @@ Использовать эту карту для сброса кода доступа на других картах в этом кошельке Отключить возможность сброса кода доступа на этой карте или других картах этого кошелька Восстановление кода доступа + Сбросить Вы уверены, что хотите это сделать? Смена кода доступа Код доступа будет изменен только на данной карте @@ -96,7 +102,6 @@ Отклонить Перезагрузить Переименовать - Сбросить Сохранить изменения Искать Поиск токенов @@ -196,7 +201,7 @@ Управление токенами Чтобы защитить свои активы, мы советуем вам выполнить эту процедуру - Бэкап кошелька не был произведен + Резервное копирование не выполнено Баланс В сумме учтены не все монеты 1INCH токены будут зачислены на адрес вашего кошелька в сети %s в течение 48 часов @@ -210,6 +215,7 @@ Вам надо сгенерировать адреса для %d новых сетей, используя вашу карту Некоторые адреса отсутствуют + Невозможно покрыть %1$s комиссию Вам необходимо установить единый код доступа для защиты всех ваших карт Защита Позже вы сможете установить индивидуальный код доступа для каждой карты @@ -407,7 +413,7 @@ Расплачивайтесь Отправляйте Храните - Встречайте\nTangem + Встречайте Tangem Обменивайте, покупайте NFT, получайте займы и делайте вклады в более чем 100 различных децентрализованных сервисах Поддержка DeFi Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен. diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 186b92d9ea..02f330b04b 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -28,11 +28,16 @@ Biometric authentication will be requested instead of the access code for interactions with your card. Keep the wallet in the app Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. + Dark + Light + System default + Theme App Settings Please scan the card Please try again in 30 seconds or scan the card Too many attempts You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings. + Start backup process %d card %d cards @@ -41,6 +46,7 @@ Allows you to use this card to reset access code on other cards in this wallet Disable the ability to reset the access code on this card or other cards in this wallet Access code recovery + Reset Are you sure you want to do this? Change Access Code Access code will be changed on this card only @@ -95,7 +101,6 @@ Reject Reload Rename - Reset Save changes Search Search tokens @@ -194,7 +199,7 @@ Manage tokens To protect your assets, we advise you to carry out this procedure - Your wallet has not been backed up + Your wallet hasn\'t been backed up Total balance The amount does not include some of your funds 1INCH tokens will be credited to your %s wallet address within 48 hours @@ -206,6 +211,7 @@ You need to generate addresses for %d new networks using your card Some addresses are missing + Unable to cover %1$s fee You have to set up a single access code to protect all your wallets Protect You can set up an individual access code on each card later @@ -233,7 +239,7 @@ Skip for later How does it work? Let\'s generate all the keys on your card and create a secure wallet - Create a wallet + Create wallet Create a wallet Other options Your keys will be securely generated inside the card. There is no seed phrase, which means nobody can export or steal it. @@ -400,7 +406,7 @@ Pay Send Store - Meet\nTangem + Meet Tangem Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services DeFi Compatible Approvals are considered an industry standard across all decentralized exchanges and protect your wallet from being accessed by a smart contract without your permission. By design, smart contracts can\'t access your tokens unless you approve access from your end. By \"unlocking\" your tokens, you are give permission to the 1inch smart contract to spend your assets. The miners of the network are compensated with a gas fee (paid by you) to record this action on the blockchain. Once permission has been granted you will be able to swap your token. diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt index 24a2316d60..41e2d008b6 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt @@ -1,44 +1,33 @@ package com.tangem.core.ui.components +import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material.RadioButton +import androidx.compose.material.RadioButtonDefaults import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.TextFieldValue 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.window.Dialog import androidx.compose.ui.window.DialogProperties import com.tangem.core.ui.R +import com.tangem.core.ui.components.SelctorDialogParamsProvider.SelectorDialogParams import com.tangem.core.ui.res.TangemTheme - -/** - * Dialog button params - * - * @param title Button text. If not provided default values will be used - * @param warning If true then button text will be in theme warning color - * @param enabled If false button will be disabled - * @param onClick Button click callback - */ -data class DialogButton( - val title: String? = null, - val warning: Boolean = false, - val enabled: Boolean = true, - val onClick: () -> Unit, -) - -/** - * Additional params for dialog text field - */ -data class AdditionalTextInputDialogParams( - val label: String? = null, - val placeholder: String? = null, - val caption: String? = null, - val enabled: Boolean = true, - val isError: Boolean = false, -) +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList /** * Simple alert dialog with a message and 'OK' button @@ -129,6 +118,54 @@ fun TextInputDialog( ) } +@Composable +fun SelectorDialog( + selectedItemIndex: Int, + items: ImmutableList, + confirmButton: DialogButton, + onSelect: (index: Int) -> Unit, + onDismissDialog: () -> Unit, + title: String? = null, + isDismissable: Boolean = true, +) { + TangemDialog( + type = DialogType.Selector(selectedItemIndex, items, onSelect), + confirmButton = confirmButton, + title = title, + onDismissDialog = onDismissDialog, + properties = DialogProperties( + dismissOnBackPress = isDismissable, + dismissOnClickOutside = isDismissable, + ), + ) +} + +/** + * Dialog button params + * + * @param title Button text. If not provided default values will be used + * @param warning If true then button text will be in theme warning color + * @param enabled If false button will be disabled + * @param onClick Button click callback + */ +data class DialogButton( + val title: String? = null, + val warning: Boolean = false, + val enabled: Boolean = true, + val onClick: () -> Unit, +) + +/** + * Additional params for dialog text field + */ +data class AdditionalTextInputDialogParams( + val label: String? = null, + val placeholder: String? = null, + val caption: String? = null, + val enabled: Boolean = true, + val isError: Boolean = false, +) + // region Defaults @Composable private fun TangemDialog( @@ -146,15 +183,18 @@ private fun TangemDialog( shape = TangemTheme.shapes.roundedCornersLarge, color = TangemTheme.colors.background.plain, ) - .padding(all = TangemTheme.dimens.spacing24), + .padding(vertical = TangemTheme.dimens.spacing24), ) { if (title != null) { Text( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing24) + .fillMaxWidth(), text = title, style = when (type) { is DialogType.Message -> TangemTheme.typography.h2 is DialogType.TextInput -> TangemTheme.typography.h3 + is DialogType.Selector -> TangemTheme.typography.h2 }, color = TangemTheme.colors.text.primary1, ) @@ -162,7 +202,13 @@ private fun TangemDialog( } DialogContent(type = type) SpacerH24() - DialogButtons(confirmButton = confirmButton, dismissButton = dismissButton) + DialogButtons( + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing24) + .fillMaxWidth(), + confirmButton = confirmButton, + dismissButton = dismissButton, + ) } } } @@ -177,7 +223,9 @@ private fun DialogContent(type: DialogType, modifier: Modifier = Modifier) { when (type) { is DialogType.Message -> { Text( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing24) + .fillMaxWidth(), text = type.message, style = TangemTheme.typography.body2, color = TangemTheme.colors.text.secondary, @@ -185,7 +233,9 @@ private fun DialogContent(type: DialogType, modifier: Modifier = Modifier) { } is DialogType.TextInput -> { OutlineTextField( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing24) + .fillMaxWidth(), value = type.value, label = type.params.label, placeholder = type.params.placeholder, @@ -197,6 +247,14 @@ private fun DialogContent(type: DialogType, modifier: Modifier = Modifier) { }, ) } + is DialogType.Selector -> { + SelectorDialogContent( + modifier = Modifier.fillMaxWidth(), + selectedItemIndex = type.selectedItemIndex, + items = type.items, + onSelect = type.onSelect, + ) + } } } } @@ -204,7 +262,7 @@ private fun DialogContent(type: DialogType, modifier: Modifier = Modifier) { @Composable private fun DialogButtons(confirmButton: DialogButton, dismissButton: DialogButton?, modifier: Modifier = Modifier) { Row( - modifier = modifier.fillMaxWidth(), + modifier = modifier, horizontalArrangement = Arrangement.spacedBy( space = TangemTheme.dimens.spacing4, alignment = Alignment.End, @@ -252,14 +310,72 @@ private fun DialogButton( } } -private sealed interface DialogType { - data class Message(val message: String) : DialogType +@Composable +private fun SelectorDialogContent( + selectedItemIndex: Int, + items: ImmutableList, + onSelect: (index: Int) -> Unit, + modifier: Modifier = Modifier, +) { + LazyColumn(modifier = modifier) { + itemsIndexed(items = items) { index, itemText -> + val onClick = remember(index) { + { onSelect(index) } + } + + val interactionSource = remember { MutableInteractionSource() } + + Row( + modifier = Modifier + .clickable( + interactionSource = interactionSource, + indication = LocalIndication.current, + onClick = onClick, + ) + .padding( + vertical = TangemTheme.dimens.spacing16, + horizontal = TangemTheme.dimens.spacing18, + ) + .fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16), + verticalAlignment = Alignment.CenterVertically, + ) { + RadioButton( + modifier = Modifier.size(TangemTheme.dimens.size24), + selected = index == selectedItemIndex, + onClick = onClick, + colors = RadioButtonDefaults.colors( + selectedColor = TangemTheme.colors.icon.accent, + unselectedColor = TangemTheme.colors.icon.secondary, + ), + interactionSource = interactionSource, + ) + Text( + text = itemText, + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.primary1, + ) + } + } + } +} + +@Immutable +private sealed class DialogType { + + data class Message(val message: String) : DialogType() data class TextInput( val value: TextFieldValue, val onValueChange: (TextFieldValue) -> Unit, val params: AdditionalTextInputDialogParams = AdditionalTextInputDialogParams(), - ) : DialogType + ) : DialogType() + + data class Selector( + val selectedItemIndex: Int, + val items: ImmutableList, + val onSelect: (index: Int) -> Unit, + ) : DialogType() } // endregion Defaults @@ -381,4 +497,65 @@ private fun TextInputDialogPreview_Dark() { TextInputDialogSample() } } + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun SelectorDialogPreview_Light( + @PreviewParameter(SelctorDialogParamsProvider::class) param: SelectorDialogParams, +) { + TangemTheme(isDark = false) { + SelectorDialog( + title = param.title, + items = param.items, + selectedItemIndex = param.selectedItemIndex, + confirmButton = DialogButton(title = "Cancel", onClick = {}), + onSelect = {}, + onDismissDialog = {}, + ) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun SelectorDialogPreview_Dark( + @PreviewParameter(SelctorDialogParamsProvider::class) param: SelectorDialogParams, +) { + TangemTheme(isDark = true) { + SelectorDialog( + title = param.title, + items = param.items, + selectedItemIndex = param.selectedItemIndex, + confirmButton = DialogButton(title = "Cancel", onClick = {}), + onSelect = {}, + onDismissDialog = {}, + ) + } +} + +private class SelctorDialogParamsProvider : CollectionPreviewParameterProvider( + collection = listOf( + SelectorDialogParams( + title = "Theme", + selectedItemIndex = 0, + persistentListOf("Light", "Dark", "Follow system"), + ), + SelectorDialogParams( + title = null, + selectedItemIndex = 2, + persistentListOf("Light", "Dark", "Follow system"), + ), + SelectorDialogParams( + title = "Count", + selectedItemIndex = 8, + List(size = 10) { it.toString() }.toImmutableList(), + ), + ), +) { + + data class SelectorDialogParams( + val title: String?, + val selectedItemIndex: Int, + val items: ImmutableList, + ) +} // endregion Preview \ No newline at end of file diff --git a/domain/app-theme/models/src/main/kotlin/com/tangem/domain/apptheme/model/AppThemeMode.kt b/domain/app-theme/models/src/main/kotlin/com/tangem/domain/apptheme/model/AppThemeMode.kt index f6ff6269f7..db744ebb4c 100644 --- a/domain/app-theme/models/src/main/kotlin/com/tangem/domain/apptheme/model/AppThemeMode.kt +++ b/domain/app-theme/models/src/main/kotlin/com/tangem/domain/apptheme/model/AppThemeMode.kt @@ -26,5 +26,10 @@ enum class AppThemeMode { * The default [AppThemeMode]. */ val DEFAULT: AppThemeMode = FORCE_LIGHT + + /** + * List of available [AppThemeMode]s. + * */ + val available: List = values().toList() } } \ No newline at end of file