Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-21 12:43:59 +03:00
commit fe2cfac54d
1140 changed files with 23493 additions and 8579 deletions

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,8 @@ dependencies {
/** Domain */
implementation(projects.domain.appCurrency)
implementation(projects.domain.wallets)
implementation(projects.domain.card)
implementation(projects.domain.settings)
/** DI */
implementation(deps.hilt.android)
@ -54,4 +58,5 @@ dependencies {
implementation(deps.timber)
implementation(tangemDeps.card.core)
implementation(tangemDeps.blockchain)
implementation(tangemDeps.hot.core)
}

View file

@ -15,10 +15,10 @@ import dagger.assisted.AssistedInject
internal class DefaultWelcomeComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted params: WelcomeComponent.Params,
@Assisted val params: Unit,
) : WelcomeComponent, AppComponentContext by context {
private val model: WelcomeModel = getOrCreateModel(params)
private val model: WelcomeModel = getOrCreateModel()
@Composable
override fun Content(modifier: Modifier) {
@ -32,6 +32,6 @@ internal class DefaultWelcomeComponent @AssistedInject constructor(
@AssistedFactory
interface Factory : WelcomeComponent.Factory {
override fun create(context: AppComponentContext, params: WelcomeComponent.Params): DefaultWelcomeComponent
override fun create(context: AppComponentContext, params: Unit): DefaultWelcomeComponent
}
}

View file

@ -1,18 +1,245 @@
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.core.wallets.UserWalletsListRepository
import com.tangem.domain.core.wallets.error.UnlockWalletError
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.settings.CanUseBiometryUseCase
import com.tangem.domain.wallets.repository.WalletsRepository
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 kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
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.*
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@Suppress("LongParameterList")
@ModelScoped
internal class WelcomeModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val uiMessageSender: UiMessageSender,
private val userWalletsListRepository: UserWalletsListRepository,
private val canUseBiometryUseCase: CanUseBiometryUseCase,
private val walletsRepository: WalletsRepository,
userWalletsFetcherFactory: UserWalletsFetcher.Factory,
) : Model() {
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())
private var routedOut = false
init {
modelScope.launch {
userWalletsListRepository.load()
wallets.value = walletsFetcher.userWallets.first()
launch {
walletsFetcher.userWallets
.collectLatest {
if (it.isEmpty()) {
router.replaceAll(AppRoute.Home())
}
wallets.value = it
}
}
tryToUnlockRightAway()
}
}
private fun tryToUnlockRightAway() {
modelScope.launch {
if (canUnlockWithBiometrics()) {
userWalletsListRepository.unlockAllWallets()
.onRight {
routedOut = true
router.replaceAll(AppRoute.Wallet)
}
.onLeft {
it.handle(null, onUserCancelled = { tryToUnlockWithAccessCodeRightAway() })
setSelectWalletState()
}
} else {
tryToUnlockWithAccessCodeRightAway()
setSelectWalletState()
}
}
}
private suspend fun tryToUnlockWithAccessCodeRightAway() {
if (onlyOneHotWalletWithAccessCode()) {
val userWallets = userWalletsListRepository.userWalletsSync()
val userWallet = userWallets.first()
uiState.value = WelcomeUM.Empty
unlockWallet(userWallet.walletId, UserWalletsListRepository.UnlockMethod.AccessCode)
}
}
private fun setSelectWalletState() {
modelScope.launch {
if (routedOut || uiState.value is WelcomeUM.SelectWallet) return@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 -> Unit // TODO
}
}
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 -> {
uiState.value = WelcomeUM.Empty
UserWalletsListRepository.UnlockMethod.AccessCode
}
}
unlockWallet(userWallet.walletId, unlockMethod)
setSelectWalletState()
}
private suspend fun canUnlockWithBiometrics(): Boolean {
return canUseBiometryUseCase() && walletsRepository.useBiometricAuthentication()
}
suspend fun unlockWallet(userWalletId: UserWalletId, unlockMethod: UserWalletsListRepository.UnlockMethod) {
userWalletsListRepository.unlock(userWalletId, unlockMethod)
.onRight {
routedOut = true
userWalletsListRepository.select(userWalletId)
router.replaceAll(AppRoute.Wallet)
}
.onLeft { error ->
error.handle(specificWalletId = userWalletId, onUserCancelled = { /* ignore*/ })
}
}
suspend fun UnlockWalletError.handle(specificWalletId: UserWalletId?, onUserCancelled: suspend () -> 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

@ -12,16 +12,17 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.inputrow.InputRowDefault
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.welcome.impl.R
import com.tangem.features.welcome.impl.ui.state.AddWalletBottomSheetContentUM
@Composable
fun AddWalletBottomSheet(config: TangemBottomSheetConfig) {
TangemBottomSheet<AddWalletBottomSheetContentUM>(
config = config,
titleText = TextReference.Str("Add Wallet"),
titleText = resourceReference(R.string.auth_info_add_wallet_title),
containerColor = TangemTheme.colors.background.tertiary,
content = { Content(it) },
)
@ -38,7 +39,7 @@ private fun Content(content: AddWalletBottomSheetContentUM) {
),
) {
InputRowDefault(
text = TextReference.Str("Create New Wallet"),
text = resourceReference(R.string.home_button_create_new_wallet),
modifier = Modifier
.roundedShapeItemDecoration(
currentIndex = 0,
@ -49,7 +50,7 @@ private fun Content(content: AddWalletBottomSheetContentUM) {
.clickable { content.onOptionClick(AddWalletBottomSheetContentUM.Option.Create) },
)
InputRowDefault(
text = TextReference.Str("Add Existing Wallet"),
text = resourceReference(R.string.home_button_add_existing_wallet),
modifier = Modifier
.roundedShapeItemDecoration(
currentIndex = 1,
@ -60,7 +61,7 @@ private fun Content(content: AddWalletBottomSheetContentUM) {
.clickable { content.onOptionClick(AddWalletBottomSheetContentUM.Option.Add) },
)
InputRowDefault(
text = TextReference.Str("Buy Tangem Wallet"),
text = resourceReference(R.string.details_buy_wallet),
modifier = Modifier
.roundedShapeItemDecoration(
currentIndex = 2,

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,7 @@ internal fun Welcome(state: WelcomeUM, modifier: Modifier = Modifier) {
state = st,
modifier = modifier,
)
is WelcomeUM.EnterAccessCode -> WelcomeEnterAccessCode(
state = st,
modifier = modifier,
)
WelcomeUM.Empty -> {}
}
}
}
@ -49,22 +47,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 +80,8 @@ 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.Empty
WelcomeUM.Empty -> 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,14 @@ 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.extensions.stringResourceSafe
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 +42,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,25 +59,33 @@ 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,
),
)
}
}
BottomFade(modifier = Modifier.align(Alignment.BottomCenter))
SecondaryButton(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.padding(16.dp)
.navigationBarsPadding()
.animateEnterExit(fadeIn(), fadeOut()),
text = "Unlock all with biometric",
onClick = state.onUnlockWithBiometricClick,
)
if (state.showUnlockWithBiometricButton) {
SecondaryButton(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.padding(16.dp)
.navigationBarsPadding()
.animateEnterExit(fadeIn(), fadeOut()),
text = stringResourceSafe(
R.string.user_wallet_list_unlock_all_with,
stringResourceSafe(id = R.string.common_biometrics),
),
onClick = state.onUnlockWithBiometricClick,
)
}
}
LaunchedEffect(state.wallets) {
@ -121,7 +126,7 @@ private fun AnimatedContentScope.TopBar(state: WelcomeUM.SelectWallet, modifier:
TextButton(
modifier = Modifier.clip(TangemTheme.shapes.roundedCornersLarge),
text = "Add Wallet",
text = stringResourceSafe(R.string.auth_info_add_wallet_title),
colors = TangemButtonsDefaults.defaultTextButtonColors.copy(
contentColor = TangemTheme.colors.text.primary1,
),
@ -145,7 +150,7 @@ private fun AnimatedContentScope.TitleText(modifier: Modifier = Modifier) {
) + fadeIn(tween(delayMillis = 300)),
exit = fadeOut(),
),
text = "Welcome back!",
text = stringResourceSafe(R.string.auth_info_title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
)
@ -160,71 +165,9 @@ private fun AnimatedContentScope.TitleText(modifier: Modifier = Modifier) {
) + fadeIn(tween(delayMillis = 300)),
exit = fadeOut(),
),
text = "Select a wallet to log in",
text = stringResourceSafe(R.string.auth_info_subtitle),
style = TangemTheme.typography.body1,
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
@ -8,20 +9,15 @@ import kotlinx.collections.immutable.persistentListOf
@Immutable
internal sealed class WelcomeUM {
data object Empty : 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()
}