Updated on 2026-08-14
This commit is contained in:
parent
f289bbab97
commit
93b0b05e69
25 changed files with 494 additions and 132 deletions
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<AppSettingsScreenState>(
|
||||
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)
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ internal sealed class AppSettingsScreenState {
|
|||
|
||||
data class Content(
|
||||
val items: ImmutableList<Item>,
|
||||
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<TextReference>,
|
||||
val onSelect: (Int) -> Unit,
|
||||
override val onDismiss: () -> Unit,
|
||||
) : Dialog()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<AppState>) {
|
||||
|
||||
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<AppState>) {
|
|||
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<AppState>) {
|
|||
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<AppState>) {
|
|||
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<AppState>) {
|
|||
} 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<AppState>) {
|
|||
}
|
||||
|
||||
private fun dismissDialog() {
|
||||
updateContentState { copy(alert = null) }
|
||||
updateContentState { copy(dialog = null) }
|
||||
}
|
||||
|
||||
private fun updateContentState(block: AppSettingsScreenState.Content.() -> AppSettingsScreenState.Content) {
|
||||
|
|
|
|||
|
|
@ -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<Alert>(
|
||||
private class AlertDialogProvider : CollectionPreviewParameterProvider<Dialog.Alert>(
|
||||
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
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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<Dialog.Selector>(
|
||||
collection = listOf(
|
||||
AppSettingsDialogsFactory().createThemeModeSelectorDialog(
|
||||
selectedModeIndex = 0,
|
||||
onSelect = {},
|
||||
onDismiss = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -28,11 +28,16 @@
|
|||
<string name="app_settings_saved_access_codes_footer">Подключите функцию хранения кодов доступа от карт на телефоне в зашифрованном виде, и при работе с картой вместо кода доступа будет запрашиваться биометрическая аутентификация.</string>
|
||||
<string name="app_settings_saved_wallet">Cохранение кошелька</string>
|
||||
<string name="app_settings_saved_wallet_footer">Подключите функцию привязки карты в приложении, а также возможность биометрической аутентификации. Подпись транзакции все так же потребует карту.</string>
|
||||
<string name="app_settings_theme_mode_dark">Тёмная</string>
|
||||
<string name="app_settings_theme_mode_light">Светлая</string>
|
||||
<string name="app_settings_theme_mode_system">Как в системе</string>
|
||||
<string name="app_settings_theme_selector_title">Тема</string>
|
||||
<string name="app_settings_title">Настройки приложения</string>
|
||||
<string name="biometric_lockout_permanent_warning_description">Пожалуйста, отсканируйте карту</string>
|
||||
<string name="biometric_lockout_warning_description">Пожалуйста, попробуйте снова через 30 секунд или отсканируйте карту</string>
|
||||
<string name="biometric_lockout_warning_title">Слишком много попыток</string>
|
||||
<string name="biometric_unavailable_warning">Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона.</string>
|
||||
<string name="button_start_backup_process">Начать резервное копирование</string>
|
||||
<plurals name="card_label_card_count">
|
||||
<item quantity="one">%d карта</item>
|
||||
<item quantity="few">%d карты</item>
|
||||
|
|
@ -43,6 +48,7 @@
|
|||
<string name="card_settings_access_code_recovery_enabled_description">Использовать эту карту для сброса кода доступа на других картах в этом кошельке</string>
|
||||
<string name="card_settings_access_code_recovery_footer">Отключить возможность сброса кода доступа на этой карте или других картах этого кошелька</string>
|
||||
<string name="card_settings_access_code_recovery_title">Восстановление кода доступа</string>
|
||||
<string name="card_settings_action_sheet_reset">Сбросить</string>
|
||||
<string name="card_settings_action_sheet_title">Вы уверены, что хотите это сделать?</string>
|
||||
<string name="card_settings_change_access_code">Смена кода доступа</string>
|
||||
<string name="card_settings_change_access_code_footer">Код доступа будет изменен только на данной карте</string>
|
||||
|
|
@ -96,7 +102,6 @@
|
|||
<string name="common_reject">Отклонить</string>
|
||||
<string name="common_reload">Перезагрузить</string>
|
||||
<string name="common_rename">Переименовать</string>
|
||||
<string name="common_reset">Сбросить</string>
|
||||
<string name="common_save_changes">Сохранить изменения</string>
|
||||
<string name="common_search">Искать</string>
|
||||
<string name="common_search_tokens">Поиск токенов</string>
|
||||
|
|
@ -196,7 +201,7 @@
|
|||
</plurals>
|
||||
<string name="main_manage_tokens">Управление токенами</string>
|
||||
<string name="main_no_backup_warning_subtitle">Чтобы защитить свои активы, мы советуем вам выполнить эту процедуру</string>
|
||||
<string name="main_no_backup_warning_title">Бэкап кошелька не был произведен</string>
|
||||
<string name="main_no_backup_warning_title">Резервное копирование не выполнено</string>
|
||||
<string name="main_page_balance">Баланс</string>
|
||||
<string name="main_processing_full_amount">В сумме учтены не все монеты</string>
|
||||
<string name="main_promotion_credited">1INCH токены будут зачислены на адрес вашего кошелька в сети %s в течение 48 часов</string>
|
||||
|
|
@ -210,6 +215,7 @@
|
|||
<item quantity="other">Вам надо сгенерировать адреса для %d новых сетей, используя вашу карту</item>
|
||||
</plurals>
|
||||
<string name="main_warning_missing_derivation_title">Некоторые адреса отсутствуют</string>
|
||||
<string name="notification_title_not_enough_funds">Невозможно покрыть %1$s комиссию</string>
|
||||
<string name="onboarding_access_code_feature_1_description">Вам необходимо установить единый код доступа для защиты всех ваших карт</string>
|
||||
<string name="onboarding_access_code_feature_1_title">Защита</string>
|
||||
<string name="onboarding_access_code_feature_2_description">Позже вы сможете установить индивидуальный код доступа для каждой карты</string>
|
||||
|
|
@ -407,7 +413,7 @@
|
|||
<string name="story_meet_pay">Расплачивайтесь</string>
|
||||
<string name="story_meet_send">Отправляйте</string>
|
||||
<string name="story_meet_store">Храните</string>
|
||||
<string name="story_meet_title">Встречайте\nTangem</string>
|
||||
<string name="story_meet_title">Встречайте Tangem</string>
|
||||
<string name="story_web3_description">Обменивайте, покупайте NFT, получайте займы и делайте вклады в более чем 100 различных децентрализованных сервисах</string>
|
||||
<string name="story_web3_title">Поддержка DeFi</string>
|
||||
<string name="swapping_approve_information_text">Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен.</string>
|
||||
|
|
|
|||
|
|
@ -28,11 +28,16 @@
|
|||
<string name="app_settings_saved_access_codes_footer">Biometric authentication will be requested instead of the access code for interactions with your card.</string>
|
||||
<string name="app_settings_saved_wallet">Keep the wallet in the app</string>
|
||||
<string name="app_settings_saved_wallet_footer">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.</string>
|
||||
<string name="app_settings_theme_mode_dark">Dark</string>
|
||||
<string name="app_settings_theme_mode_light">Light</string>
|
||||
<string name="app_settings_theme_mode_system">System default</string>
|
||||
<string name="app_settings_theme_selector_title">Theme</string>
|
||||
<string name="app_settings_title">App Settings</string>
|
||||
<string name="biometric_lockout_permanent_warning_description">Please scan the card</string>
|
||||
<string name="biometric_lockout_warning_description">Please try again in 30 seconds or scan the card</string>
|
||||
<string name="biometric_lockout_warning_title">Too many attempts</string>
|
||||
<string name="biometric_unavailable_warning">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.</string>
|
||||
<string name="button_start_backup_process">Start backup process</string>
|
||||
<plurals name="card_label_card_count">
|
||||
<item quantity="one">%d card</item>
|
||||
<item quantity="other">%d cards</item>
|
||||
|
|
@ -41,6 +46,7 @@
|
|||
<string name="card_settings_access_code_recovery_enabled_description">Allows you to use this card to reset access code on other cards in this wallet</string>
|
||||
<string name="card_settings_access_code_recovery_footer">Disable the ability to reset the access code on this card or other cards in this wallet</string>
|
||||
<string name="card_settings_access_code_recovery_title">Access code recovery</string>
|
||||
<string name="card_settings_action_sheet_reset">Reset</string>
|
||||
<string name="card_settings_action_sheet_title">Are you sure you want to do this?</string>
|
||||
<string name="card_settings_change_access_code">Change Access Code</string>
|
||||
<string name="card_settings_change_access_code_footer">Access code will be changed on this card only</string>
|
||||
|
|
@ -95,7 +101,6 @@
|
|||
<string name="common_reject">Reject</string>
|
||||
<string name="common_reload">Reload</string>
|
||||
<string name="common_rename">Rename</string>
|
||||
<string name="common_reset">Reset</string>
|
||||
<string name="common_save_changes">Save changes</string>
|
||||
<string name="common_search">Search</string>
|
||||
<string name="common_search_tokens">Search tokens</string>
|
||||
|
|
@ -194,7 +199,7 @@
|
|||
</plurals>
|
||||
<string name="main_manage_tokens">Manage tokens</string>
|
||||
<string name="main_no_backup_warning_subtitle">To protect your assets, we advise you to carry out this procedure</string>
|
||||
<string name="main_no_backup_warning_title">Your wallet has not been backed up</string>
|
||||
<string name="main_no_backup_warning_title">Your wallet hasn\'t been backed up</string>
|
||||
<string name="main_page_balance">Total balance</string>
|
||||
<string name="main_processing_full_amount">The amount does not include some of your funds</string>
|
||||
<string name="main_promotion_credited">1INCH tokens will be credited to your %s wallet address within 48 hours</string>
|
||||
|
|
@ -206,6 +211,7 @@
|
|||
<item quantity="other">You need to generate addresses for %d new networks using your card</item>
|
||||
</plurals>
|
||||
<string name="main_warning_missing_derivation_title">Some addresses are missing</string>
|
||||
<string name="notification_title_not_enough_funds">Unable to cover %1$s fee</string>
|
||||
<string name="onboarding_access_code_feature_1_description">You have to set up a single access code to protect all your wallets</string>
|
||||
<string name="onboarding_access_code_feature_1_title">Protect</string>
|
||||
<string name="onboarding_access_code_feature_2_description">You can set up an individual access code on each card later</string>
|
||||
|
|
@ -233,7 +239,7 @@
|
|||
<string name="onboarding_button_skip_backup">Skip for later</string>
|
||||
<string name="onboarding_button_what_does_it_mean">How does it work?</string>
|
||||
<string name="onboarding_create_wallet_body">Let\'s generate all the keys on your card and create a secure wallet</string>
|
||||
<string name="onboarding_create_wallet_button_create_wallet">Create a wallet</string>
|
||||
<string name="onboarding_create_wallet_button_create_wallet">Create wallet</string>
|
||||
<string name="onboarding_create_wallet_header">Create a wallet</string>
|
||||
<string name="onboarding_create_wallet_options_button_options">Other options</string>
|
||||
<string name="onboarding_create_wallet_options_message">Your keys will be securely generated inside the card. There is no seed phrase, which means nobody can export or steal it.</string>
|
||||
|
|
@ -400,7 +406,7 @@
|
|||
<string name="story_meet_pay">Pay</string>
|
||||
<string name="story_meet_send">Send</string>
|
||||
<string name="story_meet_store">Store</string>
|
||||
<string name="story_meet_title">Meet\nTangem</string>
|
||||
<string name="story_meet_title">Meet Tangem</string>
|
||||
<string name="story_web3_description">Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services</string>
|
||||
<string name="story_web3_title">DeFi Compatible</string>
|
||||
<string name="swapping_approve_information_text">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.</string>
|
||||
|
|
|
|||
|
|
@ -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<String>,
|
||||
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<String>,
|
||||
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<String>,
|
||||
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<SelectorDialogParams>(
|
||||
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<String>,
|
||||
)
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -26,5 +26,10 @@ enum class AppThemeMode {
|
|||
* The default [AppThemeMode].
|
||||
*/
|
||||
val DEFAULT: AppThemeMode = FORCE_LIGHT
|
||||
|
||||
/**
|
||||
* List of available [AppThemeMode]s.
|
||||
* */
|
||||
val available: List<AppThemeMode> = values().toList()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue