Updated on 2026-08-14
This commit is contained in:
parent
5dd09c485a
commit
5a169e6211
10 changed files with 513 additions and 318 deletions
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings
|
||||
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Alert
|
||||
import com.tangem.wallet.R
|
||||
|
||||
internal class AppSettingsAlertsFactory {
|
||||
|
||||
fun createDeleteSavedWalletsAlert(onDelete: () -> Unit, onDismiss: () -> Unit): Alert {
|
||||
return Alert(
|
||||
title = resourceReference(R.string.common_attention),
|
||||
description = resourceReference(R.string.app_settings_off_saved_wallet_alert_message),
|
||||
confirmText = resourceReference(R.string.common_delete),
|
||||
onConfirm = onDelete,
|
||||
onDismiss = onDismiss,
|
||||
)
|
||||
}
|
||||
|
||||
fun createDeleteSavedAccessCodesAlert(onDelete: () -> Unit, onDismiss: () -> Unit): Alert {
|
||||
return Alert(
|
||||
title = resourceReference(R.string.common_attention),
|
||||
description = resourceReference(R.string.app_settings_off_saved_access_code_alert_message),
|
||||
confirmText = resourceReference(R.string.common_delete),
|
||||
onConfirm = onDelete,
|
||||
onDismiss = onDismiss,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,6 @@ package com.tangem.tap.features.details.ui.appsettings
|
|||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
|
|
@ -24,14 +22,12 @@ internal class AppSettingsFragment : ComposeFragment(), StoreSubscriber<DetailsS
|
|||
override lateinit var appThemeModeHolder: AppThemeModeHolder
|
||||
|
||||
private val viewModel = AppSettingsViewModel(store)
|
||||
private var screenState: MutableState<AppSettingsScreenState> =
|
||||
mutableStateOf(viewModel.updateState(store.state.detailsState))
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
AppSettingsScreen(
|
||||
modifier = modifier,
|
||||
state = screenState.value,
|
||||
state = viewModel.uiState,
|
||||
onBackClick = {
|
||||
store.dispatch(DetailsAction.ResetCardSettingsData)
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
|
|
@ -73,6 +69,6 @@ internal class AppSettingsFragment : ComposeFragment(), StoreSubscriber<DetailsS
|
|||
|
||||
override fun newState(state: DetailsState) {
|
||||
if (activity == null || view == null) return
|
||||
screenState.value = viewModel.updateState(state)
|
||||
viewModel.updateState(state)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings
|
||||
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item
|
||||
import com.tangem.wallet.R
|
||||
|
||||
internal class AppSettingsItemsFactory {
|
||||
|
||||
fun createEnrollBiometricsCard(onClick: () -> Unit): Item.Card {
|
||||
return Item.Card(
|
||||
id = "enroll_biometrics_card",
|
||||
title = resourceReference(R.string.app_settings_enable_biometrics_title),
|
||||
description = resourceReference(R.string.app_settings_enable_biometrics_description),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
fun createSaveWalletsSwitch(
|
||||
isChecked: Boolean,
|
||||
isEnabled: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
): Item.Switch {
|
||||
return Item.Switch(
|
||||
id = "save_wallets_switch",
|
||||
title = resourceReference(R.string.app_settings_saved_wallet),
|
||||
description = resourceReference(R.string.app_settings_saved_wallet_footer),
|
||||
isEnabled = isEnabled,
|
||||
isChecked = isChecked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
)
|
||||
}
|
||||
|
||||
fun createSaveAccessCodeSwitch(
|
||||
isChecked: Boolean,
|
||||
isEnabled: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
): Item.Switch {
|
||||
return Item.Switch(
|
||||
id = "save_access_codes_switch",
|
||||
title = resourceReference(R.string.app_settings_saved_access_codes),
|
||||
description = resourceReference(R.string.app_settings_saved_access_codes_footer),
|
||||
isEnabled = isEnabled,
|
||||
isChecked = isChecked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,224 +1,113 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.SpacerH24
|
||||
import com.tangem.core.ui.components.SpacerH32
|
||||
import com.tangem.core.ui.components.SpacerH4
|
||||
import com.tangem.core.ui.components.SpacerW32
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.details.redux.AppSetting
|
||||
import com.tangem.tap.features.details.ui.appsettings.components.EnrollBiometricsCard
|
||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item
|
||||
import com.tangem.tap.features.details.ui.appsettings.components.CardItem
|
||||
import com.tangem.tap.features.details.ui.appsettings.components.SettingsAlertDialog
|
||||
import com.tangem.tap.features.details.ui.appsettings.components.SwitchItem
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
import com.tangem.tap.features.details.ui.common.TangemSwitch
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
internal fun AppSettingsScreen(state: AppSettingsScreenState, onBackClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
SettingsScreensScaffold(
|
||||
modifier = modifier,
|
||||
content = { AppSettings(state = state) },
|
||||
content = {
|
||||
when (state) {
|
||||
is AppSettingsScreenState.Content -> AppSettings(state = state)
|
||||
is AppSettingsScreenState.Loading -> Unit
|
||||
}
|
||||
},
|
||||
titleRes = R.string.app_settings_title,
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppSettings(state: AppSettingsScreenState) {
|
||||
var dialogType by remember { mutableStateOf<AppSetting?>(null) }
|
||||
val onDialogStateChange: (AppSetting?) -> Unit = { dialogType = it }
|
||||
|
||||
dialogType?.let {
|
||||
SettingsAlertDialog(
|
||||
element = it,
|
||||
onDialogStateChange = onDialogStateChange,
|
||||
onSettingToggle = { state.onSettingToggled(it, false) },
|
||||
)
|
||||
private fun AppSettings(state: AppSettingsScreenState.Content) {
|
||||
val alert by rememberUpdatedState(newValue = state.alert)
|
||||
alert?.let { safeAlert ->
|
||||
SettingsAlertDialog(alert = safeAlert)
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
if (state.showEnrollBiometricsCard) {
|
||||
EnrollBiometricsCard(onClick = state.onEnrollBiometrics)
|
||||
SpacerH24()
|
||||
}
|
||||
|
||||
AppSettingsElement(
|
||||
state = state,
|
||||
setting = AppSetting.SaveWallets,
|
||||
onDialogStateChange = onDialogStateChange,
|
||||
)
|
||||
SpacerH32()
|
||||
AppSettingsElement(
|
||||
state = state,
|
||||
setting = AppSetting.SaveAccessCode,
|
||||
onDialogStateChange = onDialogStateChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun AppSettingsElement(
|
||||
state: AppSettingsScreenState,
|
||||
setting: AppSetting,
|
||||
onDialogStateChange: (AppSetting?) -> Unit,
|
||||
) {
|
||||
val titleRes = when (setting) {
|
||||
AppSetting.SaveWallets -> R.string.app_settings_saved_wallet
|
||||
AppSetting.SaveAccessCode -> R.string.app_settings_saved_access_codes
|
||||
}
|
||||
val subtitleRes = when (setting) {
|
||||
AppSetting.SaveWallets -> R.string.app_settings_saved_wallet_footer
|
||||
AppSetting.SaveAccessCode -> R.string.app_settings_saved_access_codes_footer
|
||||
}
|
||||
val checked = state.settings[setting] ?: false
|
||||
|
||||
val titleTextColor by rememberUpdatedState(
|
||||
newValue = if (state.isTogglesEnabled) {
|
||||
TangemTheme.colors.text.primary1
|
||||
} else {
|
||||
TangemTheme.colors.text.secondary
|
||||
},
|
||||
)
|
||||
val descriptionTextColor by rememberUpdatedState(
|
||||
newValue = if (state.isTogglesEnabled) {
|
||||
TangemTheme.colors.text.secondary
|
||||
} else {
|
||||
TangemTheme.colors.text.tertiary
|
||||
},
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing20),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(weight = .9f),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = titleRes),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = titleTextColor,
|
||||
)
|
||||
SpacerH4()
|
||||
Text(
|
||||
text = stringResource(id = subtitleRes),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = descriptionTextColor,
|
||||
)
|
||||
}
|
||||
SpacerW32()
|
||||
TangemSwitch(
|
||||
checked = checked,
|
||||
enabled = state.isTogglesEnabled,
|
||||
onCheckedChange = { isChecked ->
|
||||
onCheckedChange(
|
||||
element = setting,
|
||||
enabled = isChecked,
|
||||
onSettingToggled = state.onSettingToggled,
|
||||
onDialogStateChange = onDialogStateChange,
|
||||
LazyColumn {
|
||||
items(
|
||||
items = state.items,
|
||||
key = Item::id,
|
||||
) { item ->
|
||||
when (item) {
|
||||
is Item.Card -> CardItem(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
item = item,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCheckedChange(
|
||||
element: AppSetting,
|
||||
enabled: Boolean,
|
||||
onSettingToggled: (AppSetting, Boolean) -> Unit,
|
||||
onDialogStateChange: (AppSetting?) -> Unit,
|
||||
) {
|
||||
// Show warning if user wants to disable the switch
|
||||
if (!enabled) {
|
||||
onDialogStateChange(element)
|
||||
} else {
|
||||
onSettingToggled(element, true)
|
||||
is Item.Switch -> SwitchItem(
|
||||
modifier = Modifier.padding(
|
||||
vertical = TangemTheme.dimens.spacing16,
|
||||
horizontal = TangemTheme.dimens.spacing20,
|
||||
),
|
||||
item = item,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun AppSettingsScreenSample(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
AppSettingsScreen(
|
||||
state = AppSettingsScreenState(
|
||||
settings = mapOf(
|
||||
AppSetting.SaveWallets to true,
|
||||
AppSetting.SaveAccessCode to false,
|
||||
),
|
||||
showEnrollBiometricsCard = false,
|
||||
isTogglesEnabled = true,
|
||||
onSettingToggled = { _, _ -> },
|
||||
onEnrollBiometrics = {},
|
||||
),
|
||||
onBackClick = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun AppSettingsScreenPreview_Light() {
|
||||
private fun AppSettingsScreenPreview_Light(
|
||||
@PreviewParameter(AppSettingsScreenStateProvider::class) state: AppSettingsScreenState,
|
||||
) {
|
||||
TangemTheme {
|
||||
AppSettingsScreenSample()
|
||||
AppSettingsScreen(state = state, onBackClick = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun AppSettingsScreenPreview_Dark() {
|
||||
private fun AppSettingsScreenPreview_Dark(
|
||||
@PreviewParameter(AppSettingsScreenStateProvider::class) state: AppSettingsScreenState,
|
||||
) {
|
||||
TangemTheme(isDark = true) {
|
||||
AppSettingsScreenSample()
|
||||
AppSettingsScreen(state = state, onBackClick = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppSettingsScreen_EnrollBiometrics_Sample(modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier.background(TangemTheme.colors.background.primary)) {
|
||||
AppSettingsScreen(
|
||||
state = AppSettingsScreenState(
|
||||
settings = mapOf(
|
||||
AppSetting.SaveWallets to true,
|
||||
AppSetting.SaveAccessCode to false,
|
||||
),
|
||||
showEnrollBiometricsCard = true,
|
||||
isTogglesEnabled = false,
|
||||
onSettingToggled = { _, _ -> },
|
||||
onEnrollBiometrics = {},
|
||||
),
|
||||
onBackClick = { },
|
||||
private class AppSettingsScreenStateProvider : CollectionPreviewParameterProvider<AppSettingsScreenState>(
|
||||
collection = buildList {
|
||||
val itemsFactory = AppSettingsItemsFactory()
|
||||
val dialogsFactory = AppSettingsAlertsFactory()
|
||||
val items = persistentListOf(
|
||||
itemsFactory.createEnrollBiometricsCard {},
|
||||
itemsFactory.createSaveWalletsSwitch(isChecked = true, isEnabled = true, { _ -> }),
|
||||
itemsFactory.createSaveAccessCodeSwitch(isChecked = false, isEnabled = true) { _ -> },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun AppSettingsScreen_EnrollBiometrics_Preview_Light() {
|
||||
TangemTheme {
|
||||
AppSettingsScreen_EnrollBiometrics_Sample()
|
||||
}
|
||||
}
|
||||
AppSettingsScreenState.Content(
|
||||
items = items,
|
||||
alert = null,
|
||||
).let(::add)
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun AppSettingsScreen_EnrollBiometrics_Preview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
AppSettingsScreen_EnrollBiometrics_Sample()
|
||||
}
|
||||
}
|
||||
AppSettingsScreenState.Content(
|
||||
items = items,
|
||||
alert = dialogsFactory.createDeleteSavedWalletsAlert({}, {}),
|
||||
).let(::add)
|
||||
|
||||
AppSettingsScreenState.Content(
|
||||
items = items,
|
||||
alert = dialogsFactory.createDeleteSavedAccessCodesAlert({}, {}),
|
||||
).let(::add)
|
||||
},
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -1,11 +1,48 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings
|
||||
|
||||
import com.tangem.tap.features.details.redux.AppSetting
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class AppSettingsScreenState(
|
||||
val settings: Map<AppSetting, Boolean> = emptyMap(),
|
||||
val showEnrollBiometricsCard: Boolean = false,
|
||||
val isTogglesEnabled: Boolean = false,
|
||||
val onSettingToggled: (AppSetting, Boolean) -> Unit = { _, _ -> /* no-op */ },
|
||||
val onEnrollBiometrics: () -> Unit = { /* no-op */ },
|
||||
)
|
||||
@Immutable
|
||||
internal sealed class AppSettingsScreenState {
|
||||
|
||||
object Loading : AppSettingsScreenState()
|
||||
|
||||
data class Content(
|
||||
val items: ImmutableList<Item>,
|
||||
val alert: Alert?,
|
||||
) : AppSettingsScreenState()
|
||||
|
||||
@Immutable
|
||||
sealed class Item {
|
||||
|
||||
abstract val id: String
|
||||
|
||||
data class Card(
|
||||
override val id: String,
|
||||
@DrawableRes val iconResId: Int,
|
||||
val title: TextReference,
|
||||
val description: TextReference,
|
||||
val onClick: () -> Unit,
|
||||
) : Item()
|
||||
|
||||
data class Switch(
|
||||
override val id: String,
|
||||
val title: TextReference,
|
||||
val description: TextReference,
|
||||
val isEnabled: Boolean,
|
||||
val isChecked: Boolean,
|
||||
val onCheckedChange: (Boolean) -> Unit,
|
||||
) : Item()
|
||||
}
|
||||
|
||||
data class Alert(
|
||||
val title: TextReference,
|
||||
val description: TextReference,
|
||||
val confirmText: TextReference,
|
||||
val onConfirm: () -> Unit,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,35 +1,31 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.details.redux.AppSetting
|
||||
import com.tangem.tap.features.details.redux.AppSettingsState
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import org.rekotlin.Store
|
||||
|
||||
internal class AppSettingsViewModel(private val store: Store<AppState>) {
|
||||
|
||||
fun updateState(state: DetailsState): AppSettingsScreenState {
|
||||
return with(state.appSettingsState) {
|
||||
AppSettingsScreenState(
|
||||
settings = mapOf(
|
||||
AppSetting.SaveWallets to saveWallets,
|
||||
AppSetting.SaveAccessCode to saveAccessCodes,
|
||||
),
|
||||
showEnrollBiometricsCard = needEnrollBiometrics,
|
||||
isTogglesEnabled = !needEnrollBiometrics && !isInProgress,
|
||||
onSettingToggled = { privacySetting, enabled ->
|
||||
onSettingsToggled(privacySetting, enabled)
|
||||
},
|
||||
onEnrollBiometrics = {
|
||||
store.dispatchOnMain(DetailsAction.AppSettings.EnrollBiometrics)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
private val itemsFactory = AppSettingsItemsFactory()
|
||||
private val alertsFactory = AppSettingsAlertsFactory()
|
||||
|
||||
private fun onSettingsToggled(setting: AppSetting, enable: Boolean) {
|
||||
store.dispatch(DetailsAction.AppSettings.SwitchPrivacySetting(enable = enable, setting = setting))
|
||||
var uiState: AppSettingsScreenState by mutableStateOf(AppSettingsScreenState.Loading)
|
||||
private set
|
||||
|
||||
fun updateState(state: DetailsState) {
|
||||
uiState = AppSettingsScreenState.Content(
|
||||
items = buildItems(state.appSettingsState),
|
||||
alert = (uiState as? AppSettingsScreenState.Content)?.alert,
|
||||
)
|
||||
}
|
||||
|
||||
fun checkBiometricsStatus() {
|
||||
|
|
@ -39,4 +35,85 @@ internal class AppSettingsViewModel(private val store: Store<AppState>) {
|
|||
fun refreshBiometricsStatus() {
|
||||
store.dispatch(DetailsAction.AppSettings.CheckBiometricsStatus(awaitStatusChange = true))
|
||||
}
|
||||
|
||||
private fun buildItems(state: AppSettingsState): ImmutableList<AppSettingsScreenState.Item> {
|
||||
val items = buildList {
|
||||
if (state.needEnrollBiometrics) {
|
||||
itemsFactory.createEnrollBiometricsCard(onClick = ::enrollBiometrics).let(::add)
|
||||
}
|
||||
|
||||
if (state.isBiometricsAvailable) {
|
||||
val canUseBiometrics = !state.needEnrollBiometrics && !state.isInProgress
|
||||
|
||||
itemsFactory.createSaveWalletsSwitch(
|
||||
isChecked = state.saveWallets,
|
||||
isEnabled = canUseBiometrics,
|
||||
onCheckedChange = ::onSaveWalletsToggled,
|
||||
).let(::add)
|
||||
|
||||
itemsFactory.createSaveAccessCodeSwitch(
|
||||
isChecked = state.saveAccessCodes,
|
||||
isEnabled = canUseBiometrics,
|
||||
onCheckedChange = ::onSaveAccessCodesToggled,
|
||||
).let(::add)
|
||||
}
|
||||
}
|
||||
|
||||
return items.toImmutableList()
|
||||
}
|
||||
|
||||
private fun enrollBiometrics() {
|
||||
store.dispatchOnMain(DetailsAction.AppSettings.EnrollBiometrics)
|
||||
}
|
||||
|
||||
private fun onSaveWalletsToggled(isChecked: Boolean) {
|
||||
if (isChecked) {
|
||||
onSettingsToggled(AppSetting.SaveWallets, enable = true)
|
||||
} else {
|
||||
updateContentState {
|
||||
copy(
|
||||
alert = alertsFactory.createDeleteSavedWalletsAlert(
|
||||
onDelete = {
|
||||
onSettingsToggled(AppSetting.SaveWallets, enable = false)
|
||||
dismissDialog()
|
||||
},
|
||||
onDismiss = ::dismissDialog,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSaveAccessCodesToggled(isChecked: Boolean) {
|
||||
if (isChecked) {
|
||||
onSettingsToggled(AppSetting.SaveAccessCode, enable = true)
|
||||
} else {
|
||||
updateContentState {
|
||||
copy(
|
||||
alert = alertsFactory.createDeleteSavedAccessCodesAlert(
|
||||
onDelete = {
|
||||
onSettingsToggled(AppSetting.SaveAccessCode, enable = false)
|
||||
dismissDialog()
|
||||
},
|
||||
onDismiss = ::dismissDialog,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSettingsToggled(setting: AppSetting, enable: Boolean) {
|
||||
store.dispatch(DetailsAction.AppSettings.SwitchPrivacySetting(enable = enable, setting = setting))
|
||||
}
|
||||
|
||||
private fun dismissDialog() {
|
||||
updateContentState { copy(alert = null) }
|
||||
}
|
||||
|
||||
private fun updateContentState(block: AppSettingsScreenState.Content.() -> AppSettingsScreenState.Content) {
|
||||
uiState = when (val state = uiState) {
|
||||
is AppSettingsScreenState.Content -> block(state)
|
||||
is AppSettingsScreenState.Loading -> state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,6 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Surface
|
||||
|
|
@ -14,24 +9,25 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.SpacerH4
|
||||
import com.tangem.core.ui.components.SpacerW16
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsItemsFactory
|
||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
internal fun EnrollBiometricsCard(onClick: () -> Unit) {
|
||||
internal fun CardItem(item: Item.Card, modifier: Modifier = Modifier) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing8)
|
||||
.fillMaxWidth(),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
color = TangemTheme.colors.button.disabled,
|
||||
shape = TangemTheme.shapes.roundedCornersLarge,
|
||||
onClick = onClick,
|
||||
onClick = item.onClick,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(all = 16.dp),
|
||||
|
|
@ -39,20 +35,20 @@ internal fun EnrollBiometricsCard(onClick: () -> Unit) {
|
|||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_alert_circle_24),
|
||||
painter = painterResource(id = item.iconResId),
|
||||
tint = TangemTheme.colors.icon.attention,
|
||||
contentDescription = null,
|
||||
)
|
||||
SpacerW16()
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(id = R.string.app_settings_enable_biometrics_title),
|
||||
text = item.title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerH4()
|
||||
Text(
|
||||
text = stringResource(id = R.string.app_settings_enable_biometrics_description),
|
||||
text = item.description.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
|
|
@ -62,28 +58,29 @@ internal fun EnrollBiometricsCard(onClick: () -> Unit) {
|
|||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun EnrollBiometricsCardSample(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.background(TangemTheme.colors.background.secondary),
|
||||
) {
|
||||
EnrollBiometricsCard(onClick = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun EnrollBiometricsCardPreview_Light() {
|
||||
private fun CardItemPreview_Light(@PreviewParameter(CardItemProvider::class) item: Item.Card) {
|
||||
TangemTheme {
|
||||
EnrollBiometricsCardSample()
|
||||
CardItem(item = item)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun EnrollBiometricsCardPreview_Dark() {
|
||||
private fun CardItemPreview_Dark(@PreviewParameter(CardItemProvider::class) item: Item.Card) {
|
||||
TangemTheme(isDark = true) {
|
||||
EnrollBiometricsCardSample()
|
||||
CardItem(item = item)
|
||||
}
|
||||
}
|
||||
|
||||
private class CardItemProvider : CollectionPreviewParameterProvider<Item.Card>(
|
||||
collection = buildList {
|
||||
val itemsFactory = AppSettingsItemsFactory()
|
||||
|
||||
itemsFactory.createEnrollBiometricsCard(
|
||||
onClick = { /* no-op */ },
|
||||
).let(::add)
|
||||
},
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -1,97 +1,60 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.AlertDialog
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.TextButton
|
||||
import com.tangem.core.ui.components.WarningTextButton
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButton
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.details.redux.AppSetting
|
||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsAlertsFactory
|
||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Alert
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun SettingsAlertDialog(
|
||||
element: AppSetting,
|
||||
onDialogStateChange: (AppSetting?) -> Unit,
|
||||
onSettingToggle: () -> Unit,
|
||||
) {
|
||||
val text = when (element) {
|
||||
AppSetting.SaveWallets -> R.string.app_settings_off_saved_wallet_alert_message
|
||||
AppSetting.SaveAccessCode -> R.string.app_settings_off_saved_access_code_alert_message
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = { onDialogStateChange(null) },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
text = stringResource(id = R.string.common_cancel),
|
||||
onClick = {
|
||||
onDialogStateChange(null)
|
||||
},
|
||||
)
|
||||
},
|
||||
dismissButton = {
|
||||
WarningTextButton(
|
||||
text = stringResource(id = R.string.common_delete),
|
||||
onClick = {
|
||||
onDialogStateChange(null)
|
||||
onSettingToggle()
|
||||
},
|
||||
)
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.common_attention),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.h2,
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(id = text),
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
},
|
||||
shape = TangemTheme.shapes.roundedCornersLarge,
|
||||
internal fun SettingsAlertDialog(alert: Alert) {
|
||||
BasicDialog(
|
||||
title = alert.title.resolveReference(),
|
||||
message = alert.description.resolveReference(),
|
||||
isDismissable = false,
|
||||
confirmButton = DialogButton(
|
||||
title = alert.confirmText.resolveReference(),
|
||||
warning = true,
|
||||
onClick = alert.onConfirm,
|
||||
),
|
||||
dismissButton = DialogButton(
|
||||
title = stringResource(id = R.string.common_cancel),
|
||||
onClick = alert.onDismiss,
|
||||
),
|
||||
onDismissDialog = alert.onDismiss,
|
||||
)
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun SettingsAlertDialogSample(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
SettingsAlertDialog(
|
||||
element = AppSetting.SaveAccessCode,
|
||||
onDialogStateChange = {},
|
||||
onSettingToggle = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun SettingsAlertDialogPreview_Light() {
|
||||
private fun AlertDialogPreview_Light(@PreviewParameter(AlertDialogProvider::class) dialog: Alert) {
|
||||
TangemTheme {
|
||||
SettingsAlertDialogSample()
|
||||
SettingsAlertDialog(alert = dialog)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun SettingsAlertDialogPreview_Dark() {
|
||||
private fun AlertDialogPreview_Dark(@PreviewParameter(AlertDialogProvider::class) dialog: Alert) {
|
||||
TangemTheme(isDark = true) {
|
||||
SettingsAlertDialogSample()
|
||||
SettingsAlertDialog(alert = dialog)
|
||||
}
|
||||
}
|
||||
|
||||
private class AlertDialogProvider : CollectionPreviewParameterProvider<Alert>(
|
||||
collection = buildList {
|
||||
val alertsFactory = AppSettingsAlertsFactory()
|
||||
|
||||
alertsFactory.createDeleteSavedAccessCodesAlert({}, {}).let(::add)
|
||||
alertsFactory.createDeleteSavedWalletsAlert({}, {}).let(::add)
|
||||
},
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.components.SpacerH4
|
||||
import com.tangem.core.ui.components.SpacerW32
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsItemsFactory
|
||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item
|
||||
import com.tangem.tap.features.details.ui.common.TangemSwitch
|
||||
|
||||
@Composable
|
||||
internal fun SwitchItem(item: Item.Switch, modifier: Modifier = Modifier) {
|
||||
val titleTextColor by rememberUpdatedState(
|
||||
newValue = if (item.isEnabled) {
|
||||
TangemTheme.colors.text.primary1
|
||||
} else {
|
||||
TangemTheme.colors.text.secondary
|
||||
},
|
||||
)
|
||||
val descriptionTextColor by rememberUpdatedState(
|
||||
newValue = if (item.isEnabled) {
|
||||
TangemTheme.colors.text.secondary
|
||||
} else {
|
||||
TangemTheme.colors.text.tertiary
|
||||
},
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(weight = .9f),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Text(
|
||||
text = item.title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = titleTextColor,
|
||||
)
|
||||
SpacerH4()
|
||||
Text(
|
||||
text = item.description.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = descriptionTextColor,
|
||||
)
|
||||
}
|
||||
SpacerW32()
|
||||
TangemSwitch(
|
||||
checked = item.isChecked,
|
||||
enabled = item.isEnabled,
|
||||
onCheckedChange = item.onCheckedChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun SwitchItemPreview_Light(@PreviewParameter(SwitchItemProvider::class) item: Item.Switch) {
|
||||
TangemTheme {
|
||||
SwitchItem(item = item)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun SwitchItemPreview_Dark(@PreviewParameter(SwitchItemProvider::class) item: Item.Switch) {
|
||||
TangemTheme(isDark = true) {
|
||||
SwitchItem(item = item)
|
||||
}
|
||||
}
|
||||
|
||||
private class SwitchItemProvider : CollectionPreviewParameterProvider<Item.Switch>(
|
||||
collection = buildList {
|
||||
val itemsFactory = AppSettingsItemsFactory()
|
||||
|
||||
itemsFactory.createSaveAccessCodeSwitch(
|
||||
isChecked = true,
|
||||
isEnabled = true,
|
||||
onCheckedChange = { /* no-op */ },
|
||||
).let(::add)
|
||||
itemsFactory.createSaveAccessCodeSwitch(
|
||||
isChecked = false,
|
||||
isEnabled = true,
|
||||
onCheckedChange = { /* no-op */ },
|
||||
).let(::add)
|
||||
itemsFactory.createSaveAccessCodeSwitch(
|
||||
isChecked = true,
|
||||
isEnabled = false,
|
||||
onCheckedChange = { /* no-op */ },
|
||||
).let(::add)
|
||||
itemsFactory.createSaveAccessCodeSwitch(
|
||||
isChecked = false,
|
||||
isEnabled = false,
|
||||
onCheckedChange = { /* no-op */ },
|
||||
).let(::add)
|
||||
},
|
||||
)
|
||||
// endregion Preview
|
||||
Loading…
Add table
Add a link
Reference in a new issue