Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-18 12:34:18 +03:00
parent 48b8c77f5d
commit cb795ebf3d
27 changed files with 432 additions and 262 deletions

View file

@ -36,6 +36,7 @@ internal class UserWalletListModel @Inject constructor(
private val userWalletsFetcher = userWalletsFetcherFactory.create(
messageSender = messageSender,
onlyMultiCurrency = false,
authMode = false,
onWalletClick = { userWalletId -> router.push(AppRoute.WalletSettings(userWalletId)) },
)

View file

@ -5,10 +5,10 @@ import arrow.core.getOrElse
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.features.hotwallet.accesscode.entity.AccessCodeUM
import com.tangem.hot.sdk.TangemHotSdk
import com.tangem.hot.sdk.model.HotAuth
@ -27,7 +27,7 @@ internal class AccessCodeModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val saveWalletUseCase: SaveWalletUseCase,
private val userWalletsListRepository: UserWalletsListRepository,
private val tangemHotSdk: TangemHotSdk,
) : Model() {
@ -77,15 +77,41 @@ internal class AccessCodeModel @Inject constructor(
runCatching {
val userWallet = getUserWalletUseCase(userWalletId)
.getOrElse { error("User wallet with id $userWalletId not found") }
if (userWallet is UserWallet.Hot) {
val unlockHotWallet = UnlockHotWallet(userWallet.hotWalletId, HotAuth.NoAuth)
val updatedHotWalletId = tangemHotSdk.changeAuth(
unlockHotWallet = unlockHotWallet,
if (userWallet !is UserWallet.Hot) return@launch
val unlockHotWallet = UnlockHotWallet(userWallet.hotWalletId, HotAuth.NoAuth)
var updatedHotWalletId = tangemHotSdk.changeAuth(
unlockHotWallet = unlockHotWallet,
auth = HotAuth.Password(accessCode.toCharArray()),
)
updatedHotWalletId = tangemHotSdk.changeAuth(
unlockHotWallet = UnlockHotWallet(
walletId = updatedHotWalletId,
auth = HotAuth.Password(accessCode.toCharArray()),
)
saveWalletUseCase(userWallet.copy(hotWalletId = updatedHotWalletId), canOverride = true)
params.callbacks.onAccessCodeConfirmed(params.userWalletId)
}
),
auth = HotAuth.Biometry,
)
userWalletsListRepository.saveWithoutLock(
userWallet.copy(
hotWalletId = updatedHotWalletId,
backedUp = true,
),
canOverride = true,
)
userWalletsListRepository.setLock(
userWallet.walletId,
UserWalletsListRepository.LockMethod.AccessCode(accessCode.toCharArray()),
)
userWalletsListRepository.setLock(
userWallet.walletId,
UserWalletsListRepository.LockMethod.Biometric,
)
params.callbacks.onAccessCodeConfirmed(params.userWalletId)
}.onFailure {
Timber.e(it)

View file

@ -45,9 +45,8 @@ internal class CreateMobileWalletModel @Inject constructor(
runCatching {
val hotWalletId = tangemHotSdk.generateWallet(HotAuth.NoAuth, mnemonicType = MnemonicType.Words12)
val hotUserWalletBuilder = hotUserWalletBuilderFactory.create(hotWalletId)
saveUserWalletUseCase(
hotUserWalletBuilder.build(),
)
val userWallet = hotUserWalletBuilder.build()
saveUserWalletUseCase(userWallet)
router.replaceAll(AppRoute.Wallet)
}.onFailure {
Timber.e(it)

View file

@ -14,6 +14,7 @@ interface UserWalletsFetcher {
fun create(
messageSender: UiMessageSender,
onlyMultiCurrency: Boolean,
authMode: Boolean,
onWalletClick: (UserWalletId) -> Unit,
): UserWalletsFetcher
}

View file

@ -43,7 +43,8 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
@Assisted private val onWalletClick: (UserWalletId) -> Unit,
@Assisted private val messageSender: UiMessageSender,
@Assisted private val onlyMultiCurrency: Boolean,
@Assisted("onlyMultiCurrency") private val onlyMultiCurrency: Boolean,
@Assisted("authMode") private val authMode: Boolean,
private val getCardImageUseCase: GetCardImageUseCase,
dispatchers: CoroutineDispatcherProvider,
) : UserWalletsFetcher {
@ -54,7 +55,10 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
@OptIn(ExperimentalCoroutinesApi::class)
override val userWallets: Flow<ImmutableList<UserWalletItemUM>> = walletsFlow.transformLatest { wallets ->
val uiModels = UserWalletItemUMConverter(onClick = onWalletClick).convertList(wallets)
val uiModels = UserWalletItemUMConverter(
onClick = onWalletClick,
authMode = authMode,
).convertList(wallets)
.toImmutableList()
emit(uiModels)
@ -132,6 +136,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
balance = balance,
isBalanceHidden = balanceHidingSettings.isBalanceHidden,
artwork = artworks[userWallet.walletId],
authMode = authMode,
)
.convert(userWallet)
}
@ -149,7 +154,8 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
interface Factory : UserWalletsFetcher.Factory {
override fun create(
messageSender: UiMessageSender,
onlyMultiCurrency: Boolean,
@Assisted("onlyMultiCurrency") onlyMultiCurrency: Boolean,
@Assisted("authMode") authMode: Boolean,
onWalletClick: (UserWalletId) -> Unit,
): DefaultUserWalletsFetcher
}

View file

@ -29,6 +29,7 @@ internal class WcUserWalletsFetcher(
private val userWalletsFetcher = userWalletsFetcherFactory.create(
messageSender = messageSender,
onlyMultiCurrency = true,
authMode = false,
onWalletClick = { onWalletSelected(it) },
)

View file

@ -13,11 +13,13 @@ android {
dependencies {
implementation(projects.features.welcome.api)
implementation(projects.features.wallet.api)
/** Core */
implementation(projects.core.configToggles)
implementation(projects.core.decompose)
implementation(projects.core.ui)
implementation(projects.core.analytics)
implementation(projects.common.routing)
implementation(projects.common.ui)
@ -30,6 +32,7 @@ dependencies {
/** Domain */
implementation(projects.domain.appCurrency)
implementation(projects.domain.wallets)
implementation(projects.domain.card)
/** DI */
implementation(deps.hilt.android)
@ -54,4 +57,5 @@ dependencies {
implementation(deps.timber)
implementation(tangemDeps.card.core)
implementation(tangemDeps.blockchain)
implementation(tangemDeps.hot.core)
}

View file

@ -1,18 +1,233 @@
package com.tangem.features.welcome.impl.model
import com.tangem.common.routing.AppRoute
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.core.wallets.error.UnlockWalletError
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetIsBiometricsEnabledUseCase
import com.tangem.features.wallet.utils.UserWalletsFetcher
import com.tangem.features.welcome.impl.R
import com.tangem.features.welcome.impl.ui.state.AddWalletBottomSheetContentUM
import com.tangem.features.welcome.impl.ui.state.AddWalletBottomSheetContentUM.Option.*
import com.tangem.features.welcome.impl.ui.state.WelcomeUM
import com.tangem.hot.sdk.model.HotWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@ModelScoped
internal class WelcomeModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val uiMessageSender: UiMessageSender,
private val userWalletsFetcherFactory: UserWalletsFetcher.Factory,
private val userWalletsListRepository: UserWalletsListRepository,
private val getIsBiometricsEnabledUseCase: GetIsBiometricsEnabledUseCase,
) : Model() {
// TODO add intent handling
// val params
val uiState: StateFlow<WelcomeUM>
field = MutableStateFlow(WelcomeUM.Plain)
field = MutableStateFlow<WelcomeUM>(WelcomeUM.Plain)
private val walletsFetcher = userWalletsFetcherFactory.create(
messageSender = uiMessageSender,
onlyMultiCurrency = false,
authMode = true,
onWalletClick = { walletId ->
modelScope.launch {
val userWallets = userWalletsListRepository.userWalletsSync()
val userWallet = userWallets.first { it.walletId == walletId }
onUserWalletClick(userWallet)
}
},
)
private val walletsFetcherJobHolder = JobHolder()
private val wallets = MutableStateFlow<ImmutableList<UserWalletItemUM>>(persistentListOf())
init {
modelScope.launch {
userWalletsListRepository.load()
wallets.value = walletsFetcher.userWallets.first()
launch {
walletsFetcher.userWallets
.collectLatest { wallets.value = it }
}
tryToUnlockRightAway()
}
}
private fun tryToUnlockRightAway() {
modelScope.launch {
if (canUnlockWithBiometrics()) {
userWalletsListRepository.unlockAllWallets()
.onRight {
router.replaceAll(AppRoute.Wallet)
}
.onLeft {
it.handle(null, onUserCancelled = { tryToUnlockWithAccessCodeRightAway() })
setSelectWalletState()
}
} else {
tryToUnlockWithAccessCodeRightAway()
setSelectWalletState()
}
}
}
private fun tryToUnlockWithAccessCodeRightAway() = modelScope.launch {
if (onlyOneHotWalletWithAccessCode()) {
val userWallets = userWalletsListRepository.userWalletsSync()
val userWallet = userWallets.first()
unlockWallet(userWallet.walletId, UserWalletsListRepository.UnlockMethod.AccessCode)
}
}
private fun setSelectWalletState() {
modelScope.launch {
uiState.value = WelcomeUM.SelectWallet(
wallets = walletsFetcher.userWallets.first(),
showUnlockWithBiometricButton = canUnlockWithBiometrics(),
addWalletClick = ::addWalletClick,
onUnlockWithBiometricClick = {
modelScope.launch {
userWalletsListRepository.unlockAllWallets()
.onRight {
router.replaceAll(AppRoute.Wallet)
}
.onLeft {
it.handle(null, onUserCancelled = { /* ignore */ })
}
}
},
)
wallets.collectLatest { wallets ->
updateSelectState {
it.copy(wallets = wallets)
}
}
}.saveIn(walletsFetcherJobHolder)
}
private fun addWalletClick() {
updateSelectState { currentState ->
currentState.copy(
addWalletBottomSheet = TangemBottomSheetConfig(
isShown = true,
content = AddWalletBottomSheetContentUM(
onOptionClick = ::onAddWalletOptionClick,
),
onDismissRequest = {
updateSelectState {
it.copy(addWalletBottomSheet = it.addWalletBottomSheet.copy(isShown = false))
}
},
),
)
}
}
private fun onAddWalletOptionClick(option: AddWalletBottomSheetContentUM.Option) {
when (option) {
Create -> router.push(AppRoute.CreateWalletSelection)
Add -> router.push(AppRoute.AddExistingWallet)
Buy -> {
}
}
}
private suspend fun onlyOneHotWalletWithAccessCode(): Boolean {
val userWalletsWithLock = userWalletsListRepository.userWalletsSync().filter { it.isLocked }
if (userWalletsWithLock.size != 1) return false
val wallet = userWalletsWithLock.first()
return wallet is UserWallet.Hot && wallet.hotWalletId.authType != HotWalletId.AuthType.NoPassword
}
private fun onUserWalletClick(userWallet: UserWallet) = modelScope.launch {
if (userWallet.isLocked.not()) {
// If the wallet is not locked, we can proceed to the wallet screen directly
userWalletsListRepository.select(userWallet.walletId)
router.replaceAll(AppRoute.Wallet)
return@launch
}
val unlockMethod = when (userWallet) {
is UserWallet.Cold -> UserWalletsListRepository.UnlockMethod.Scan
is UserWallet.Hot -> UserWalletsListRepository.UnlockMethod.AccessCode
}
unlockWallet(userWallet.walletId, unlockMethod)
}
private fun canUnlockWithBiometrics(): Boolean {
return getIsBiometricsEnabledUseCase.canUseBiometry()
}
suspend fun unlockWallet(userWalletId: UserWalletId, unlockMethod: UserWalletsListRepository.UnlockMethod) {
userWalletsListRepository.unlock(userWalletId, unlockMethod)
.onRight {
userWalletsListRepository.select(userWalletId)
router.replaceAll(AppRoute.Wallet)
}
.onLeft { error ->
error.handle(specificWalletId = userWalletId, onUserCancelled = { /* ignore*/ })
}
}
suspend fun UnlockWalletError.handle(specificWalletId: UserWalletId?, onUserCancelled: () -> Unit = { }) {
when (this) {
UnlockWalletError.AlreadyUnlocked -> {
// this should not happen, as we check for locked state before this
specificWalletId?.let { userWalletsListRepository.select(it) }
router.replaceAll(AppRoute.Wallet)
}
UnlockWalletError.ScannedCardWalletNotMatched -> {
// TODO Scanned card does not match the wallet
}
UnlockWalletError.UnableToUnlock -> {
// TODO Unable to unlock the wallet"
}
UnlockWalletError.UserCancelled -> onUserCancelled()
UnlockWalletError.UserWalletNotFound -> {
// This should never happen in this flow, as we always check for the wallet existence before unlocking
Timber.e("User wallet not found for unlock: $specificWalletId")
uiMessageSender.send(
SnackbarMessage(TextReference.Res(R.string.generic_error)),
)
}
}
}
private fun updateSelectState(block: (WelcomeUM.SelectWallet) -> WelcomeUM.SelectWallet) {
uiState.update { currentState ->
if (currentState is WelcomeUM.SelectWallet) {
block(currentState)
} else {
currentState
}
}
}
}

View file

@ -10,10 +10,11 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.extensions.TextReference
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.welcome.impl.ui.state.WalletUM
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.welcome.impl.ui.state.WelcomeUM
import kotlinx.collections.immutable.persistentListOf
@ -34,10 +35,6 @@ internal fun Welcome(state: WelcomeUM, modifier: Modifier = Modifier) {
state = st,
modifier = modifier,
)
is WelcomeUM.EnterAccessCode -> WelcomeEnterAccessCode(
state = st,
modifier = modifier,
)
}
}
}
@ -49,22 +46,30 @@ private fun Preview() {
TangemThemePreview {
val state = WelcomeUM.SelectWallet(
wallets = persistentListOf(
WalletUM(
name = TextReference.Str("Wallet 1"),
subtitle = TextReference.Str("3 cards"),
imageState = WalletUM.ImageState.Loading,
UserWalletItemUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = stringReference("Multi Card"),
information = UserWalletItemUM.Information.Loading,
balance = UserWalletItemUM.Balance.Loaded(
value = "1.2345 BTC",
isFlickering = false,
),
isEnabled = true,
onClick = {},
),
WalletUM(
name = TextReference.Str("Wallet 1"),
subtitle = TextReference.Str("Mobile wallet"),
imageState = WalletUM.ImageState.MobileWallet,
UserWalletItemUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = stringReference("Multi Card"),
information = UserWalletItemUM.Information.Failed,
imageState = UserWalletItemUM.ImageState.MobileWallet,
balance = UserWalletItemUM.Balance.Locked,
isEnabled = true,
onClick = {},
),
),
)
var currentState by remember { mutableStateOf<WelcomeUM>(WelcomeUM.EnterAccessCode()) }
var currentState by remember { mutableStateOf<WelcomeUM>(WelcomeUM.SelectWallet()) }
Box {
Welcome(currentState)
@ -74,11 +79,7 @@ private fun Preview() {
onClick = {
currentState = when (currentState) {
is WelcomeUM.Plain -> state
is WelcomeUM.SelectWallet -> WelcomeUM.EnterAccessCode(
value = "",
onValueChange = {},
)
is WelcomeUM.EnterAccessCode -> WelcomeUM.Plain
is WelcomeUM.SelectWallet -> WelcomeUM.Plain
}
},
) {

View file

@ -1,94 +0,0 @@
package com.tangem.features.welcome.impl.ui
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerH24
import com.tangem.core.ui.components.appbar.TopAppBarButton
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.components.fields.PinTextField
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.welcome.impl.ui.state.WelcomeUM
@Suppress("MagicNumber")
@Composable
internal fun AnimatedContentScope.WelcomeEnterAccessCode(
state: WelcomeUM.EnterAccessCode,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier
.fillMaxSize()
.statusBarsPadding(),
) {
Column {
TopAppBarButton(
modifier = Modifier
.padding(12.dp),
button = TopAppBarButtonUM.Back(onBackClicked = state.onBackClick),
tint = TangemTheme.colors.icon.primary1,
)
SpacerH(68.dp)
Text(
modifier = Modifier
.animateEnterExit(
enter = slideInVertically(
tween(delayMillis = 300),
initialOffsetY = { it + 200 },
) + fadeIn(tween(delayMillis = 300)),
exit = fadeOut(),
)
.align(Alignment.CenterHorizontally),
text = "Enter Access Code",
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
)
SpacerH24()
Box(
modifier = Modifier
.animateEnterExit(
enter = slideInVertically(
tween(delayMillis = 300),
initialOffsetY = { it + 200 },
) + fadeIn(tween(delayMillis = 300)),
exit = fadeOut(),
)
.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
PinTextField(
length = 6,
isPasswordVisual = true,
value = state.value,
onValueChange = state.onValueChange,
)
}
}
SecondaryButton(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.padding(16.dp)
.navigationBarsPadding()
.imePadding()
.animateEnterExit(fadeIn(), fadeOut()),
text = "Log in with biometric",
onClick = state.onUnlockWithBiometricClick,
)
}
}

View file

@ -9,8 +9,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.welcome.impl.R
@Composable
@ -26,4 +28,14 @@ internal fun WelcomePlain(modifier: Modifier = Modifier) {
contentDescription = null,
)
}
}
@Preview(showBackground = true)
@Composable
private fun Preview() {
TangemThemePreview {
WelcomePlain(
modifier = Modifier.fillMaxSize(),
)
}
}

View file

@ -5,12 +5,9 @@ import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.*
@ -20,14 +17,13 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.userwallet.CardImage
import com.tangem.common.ui.userwallet.UserWalletItem
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.block.TangemBlockCardColors
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.welcome.impl.R
import com.tangem.features.welcome.impl.ui.state.WalletUM
import com.tangem.features.welcome.impl.ui.state.WelcomeUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -45,7 +41,7 @@ internal fun AnimatedContentScope.WelcomeSelectWallet(state: WelcomeUM.SelectWal
TitleText()
SpacerH12()
var actualWallets by remember { mutableStateOf<ImmutableList<WalletUM>>(persistentListOf()) }
var actualWallets by remember { mutableStateOf<ImmutableList<UserWalletItemUM>>(persistentListOf()) }
Box(modifier = Modifier.weight(1f)) {
LazyColumn(
@ -62,9 +58,12 @@ internal fun AnimatedContentScope.WelcomeSelectWallet(state: WelcomeUM.SelectWal
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
itemsIndexed(actualWallets) { index, walletState ->
WalletItem(
UserWalletItem(
modifier = Modifier.fillMaxWidth(),
state = walletState,
modifier = Modifier,
blockColors = TangemBlockCardColors.copy(
containerColor = TangemTheme.colors.field.primary,
),
)
}
}
@ -165,66 +164,4 @@ private fun AnimatedContentScope.TitleText(modifier: Modifier = Modifier) {
color = TangemTheme.colors.text.secondary,
)
}
}
@Suppress("MagicNumber")
@Composable
private fun WalletItem(state: WalletUM, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.secondary, TangemTheme.shapes.roundedCornersXMedium)
.clickable(onClick = state.onClick)
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
WalletImage(state.imageState)
SpacerW12()
Column(Modifier.weight(1f)) {
Text(
text = state.name.resolveReference(),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
Text(
text = state.subtitle.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
}
}
@Composable
private fun WalletImage(state: WalletUM.ImageState, modifier: Modifier = Modifier) {
when (state) {
WalletUM.ImageState.MobileWallet -> {
Box(
modifier = modifier
.size(36.dp)
.background(TangemTheme.colors.icon.accent.copy(alpha = 0.1f), CircleShape),
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_wallet_filled_24),
tint = TangemTheme.colors.icon.accent,
contentDescription = null,
)
}
}
else -> {
CardImage(
imageState = when (state) {
is WalletUM.ImageState.Image -> UserWalletItemUM.ImageState.Image(state.artwork)
WalletUM.ImageState.Loading -> UserWalletItemUM.ImageState.Loading
else -> error("")
},
modifier = modifier,
)
}
}
}

View file

@ -1,22 +0,0 @@
package com.tangem.features.welcome.impl.ui.state
import com.tangem.core.ui.components.artwork.ArtworkUM
import com.tangem.core.ui.extensions.TextReference
import javax.annotation.concurrent.Immutable
internal data class WalletUM(
val name: TextReference,
val subtitle: TextReference,
val imageState: ImageState,
val onClick: () -> Unit,
) {
@Immutable
sealed class ImageState {
data object MobileWallet : ImageState()
data object Loading : ImageState()
data class Image(
val artwork: ArtworkUM,
) : ImageState()
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.features.welcome.impl.ui.state
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -11,17 +12,10 @@ internal sealed class WelcomeUM {
data object Plain : WelcomeUM()
data class SelectWallet(
val wallets: ImmutableList<WalletUM> = persistentListOf(),
val wallets: ImmutableList<UserWalletItemUM> = persistentListOf(),
val showUnlockWithBiometricButton: Boolean = false,
val addWalletBottomSheet: TangemBottomSheetConfig = TangemBottomSheetConfig.Empty,
val onUnlockWithBiometricClick: () -> Unit = {},
val addWalletClick: () -> Unit = {},
) : WelcomeUM()
data class EnterAccessCode(
val value: String = "",
val onUnlockWithBiometricClick: () -> Unit = {},
val onValueChange: (String) -> Unit = {},
val onBackClick: () -> Unit = {},
) : WelcomeUM()
}