Updated on 2026-08-14
This commit is contained in:
commit
fe2cfac54d
1140 changed files with 23493 additions and 8579 deletions
|
|
@ -1,14 +1,9 @@
|
|||
package com.tangem.features.welcome
|
||||
|
||||
import com.tangem.common.routing.entity.SerializableIntent
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
|
||||
interface WelcomeComponent : ComposableContentComponent {
|
||||
|
||||
data class Params(
|
||||
val intent: SerializableIntent?,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, WelcomeComponent>
|
||||
interface Factory : ComponentFactory<Unit, WelcomeComponent>
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
},
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue