Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-07 20:04:35 +05:00
parent 1d47d83d70
commit 6178034e71
20 changed files with 401 additions and 160 deletions

View file

@ -30,6 +30,9 @@ import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.components.block.TangemBlockCardColors
import com.tangem.core.ui.components.label.Label
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.components.text.applyBladeBrush
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme
@ -67,8 +70,10 @@ fun UserWalletItem(
balance = state.balance,
)
state.label?.let { Label(it) }
when (state.endIcon) {
UserWalletItemUM.EndIcon.None -> {}
UserWalletItemUM.EndIcon.None -> Unit
UserWalletItemUM.EndIcon.Arrow -> {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_right_24),
@ -257,6 +262,18 @@ private fun Preview_UserWalletItem(
private class UserWalletItemUMPreviewProvider : PreviewParameterProvider<UserWalletItemUM> {
override val values: Sequence<UserWalletItemUM>
get() = sequenceOf(
UserWalletItemUM(
id = UserWalletId("user_wallet_0".encodeToByteArray()),
name = stringReference("Mobile Wallet"),
information = getInformation(cardCount = 1),
balance = UserWalletItemUM.Balance.Locked,
label = LabelUM(
text = resourceReference(R.string.hw_backup_no_backup),
style = LabelStyle.WARNING,
),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_1".encodeToByteArray()),
name = stringReference("My Wallet"),

View file

@ -2,7 +2,10 @@ package com.tangem.common.ui.userwallet.converter
import com.tangem.common.ui.R
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.fiat
@ -51,6 +54,14 @@ class UserWalletItemUMConverter(
imageState = artwork?.let {
UserWalletItemUM.ImageState.Image(artworkUMConverter.convert(it))
} ?: UserWalletItemUM.ImageState.Loading,
label = if (this is UserWallet.Hot && !this.backedUp) {
LabelUM(
text = resourceReference(R.string.hw_backup_no_backup),
style = LabelStyle.WARNING,
)
} else {
null
},
)
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.common.ui.userwallet.state
import com.tangem.core.ui.components.artwork.ArtworkUM
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.wallet.UserWalletId
import javax.annotation.concurrent.Immutable
@ -15,6 +16,7 @@ data class UserWalletItemUM(
val isEnabled: Boolean,
val endIcon: EndIcon = EndIcon.None,
val onClick: () -> Unit,
val label: LabelUM? = null,
) {
enum class EndIcon {
None,

View file

@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import com.tangem.core.ui.components.block.model.BlockUM
import com.tangem.core.ui.components.label.Label
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
@ -38,6 +39,7 @@ fun BlockItem(model: BlockUM, modifier: Modifier = Modifier) {
)
Text(
modifier = Modifier.weight(1f),
text = model.text.resolveReference(),
style = TangemTheme.typography.subtitle1,
color = when (model.accentType) {
@ -48,6 +50,8 @@ fun BlockItem(model: BlockUM, modifier: Modifier = Modifier) {
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
model.label?.let { Label(it) }
}
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.core.ui.components.block.model
import androidx.annotation.DrawableRes
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.TextReference
data class BlockUM(
@ -8,6 +9,7 @@ data class BlockUM(
@DrawableRes val iconRes: Int,
val onClick: () -> Unit,
val accentType: AccentType = AccentType.NONE,
val label: LabelUM? = null,
) {
enum class AccentType {

View file

@ -0,0 +1,104 @@
package com.tangem.core.ui.components.label
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
/**
* Label component
*
* @param state component state
* @param modifier composable modifier
*
* @see <a href="https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=4480-1459&t=2QTpi1G7FeTexTFS-4">Figma</a>
*/
@Composable
fun Label(state: LabelUM, modifier: Modifier = Modifier) {
val backgroundColor by animateColorAsState(
targetValue = when (state.style) {
LabelStyle.ACCENT -> TangemTheme.colors.text.accent.copy(alpha = 0.1f)
LabelStyle.REGULAR -> TangemTheme.colors.control.unchecked
LabelStyle.WARNING -> TangemTheme.colors.text.warning.copy(alpha = 0.1f)
},
)
val textColor by animateColorAsState(
targetValue = when (state.style) {
LabelStyle.ACCENT -> TangemTheme.colors.text.accent
LabelStyle.REGULAR -> TangemTheme.colors.text.secondary
LabelStyle.WARNING -> TangemTheme.colors.text.warning
},
)
AnimatedContent(targetState = state.text) { text ->
Box(
modifier = modifier
.padding(horizontal = 4.dp)
.background(
color = backgroundColor,
shape = TangemTheme.shapes.roundedCorners8,
)
.padding(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
text = text.resolveReference(),
style = TangemTheme.typography.caption1,
color = textColor,
)
}
}
}
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun LabelPreview() {
TangemThemePreview {
Column(
modifier = Modifier.padding(16.dp),
) {
Label(
state = LabelUM(
text = TextReference.Str("Regular Label"),
style = LabelStyle.REGULAR,
),
)
Spacer(modifier = Modifier.height(8.dp))
Label(
state = LabelUM(
text = TextReference.Str("Accent Label"),
style = LabelStyle.ACCENT,
),
)
Spacer(modifier = Modifier.height(8.dp))
Label(
state = LabelUM(
text = TextReference.Str("Warning Label"),
style = LabelStyle.WARNING,
),
)
}
}
}

View file

@ -0,0 +1,12 @@
package com.tangem.core.ui.components.label.entity
import com.tangem.core.ui.extensions.TextReference
data class LabelUM(
val text: TextReference,
val style: LabelStyle,
)
enum class LabelStyle {
REGULAR, ACCENT, WARNING,
}

View file

@ -13,9 +13,9 @@ internal data class AddExistingWalletImportUM(
val onPassphraseInfoClick: () -> Unit,
val wordsErrorText: TextReference?,
val invalidWords: ImmutableList<String>,
val createWalletEnabled: Boolean,
val createWalletProgress: Boolean,
val createWalletClick: () -> Unit,
val importWalletEnabled: Boolean,
val importWalletProgress: Boolean,
val importWalletClick: () -> Unit,
val suggestionsList: ImmutableList<String>,
val onSuggestionClick: (String) -> Unit,
val infoBottomSheetConfig: TangemBottomSheetConfig,

View file

@ -55,7 +55,7 @@ internal class AddExistingWalletImportModel @Inject constructor(
private fun importWallet(mnemonic: Mnemonic, passphrase: String?) {
modelScope.launch {
uiState.update {
it.copy(createWalletProgress = true)
it.copy(importWalletProgress = true)
}
runCatching {
@ -68,7 +68,7 @@ internal class AddExistingWalletImportModel @Inject constructor(
Timber.e(it)
uiState.update {
it.copy(createWalletProgress = false)
it.copy(importWalletProgress = false)
}
}
}

View file

@ -35,8 +35,8 @@ internal class ImportSeedPhraseUiStateBuilder(
passPhrase = TextFieldValue(""),
wordsErrorText = null,
invalidWords = persistentListOf(),
createWalletEnabled = false,
createWalletProgress = false,
importWalletEnabled = false,
importWalletProgress = false,
suggestionsList = persistentListOf(),
wordsChange = {
launchInterceptWords(wordsField = it)
@ -50,7 +50,7 @@ internal class ImportSeedPhraseUiStateBuilder(
updateUiState { state -> state.copy(passPhrase = it) }
},
onPassphraseInfoClick = ::showInfoBS,
createWalletClick = ::onCreateWallet,
importWalletClick = ::onCreateWallet,
onSuggestionClick = { word -> addSuggestedWord(word) },
readyToImport = false,
infoBottomSheetConfig = TangemBottomSheetConfig.Empty,
@ -115,7 +115,7 @@ internal class ImportSeedPhraseUiStateBuilder(
updateUiState {
it.copy(
createWalletEnabled = false,
importWalletEnabled = false,
wordsErrorText = null,
)
}
@ -130,7 +130,7 @@ internal class ImportSeedPhraseUiStateBuilder(
it.copy(
invalidWords = invalidWords.toImmutableList(),
wordsErrorText = resourceReference(R.string.onboarding_seed_mnemonic_wrong_words),
createWalletEnabled = false,
importWalletEnabled = false,
)
}
return
@ -143,7 +143,7 @@ internal class ImportSeedPhraseUiStateBuilder(
it.copy(
invalidWords = emptyList<String>().toImmutableList(),
wordsErrorText = null,
createWalletEnabled = true,
importWalletEnabled = true,
)
}
readyToImport(true)
@ -154,13 +154,13 @@ internal class ImportSeedPhraseUiStateBuilder(
updateUiState {
it.copy(
wordsErrorText = resourceReference(R.string.onboarding_seed_mnemonic_invalid_checksum),
createWalletEnabled = false,
importWalletEnabled = false,
)
}
} else {
updateUiState {
it.copy(
createWalletEnabled = false,
importWalletEnabled = false,
wordsErrorText = null,
)
}

View file

@ -96,9 +96,9 @@ internal fun AddExistingWalletImportContent(state: AddExistingWalletImportUM, mo
.padding(16.dp)
.fillMaxWidth(),
text = stringResourceSafe(id = R.string.common_import),
enabled = state.createWalletEnabled,
showProgress = state.createWalletProgress,
onClick = state.createWalletClick,
enabled = state.importWalletEnabled,
showProgress = state.importWalletProgress,
onClick = state.importWalletClick,
)
}
@ -228,9 +228,9 @@ private fun PreviewAddExistingWalletImportContent() {
onPassphraseInfoClick = {},
wordsErrorText = null,
invalidWords = persistentListOf(),
createWalletEnabled = false,
createWalletProgress = false,
createWalletClick = {},
importWalletEnabled = false,
importWalletProgress = false,
importWalletClick = {},
suggestionsList = persistentListOf(),
onSuggestionClick = {},
infoBottomSheetConfig = TangemBottomSheetConfig.Empty,

View file

@ -17,7 +17,7 @@ import com.tangem.core.ui.extensions.clickableSingle
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.res.TangemTheme
internal const val DISABLED_COLORS_ALPHA = 0.5f
private const val DISABLED_COLORS_ALPHA = 0.5f
@Suppress("LongParameterList")
@Composable

View file

@ -111,6 +111,9 @@ internal class ManualBackupCheckModel @Inject constructor(
}
callbacks.onCompleteClick()
}
uiState.update {
it.copy(completeButtonProgress = false)
}
}.onFailure {
Timber.e(it)

View file

@ -1,9 +1,11 @@
package com.tangem.features.hotwallet.walletbackup.entity
import com.tangem.core.ui.components.label.entity.LabelUM
internal data class WalletBackupUM(
val onBackClick: () -> Unit,
val recoveryPhraseStatus: BackupStatus,
val googleDriveStatus: BackupStatus,
val recoveryPhraseStatus: LabelUM?,
val googleDriveStatus: LabelUM?,
val onRecoveryPhraseClick: () -> Unit,
val onGoogleDriveClick: () -> Unit,
)

View file

@ -4,9 +4,13 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.R
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.hotwallet.WalletBackupComponent
import com.tangem.features.hotwallet.walletbackup.entity.BackupStatus
import com.tangem.features.hotwallet.walletbackup.entity.WalletBackupUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
@ -26,8 +30,14 @@ internal class WalletBackupModel @Inject constructor(
field = MutableStateFlow(
WalletBackupUM(
onBackClick = { router.pop() },
recoveryPhraseStatus = BackupStatus.NoBackup,
googleDriveStatus = BackupStatus.ComingSoon,
recoveryPhraseStatus = LabelUM(
text = resourceReference(R.string.hw_backup_no_backup),
style = LabelStyle.WARNING,
),
googleDriveStatus = LabelUM(
text = resourceReference(R.string.common_coming_soon),
style = LabelStyle.REGULAR,
),
onRecoveryPhraseClick = { },
onGoogleDriveClick = { },
),
@ -37,18 +47,38 @@ internal class WalletBackupModel @Inject constructor(
getWalletUseCase.invokeFlow(params.userWalletId)
.map { it.getOrNull() }
.distinctUntilChanged()
.filterNotNull()
.onEach {
updateBackupStatuses()
updateBackupStatuses(it)
}
.launchIn(modelScope)
}
private fun updateBackupStatuses() {
private fun updateBackupStatuses(userWallet: UserWallet) {
uiState.update { currentState ->
currentState.copy(
recoveryPhraseStatus = BackupStatus.NoBackup,
googleDriveStatus = BackupStatus.ComingSoon,
)
if (userWallet is UserWallet.Hot) {
currentState.updateBackupStatusesHotWallet(userWallet)
} else {
currentState
}
}
}
private fun WalletBackupUM.updateBackupStatusesHotWallet(userWallet: UserWallet.Hot): WalletBackupUM = copy(
recoveryPhraseStatus = if (userWallet.backedUp) {
LabelUM(
text = resourceReference(R.string.common_done),
style = LabelStyle.ACCENT,
)
} else {
LabelUM(
text = resourceReference(R.string.hw_backup_no_backup),
style = LabelStyle.WARNING,
)
},
googleDriveStatus = LabelUM(
text = resourceReference(R.string.common_coming_soon),
style = LabelStyle.REGULAR,
),
)
}

View file

@ -1,20 +1,14 @@
package com.tangem.features.hotwallet.walletbackup.ui
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -28,7 +22,10 @@ import com.tangem.features.hotwallet.walletbackup.entity.BackupStatus
import com.tangem.features.hotwallet.walletbackup.entity.WalletBackupUM
import com.tangem.features.hotwallet.common.ui.OptionBlock
import com.tangem.core.ui.R
import com.tangem.features.hotwallet.common.ui.DISABLED_COLORS_ALPHA
import com.tangem.core.ui.components.label.Label
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.resourceReference
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -54,7 +51,7 @@ internal fun WalletBackupContent(state: WalletBackupUM, modifier: Modifier = Mod
title = stringResourceSafe(R.string.hw_backup_seed_title),
description = stringResourceSafe(R.string.hw_backup_seed_description),
badge = {
BackupStatusBadge(status = state.recoveryPhraseStatus)
state.recoveryPhraseStatus?.let { Label(it) }
},
onClick = state.onRecoveryPhraseClick,
enabled = true,
@ -66,7 +63,7 @@ internal fun WalletBackupContent(state: WalletBackupUM, modifier: Modifier = Mod
title = stringResourceSafe(R.string.hw_backup_google_drive_title),
description = stringResourceSafe(R.string.hw_backup_google_drive_description),
badge = {
BackupStatusBadge(status = state.googleDriveStatus)
state.googleDriveStatus?.let { Label(it) }
},
onClick = state.onGoogleDriveClick,
enabled = state.googleDriveStatus != BackupStatus.ComingSoon,
@ -76,49 +73,6 @@ internal fun WalletBackupContent(state: WalletBackupUM, modifier: Modifier = Mod
}
}
@Composable
private fun BackupStatusBadge(status: BackupStatus, modifier: Modifier = Modifier) {
val text = when (status) {
BackupStatus.Done -> stringResourceSafe(R.string.common_done)
BackupStatus.ComingSoon -> stringResourceSafe(R.string.common_coming_soon)
BackupStatus.NoBackup -> stringResourceSafe(R.string.hw_backup_no_backup)
}
val backgroundColor by animateColorAsState(
targetValue = when (status) {
BackupStatus.Done -> TangemTheme.colors.text.accent.copy(alpha = 0.1f)
BackupStatus.ComingSoon -> TangemTheme.colors.control.unchecked.copy(DISABLED_COLORS_ALPHA)
BackupStatus.NoBackup -> TangemTheme.colors.text.warning.copy(alpha = 0.1f)
},
)
val textColor by animateColorAsState(
targetValue = when (status) {
BackupStatus.Done -> TangemTheme.colors.text.accent
BackupStatus.ComingSoon -> TangemTheme.colors.text.secondary.copy(DISABLED_COLORS_ALPHA)
BackupStatus.NoBackup -> TangemTheme.colors.text.warning
},
)
AnimatedContent(targetState = text) { text ->
Box(
modifier = modifier
.padding(horizontal = 4.dp)
.background(
color = backgroundColor,
shape = TangemTheme.shapes.roundedCorners8,
)
.padding(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
text = text,
style = TangemTheme.typography.caption1,
color = textColor,
)
}
}
}
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
@ -131,22 +85,40 @@ private fun WalletBackupContentPreview(@PreviewParameter(WalletBackupUMProvider:
private class WalletBackupUMProvider : CollectionPreviewParameterProvider<WalletBackupUM>(
collection = listOf(
WalletBackupUM(
recoveryPhraseStatus = BackupStatus.NoBackup,
googleDriveStatus = BackupStatus.ComingSoon,
recoveryPhraseStatus = LabelUM(
text = resourceReference(R.string.hw_backup_no_backup),
style = LabelStyle.WARNING,
),
googleDriveStatus = LabelUM(
text = resourceReference(R.string.common_coming_soon),
style = LabelStyle.REGULAR,
),
onBackClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveClick = {},
),
WalletBackupUM(
recoveryPhraseStatus = BackupStatus.NoBackup,
googleDriveStatus = BackupStatus.NoBackup,
recoveryPhraseStatus = LabelUM(
text = resourceReference(R.string.hw_backup_no_backup),
style = LabelStyle.WARNING,
),
googleDriveStatus = LabelUM(
text = resourceReference(R.string.hw_backup_no_backup),
style = LabelStyle.WARNING,
),
onBackClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveClick = {},
),
WalletBackupUM(
recoveryPhraseStatus = BackupStatus.Done,
googleDriveStatus = BackupStatus.Done,
recoveryPhraseStatus = LabelUM(
text = resourceReference(R.string.common_done),
style = LabelStyle.ACCENT,
),
googleDriveStatus = LabelUM(
text = resourceReference(R.string.common_done),
style = LabelStyle.ACCENT,
),
onBackClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveClick = {},

View file

@ -65,4 +65,7 @@ dependencies {
implementation(deps.kotlin.immutable.collections)
implementation(deps.timber)
implementation(deps.reKotlin)
/** Tangem libraries */
implementation(tangemDeps.hot.core)
}

View file

@ -4,11 +4,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.analytics.DummyAnalyticsEventHandler
import com.tangem.core.decompose.navigation.DummyRouter
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
import com.tangem.feature.walletsettings.entity.WalletSettingsUM
import com.tangem.feature.walletsettings.ui.WalletSettingsScreen
import com.tangem.feature.walletsettings.utils.ItemsBuilder
import com.tangem.hot.sdk.model.HotWalletId
internal class PreviewWalletSettingsComponent : WalletSettingsComponent {
@ -18,7 +20,13 @@ internal class PreviewWalletSettingsComponent : WalletSettingsComponent {
router = DummyRouter(),
analyticsEventHandler = DummyAnalyticsEventHandler(),
).buildItems(
userWalletId = UserWalletId("011"),
userWallet = UserWallet.Hot(
walletId = UserWalletId("011"),
name = "My Wallet",
hotWalletId = HotWalletId("", HotWalletId.AuthType.NoPassword),
wallets = null,
backedUp = false,
),
userWalletName = "My Wallet",
isReferralAvailable = true,
isLinkMoreCardsAvailable = true,
@ -36,7 +44,7 @@ internal class PreviewWalletSettingsComponent : WalletSettingsComponent {
onCheckedNotificationsChanged = {},
onNotificationsDescriptionClick = {},
isNotificationsPermissionGranted = false,
isHotWalletEnabled = false,
onAccessCodeClick = {},
),
requestPushNotificationsPermission = false,
onPushNotificationPermissionGranted = {},

View file

@ -31,8 +31,6 @@ import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.notifications.toggles.NotificationsFeatureToggles
import com.tangem.domain.settings.repositories.PermissionRepository
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.wallets.usecase.*
import com.tangem.feature.walletsettings.analytics.Settings
import com.tangem.feature.walletsettings.analytics.WalletSettingsAnalyticEvents
@ -43,7 +41,6 @@ import com.tangem.feature.walletsettings.entity.WalletSettingsItemUM
import com.tangem.feature.walletsettings.entity.WalletSettingsUM
import com.tangem.feature.walletsettings.impl.R
import com.tangem.feature.walletsettings.utils.ItemsBuilder
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnalyticEvents
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.PersistentList
@ -75,7 +72,6 @@ internal class WalletSettingsModel @Inject constructor(
private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase,
private val settingsManager: SettingsManager,
private val permissionsRepository: PermissionRepository,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val notificationsRepository: NotificationsRepository,
private val getIsHuaweiDeviceWithoutGoogleServicesUseCase: GetIsHuaweiDeviceWithoutGoogleServicesUseCase,
) : Model() {
@ -100,7 +96,6 @@ internal class WalletSettingsModel @Inject constructor(
getWalletNotificationsEnabledUseCase(params.userWalletId),
) { maybeWallet, nftEnabled, notificationsEnabled ->
val wallet = maybeWallet.getOrNull() ?: return@combine
wallet.requireColdWallet() // TODO [REDACTED_TASK_KEY] [Hot Wallet] Wallet Settings
val isRenameWalletAvailable = getShouldSaveUserWalletsSyncUseCase()
val isNeedShowNotifications = notificationsToggles.isNotificationsEnabled &&
!getIsHuaweiDeviceWithoutGoogleServicesUseCase()
@ -114,7 +109,6 @@ internal class WalletSettingsModel @Inject constructor(
isNotificationsEnabled = notificationsEnabled,
isNotificationsFeatureEnabled = isNeedShowNotifications,
isNotificationsPermissionGranted = isNotificationsPermissionGranted(),
isHotWalletEnabled = hotWalletFeatureToggles.isHotWalletEnabled,
),
)
}
@ -133,51 +127,65 @@ internal class WalletSettingsModel @Inject constructor(
}
private fun buildItems(
userWallet: UserWallet.Cold,
userWallet: UserWallet,
dialogNavigation: SlotNavigation<DialogConfig>,
isRenameWalletAvailable: Boolean,
isNFTEnabled: Boolean,
isNotificationsFeatureEnabled: Boolean,
isNotificationsEnabled: Boolean,
isNotificationsPermissionGranted: Boolean,
isHotWalletEnabled: Boolean,
): PersistentList<WalletSettingsItemUM> = itemsBuilder.buildItems(
userWalletId = userWallet.walletId,
userWalletName = userWallet.name,
isReferralAvailable = userWallet.cardTypesResolver.isTangemWallet(),
isLinkMoreCardsAvailable = userWallet.scanResponse.card.backupStatus == CardDTO.BackupStatus.NoBackup,
isManageTokensAvailable = userWallet.isMultiCurrency,
isRenameWalletAvailable = isRenameWalletAvailable,
renameWallet = { openRenameWalletDialog(userWallet, dialogNavigation) },
isNFTFeatureEnabled = userWallet.isMultiCurrency,
isNFTEnabled = isNFTEnabled,
onCheckedNFTChange = ::onCheckedNFTChange,
forgetWallet = {
val message = DialogMessage(
message = resourceReference(R.string.user_wallet_list_delete_prompt),
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.common_delete),
warning = true,
onClick = ::forgetWallet,
)
},
secondActionBuilder = { cancelAction() },
)
): PersistentList<WalletSettingsItemUM> {
val isMultiCurrency = when (userWallet) {
is UserWallet.Cold -> userWallet.isMultiCurrency
is UserWallet.Hot -> true
}
return itemsBuilder.buildItems(
userWallet = userWallet,
userWalletName = userWallet.name,
isReferralAvailable = when (userWallet) {
is UserWallet.Cold -> userWallet.cardTypesResolver.isTangemWallet()
is UserWallet.Hot -> false
},
isLinkMoreCardsAvailable = when (userWallet) {
is UserWallet.Cold -> userWallet.scanResponse.card.backupStatus == CardDTO.BackupStatus.NoBackup
is UserWallet.Hot -> false
},
isManageTokensAvailable = isMultiCurrency,
isRenameWalletAvailable = isRenameWalletAvailable,
renameWallet = { openRenameWalletDialog(userWallet, dialogNavigation) },
isNFTFeatureEnabled = isMultiCurrency,
isNFTEnabled = isNFTEnabled,
onCheckedNFTChange = ::onCheckedNFTChange,
forgetWallet = {
val message = DialogMessage(
message = resourceReference(R.string.user_wallet_list_delete_prompt),
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.common_delete),
warning = true,
onClick = ::forgetWallet,
)
},
secondActionBuilder = { cancelAction() },
)
messageSender.send(message)
},
onLinkMoreCardsClick = {
onLinkMoreCardsClick(scanResponse = userWallet.scanResponse)
},
onReferralClick = { onReferralClick(userWallet) },
isNotificationsEnabled = isNotificationsEnabled,
isNotificationsFeatureEnabled = isNotificationsFeatureEnabled,
isNotificationsPermissionGranted = isNotificationsPermissionGranted,
onCheckedNotificationsChanged = ::onCheckedNotificationsChange,
onNotificationsDescriptionClick = ::onNotificationsDescriptionClick,
isHotWalletEnabled = isHotWalletEnabled,
)
messageSender.send(message)
},
onLinkMoreCardsClick = {
when (userWallet) {
is UserWallet.Cold -> onLinkMoreCardsClick(scanResponse = userWallet.scanResponse)
is UserWallet.Hot -> Unit
}
},
onReferralClick = { onReferralClick(userWallet) },
isNotificationsEnabled = isNotificationsEnabled,
isNotificationsFeatureEnabled = isNotificationsFeatureEnabled,
isNotificationsPermissionGranted = isNotificationsPermissionGranted,
onCheckedNotificationsChanged = ::onCheckedNotificationsChange,
onNotificationsDescriptionClick = ::onNotificationsDescriptionClick,
onAccessCodeClick = ::onAccessCodeClick,
)
}
private fun openRenameWalletDialog(userWallet: UserWallet, dialogNavigation: SlotNavigation<DialogConfig>) {
val config = DialogConfig.RenameWallet(
@ -320,4 +328,8 @@ internal class WalletSettingsModel @Inject constructor(
router.push(AppRoute.ReferralProgram(userWallet.walletId))
}
}
private fun onAccessCodeClick() {
// TODO [REDACTED_TASK_KEY]
}
}

View file

@ -6,12 +6,15 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.components.block.model.BlockUM
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.walletsettings.analytics.Settings
import com.tangem.feature.walletsettings.entity.WalletSettingsItemUM
import com.tangem.feature.walletsettings.impl.R
import com.tangem.hot.sdk.model.HotWalletId
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@ -25,7 +28,7 @@ internal class ItemsBuilder @Inject constructor(
@Suppress("LongParameterList")
fun buildItems(
userWalletId: UserWalletId,
userWallet: UserWallet,
userWalletName: String,
isLinkMoreCardsAvailable: Boolean,
isReferralAvailable: Boolean,
@ -43,25 +46,18 @@ internal class ItemsBuilder @Inject constructor(
renameWallet: () -> Unit,
onLinkMoreCardsClick: () -> Unit,
onReferralClick: () -> Unit,
isHotWalletEnabled: Boolean,
onAccessCodeClick: () -> Unit,
): PersistentList<WalletSettingsItemUM> = persistentListOf<WalletSettingsItemUM>()
.add(buildNameItem(userWalletName, isRenameWalletAvailable, renameWallet))
.run {
if (isNFTFeatureEnabled) {
add(buildNFTItem(isNFTEnabled, onCheckedNFTChange))
} else {
this
}
}
.addAll(buildAccessCodeItem(userWallet, onAccessCodeClick))
.add(
buildCardItem(
userWalletId = userWalletId,
userWallet = userWallet,
isLinkMoreCardsAvailable = isLinkMoreCardsAvailable,
isReferralAvailable = isReferralAvailable,
isManageTokensAvailable = isManageTokensAvailable,
onLinkMoreCardsClick = onLinkMoreCardsClick,
onReferralClick = onReferralClick,
isHotWalletEnabled = isHotWalletEnabled,
),
)
.addAll(
@ -73,8 +69,27 @@ internal class ItemsBuilder @Inject constructor(
onNotificationsDescriptionClick = onNotificationsDescriptionClick,
),
)
.addAll(
buildNFTItems(
isNFTFeatureEnabled = isNFTFeatureEnabled,
isNFTEnabled = isNFTEnabled,
onCheckedNFTChange = onCheckedNFTChange,
),
)
.add(buildForgetItem(forgetWallet))
private fun buildNFTItems(
isNFTFeatureEnabled: Boolean,
isNFTEnabled: Boolean,
onCheckedNFTChange: (Boolean) -> Unit,
): List<WalletSettingsItemUM> {
return if (isNFTFeatureEnabled) {
listOf(buildNFTItem(isNFTEnabled, onCheckedNFTChange))
} else {
emptyList()
}
}
private fun buildNotificationItems(
isNotificationsFeatureEnabled: Boolean,
isNotificationsPermissionGranted: Boolean,
@ -133,17 +148,35 @@ internal class ItemsBuilder @Inject constructor(
@Suppress("LongParameterList")
private fun buildCardItem(
userWalletId: UserWalletId,
userWallet: UserWallet,
isLinkMoreCardsAvailable: Boolean,
isReferralAvailable: Boolean,
isManageTokensAvailable: Boolean,
onLinkMoreCardsClick: () -> Unit,
onReferralClick: () -> Unit,
isHotWalletEnabled: Boolean,
) = WalletSettingsItemUM.WithItems(
id = "card",
description = resourceReference(R.string.settings_card_settings_footer),
blocks = buildList {
val userWalletId = userWallet.walletId
val isHotWallet = userWallet is UserWallet.Hot
if (isHotWallet) {
val hasBackup = userWallet.backedUp
BlockUM(
text = resourceReference(R.string.common_backup),
iconRes = R.drawable.ic_more_cards_24,
onClick = { router.push(AppRoute.WalletBackup(userWalletId)) },
label = if (hasBackup) {
null
} else {
LabelUM(
text = resourceReference(R.string.hw_backup_no_backup),
style = LabelStyle.WARNING,
)
},
).let(::add)
}
if (isManageTokensAvailable) {
BlockUM(
text = resourceReference(R.string.add_tokens_title),
@ -163,17 +196,11 @@ internal class ItemsBuilder @Inject constructor(
).let(::add)
}
BlockUM(
text = resourceReference(R.string.card_settings_title),
iconRes = R.drawable.ic_card_settings_24,
onClick = { router.push(AppRoute.CardSettings(userWalletId)) },
).let(::add)
if (isHotWalletEnabled) {
if (!isHotWallet) {
BlockUM(
text = resourceReference(R.string.common_backup),
iconRes = R.drawable.ic_more_cards_24,
onClick = { router.push(AppRoute.WalletBackup(userWalletId)) },
text = resourceReference(R.string.card_settings_title),
iconRes = R.drawable.ic_card_settings_24,
onClick = { router.push(AppRoute.CardSettings(userWalletId)) },
).let(::add)
}
@ -199,4 +226,36 @@ internal class ItemsBuilder @Inject constructor(
),
),
)
private fun buildAccessCodeItem(userWallet: UserWallet, onItemClick: () -> Unit): List<WalletSettingsItemUM> {
return when (userWallet) {
is UserWallet.Cold -> emptyList()
is UserWallet.Hot -> buildHotWalletAccessCodeItem(userWallet, onItemClick)
}
}
private fun buildHotWalletAccessCodeItem(
userWallet: UserWallet.Hot,
onItemClick: () -> Unit,
): List<WalletSettingsItemUM> {
val isCodeSet = userWallet.hotWalletId.authType != HotWalletId.AuthType.NoPassword
return listOf(
WalletSettingsItemUM.WithItems(
id = "access_code",
description = resourceReference(R.string.wallet_settings_access_code_description),
blocks = persistentListOf(
BlockUM(
text = if (isCodeSet) {
resourceReference(R.string.wallet_settings_change_access_code_title)
} else {
resourceReference(R.string.wallet_settings_set_access_code_title)
},
iconRes = R.drawable.ic_lock_24,
onClick = onItemClick,
accentType = BlockUM.AccentType.ACCENT,
),
),
),
)
}
}