Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-05 12:08:56 +03:00
parent 1694ec07be
commit 1061426e87
10 changed files with 176 additions and 47 deletions

View file

@ -39,8 +39,11 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
is DetailsAction.AppSettings -> {
handlePrivacyAction(action, detailsState)
}
is DetailsAction.ChangeAppCurrency ->
detailsState.copy(appCurrency = action.fiatCurrency)
is DetailsAction.ChangeAppCurrency -> detailsState.copy(
appSettingsState = detailsState.appSettingsState.copy(
selectedFiatCurrency = action.fiatCurrency,
),
)
is DetailsAction.AccessCodeRecovery -> handleAccessCodeRecoveryAction(action, detailsState)
else -> detailsState
}
@ -50,11 +53,11 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen): DetailsSta
return DetailsState(
scanResponse = action.scanResponse,
createBackupAllowed = action.scanResponse.card.backupStatus == CardDTO.BackupStatus.NoBackup,
appCurrency = store.state.globalState.appCurrency,
appSettingsState = AppSettingsState(
isBiometricsAvailable = tangemSdkManager.canUseBiometry,
saveWallets = preferencesStorage.shouldSaveUserWallets,
saveAccessCodes = preferencesStorage.shouldSaveAccessCodes,
selectedFiatCurrency = store.state.globalState.appCurrency,
),
)
}

View file

@ -12,7 +12,6 @@ data class DetailsState(
val cardSettingsState: CardSettingsState? = null,
val privacyPolicyUrl: String? = null,
val createBackupAllowed: Boolean = false,
val appCurrency: FiatCurrency = FiatCurrency.Default,
val appSettingsState: AppSettingsState = AppSettingsState(),
) : StateType
@ -55,6 +54,7 @@ data class AppSettingsState(
val isBiometricsAvailable: Boolean = false,
val needEnrollBiometrics: Boolean = false,
val isInProgress: Boolean = false,
val selectedFiatCurrency: FiatCurrency = FiatCurrency.Default,
)
enum class SecurityOption { LongTap, PassCode, AccessCode }

View file

@ -1,6 +1,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.tap.features.details.ui.appsettings.AppSettingsScreenState.Item
import com.tangem.wallet.R
@ -45,4 +46,14 @@ internal class AppSettingsItemsFactory {
onCheckedChange = onCheckedChange,
)
}
fun createSelectAppCurrencyButton(currentAppCurrencyName: String, onClick: () -> Unit): Item.Button {
return Item.Button(
id = "select_app_currency_button",
title = resourceReference(R.string.details_row_title_currency),
description = stringReference(currentAppCurrencyName),
isEnabled = true,
onClick = onClick,
)
}
}

View file

@ -12,6 +12,7 @@ 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.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
@ -51,6 +52,10 @@ private fun AppSettings(state: AppSettingsScreenState.Content) {
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
item = item,
)
is Item.Button -> ButtonItem(
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8),
item = item,
)
is Item.Switch -> SwitchItem(
modifier = Modifier.padding(
vertical = TangemTheme.dimens.spacing16,
@ -90,6 +95,7 @@ private class AppSettingsScreenStateProvider : CollectionPreviewParameterProvide
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) { _ -> },
)

View file

@ -36,6 +36,14 @@ internal sealed class AppSettingsScreenState {
val isChecked: Boolean,
val onCheckedChange: (Boolean) -> Unit,
) : Item()
data class Button(
override val id: String,
val title: TextReference,
val description: TextReference,
val isEnabled: Boolean,
val onClick: () -> Unit,
) : Item()
}
data class Alert(

View file

@ -9,6 +9,7 @@ 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 com.tangem.tap.features.wallet.redux.WalletAction
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import org.rekotlin.Store
@ -42,6 +43,11 @@ internal class AppSettingsViewModel(private val store: Store<AppState>) {
itemsFactory.createEnrollBiometricsCard(onClick = ::enrollBiometrics).let(::add)
}
itemsFactory.createSelectAppCurrencyButton(
currentAppCurrencyName = state.selectedFiatCurrency.name,
onClick = ::showAppCurrencySelector,
).let(::add)
if (state.isBiometricsAvailable) {
val canUseBiometrics = !state.needEnrollBiometrics && !state.isInProgress
@ -66,6 +72,10 @@ internal class AppSettingsViewModel(private val store: Store<AppState>) {
store.dispatchOnMain(DetailsAction.AppSettings.EnrollBiometrics)
}
private fun showAppCurrencySelector() {
store.dispatchOnMain(WalletAction.AppCurrencyAction.ChooseAppCurrency)
}
private fun onSaveWalletsToggled(isChecked: Boolean) {
if (isChecked) {
onSettingsToggled(AppSetting.SaveWallets, enable = true)

View file

@ -0,0 +1,80 @@
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.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
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.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
@OptIn(ExperimentalMaterialApi::class)
@Composable
internal fun ButtonItem(item: Item.Button, modifier: Modifier = Modifier) {
Surface(
modifier = modifier.fillMaxWidth(),
color = TangemTheme.colors.background.secondary,
onClick = item.onClick,
) {
Column(
modifier = Modifier.padding(
horizontal = TangemTheme.dimens.spacing20,
vertical = TangemTheme.dimens.spacing8,
),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
horizontalAlignment = Alignment.Start,
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = item.title.resolveReference(),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
Text(
modifier = Modifier.fillMaxWidth(),
text = item.description.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
)
}
}
}
// region Preview
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun ButtonItemPreview_Light(@PreviewParameter(ButtonItemProvider::class) item: Item.Button) {
TangemTheme {
ButtonItem(item = item)
}
}
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun ButtonItemPreview_Dark(@PreviewParameter(ButtonItemProvider::class) item: Item.Button) {
TangemTheme(isDark = true) {
ButtonItem(item = item)
}
}
private class ButtonItemProvider : CollectionPreviewParameterProvider<Item.Button>(
collection = buildList {
val itemsFactory = AppSettingsItemsFactory()
itemsFactory.createSelectAppCurrencyButton(
currentAppCurrencyName = "US Dollar",
onClick = { /* no-op */ },
).let(::add)
},
)
// endregion Preview

View file

@ -20,6 +20,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerHMax
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.features.details.ui.common.ScreenTitle
@ -44,39 +46,53 @@ private fun Content(state: DetailsScreenState, modifier: Modifier = Modifier) {
.fillMaxSize()
.verticalScroll(rememberScrollState()),
) {
ScreenTitle(titleRes = R.string.details_title, Modifier.padding(bottom = 52.dp))
state.elements.map { element ->
if (element == SettingsElement.WalletConnect) {
WalletConnectDetailsItem(onItemsClick = state.onItemsClick)
} else {
DetailsItem(
item = element,
appCurrency = state.appCurrency,
onItemsClick = { state.onItemsClick(element) },
)
}
}
Spacer(modifier = Modifier.weight(1f))
TangemSocialAccounts(state.tangemLinks, state.onSocialNetworkClick)
Spacer(modifier = Modifier.size(12.dp))
Text(
text = "${stringResource(id = state.appNameRes)} ${state.tangemVersion}",
style = TangemTheme.typography.caption,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 40.dp),
ScreenTitle(titleRes = R.string.details_title)
SpacerH(height = TangemTheme.dimens.spacing36)
SettingsItems(
items = state.elements,
onItemsClick = state.onItemsClick,
)
SpacerHMax()
TangemSocialAccounts(
links = state.tangemLinks,
onSocialNetworkClick = state.onSocialNetworkClick,
)
SpacerH(height = TangemTheme.dimens.spacing16)
TangemAppVersion(
appNameRes = state.appNameRes,
version = state.tangemVersion,
)
SpacerH(height = TangemTheme.dimens.spacing24)
}
ShowSnackbarIfNeeded(state.showErrorSnackbar.value)
}
}
@Composable
private fun WalletConnectDetailsItem(onItemsClick: (SettingsElement) -> Unit) {
private fun SettingsItems(items: List<SettingsElement>, onItemsClick: (SettingsElement) -> Unit) {
items.forEach { item ->
val onItemClick = remember(item) {
{ onItemsClick(item) }
}
if (item == SettingsElement.WalletConnect) {
WalletConnectDetailsItem(onItemClick)
} else {
DetailsItem(
item = item,
onItemClick = onItemClick,
)
}
}
}
@Composable
private fun WalletConnectDetailsItem(onItemClick: () -> Unit) {
Row(
modifier = Modifier
.defaultMinSize(minHeight = 84.dp)
.fillMaxWidth()
.clickable { onItemsClick(SettingsElement.WalletConnect) },
.clickable(onClick = onItemClick),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
) {
@ -108,12 +124,12 @@ private fun WalletConnectDetailsItem(onItemsClick: (SettingsElement) -> Unit) {
}
@Composable
private fun DetailsItem(item: SettingsElement, appCurrency: String, onItemsClick: () -> Unit) {
private fun DetailsItem(item: SettingsElement, onItemClick: () -> Unit) {
Row(
modifier = Modifier
.height(56.dp)
.fillMaxWidth()
.clickable(onClick = onItemsClick),
.clickable(onClick = onItemClick),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
) {
@ -130,13 +146,6 @@ private fun DetailsItem(item: SettingsElement, appCurrency: String, onItemsClick
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
if (item == SettingsElement.AppCurrency) {
Text(
text = appCurrency,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
)
}
}
}
}
@ -190,6 +199,16 @@ private fun BoxScope.ShowSnackbarIfNeeded(snackbarErrorState: EventError) {
}
}
@Composable
private fun TangemAppVersion(appNameRes: Int, version: String, modifier: Modifier = Modifier) {
Text(
modifier = modifier.padding(horizontal = TangemTheme.dimens.spacing16),
text = "${stringResource(id = appNameRes)} $version",
style = TangemTheme.typography.caption,
color = TangemTheme.colors.text.tertiary,
)
}
// region Preview
@Composable
private fun DetailsScreenContentSample() {
@ -198,7 +217,6 @@ private fun DetailsScreenContentSample() {
elements = SettingsElement.values().toList(),
tangemLinks = TangemSocialAccounts.accountsEn,
tangemVersion = "Tangem 2.14.12 (343)",
appCurrency = "Dollar",
onItemsClick = {},
onSocialNetworkClick = {},
),

View file

@ -10,7 +10,6 @@ internal data class DetailsScreenState(
val elements: List<SettingsElement>,
val tangemLinks: List<SocialNetworkLink>,
val tangemVersion: String,
val appCurrency: String,
val onItemsClick: (SettingsElement) -> Unit,
val onSocialNetworkClick: (SocialNetworkLink) -> Unit,
val showErrorSnackbar: MutableState<EventError> = mutableStateOf(EventError.Empty),
@ -24,14 +23,13 @@ internal enum class SettingsElement(
val titleRes: Int,
) {
WalletConnect(R.drawable.ic_walletconnect, R.string.wallet_connect_title),
Chat(R.drawable.ic_chat, R.string.details_chat),
SendFeedback(R.drawable.ic_comment, R.string.details_row_title_send_feedback),
LinkMoreCards(R.drawable.ic_more_cards, R.string.details_row_title_create_backup),
ReferralProgram(R.drawable.ic_add_friends, R.string.details_referral_title),
CardSettings(R.drawable.ic_card_settings, R.string.card_settings_title),
AppCurrency(R.drawable.ic_currency, R.string.details_row_title_currency),
AppSettings(R.drawable.ic_settings, R.string.app_settings_title),
LinkMoreCards(R.drawable.ic_more_cards, R.string.details_row_title_create_backup),
TermsOfService(R.drawable.ic_text, R.string.disclaimer_title), // General Terms of Service of the App
Chat(R.drawable.ic_chat, R.string.details_chat),
SendFeedback(R.drawable.ic_comment, R.string.details_row_title_send_feedback),
TermsOfService(R.drawable.ic_text, R.string.disclaimer_title), // General Terms of Service of the App,
PrivacyPolicy(R.drawable.ic_lock_24, R.string.details_row_privacy_policy),
TesterMenu(R.drawable.ic_alert_24, R.string.tester_menu),
}

View file

@ -42,7 +42,6 @@ internal class DetailsViewModel(private val store: Store<AppState>) {
elements = createSettingsItems(state),
tangemLinks = getSocialLinks(),
tangemVersion = getTangemAppVersion(),
appCurrency = state.appCurrency.name,
onItemsClick = { handleClickingSettingsItem(it) },
onSocialNetworkClick = { handleSocialNetworkClick(it) },
)
@ -60,7 +59,6 @@ internal class DetailsViewModel(private val store: Store<AppState>) {
SettingsElement.LinkMoreCards -> if (state.createBackupAllowed) it else null
SettingsElement.PrivacyPolicy -> if (state.privacyPolicyUrl != null) it else null
SettingsElement.AppSettings -> if (state.appSettingsState.isBiometricsAvailable) it else null
SettingsElement.AppCurrency -> if (cardTypesResolver.isMultiwalletAllowed()) null else it
SettingsElement.ReferralProgram -> if (cardTypesResolver.isTangemWallet()) it else null
SettingsElement.TesterMenu -> if (BuildConfig.TESTER_MENU_ENABLED) it else null
else -> it
@ -91,9 +89,6 @@ internal class DetailsViewModel(private val store: Store<AppState>) {
Analytics.send(Settings.ButtonCardSettings())
store.dispatch(NavigationAction.NavigateTo(AppScreen.CardSettings))
}
SettingsElement.AppCurrency -> {
store.dispatch(WalletAction.AppCurrencyAction.ChooseAppCurrency)
}
SettingsElement.AppSettings -> {
Analytics.send(Settings.ButtonAppSettings())
store.dispatch(NavigationAction.NavigateTo(AppScreen.AppSettings))