Updated on 2026-08-14
This commit is contained in:
parent
48b8c77f5d
commit
cb795ebf3d
27 changed files with 432 additions and 262 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 23aae9e3496d89a021ac9a0833b54b49635bb193
|
||||
Subproject commit 3ac868e93f88498258867d457f8b8c4577b40f98
|
||||
|
|
@ -77,7 +77,9 @@ internal class DefaultUserWalletsListRepository(
|
|||
|
||||
override suspend fun userWalletsSync(): List<UserWallet> {
|
||||
load()
|
||||
return userWallets.value!!
|
||||
return requireNotNull(userWallets.value) {
|
||||
"This should never happen"
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun selectedUserWalletSync(): UserWallet? {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.tangem.features.hotwallet.CreateMobileWalletComponent
|
|||
import com.tangem.features.hotwallet.WalletActivationComponent
|
||||
import com.tangem.features.hotwallet.WalletBackupComponent
|
||||
import com.tangem.features.hotwallet.UpdateAccessCodeComponent
|
||||
import com.tangem.features.hotwallet.HotWalletFeatureToggles
|
||||
import com.tangem.features.managetokens.component.ChooseManagedTokensComponent
|
||||
import com.tangem.features.managetokens.component.ManageTokensComponent
|
||||
import com.tangem.features.managetokens.component.ManageTokensSource
|
||||
|
|
@ -52,6 +53,7 @@ import com.tangem.tap.routing.component.RoutingComponent.Child
|
|||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import javax.inject.Inject
|
||||
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent as RedesignedWalletConnectComponent
|
||||
import com.tangem.features.welcome.WelcomeComponent as NewWelcomeComponent
|
||||
|
||||
@ActivityScoped
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
|
|
@ -70,6 +72,7 @@ internal class ChildFactory @Inject constructor(
|
|||
private val swapSelectTokensComponentFactory: SwapSelectTokensComponent.Factory,
|
||||
private val onboardingEntryComponentFactory: OnboardingEntryComponent.Factory,
|
||||
private val welcomeComponentFactory: WelcomeComponent.Factory,
|
||||
private val newWelcomeComponentFactory: NewWelcomeComponent.Factory,
|
||||
private val storiesComponentFactory: StoriesComponent.Factory,
|
||||
private val stakingComponentFactory: StakingComponent.Factory,
|
||||
private val swapComponentFactory: SwapComponent.Factory,
|
||||
|
|
@ -102,6 +105,7 @@ internal class ChildFactory @Inject constructor(
|
|||
private val sendWithSwapComponentFactory: SendWithSwapComponent.Factory,
|
||||
private val sendEntryPointComponentFactory: SendEntryPointComponent.Factory,
|
||||
private val walletConnectFeatureToggles: WalletConnectFeatureToggles,
|
||||
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
|
||||
) {
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
|
|
@ -138,14 +142,25 @@ internal class ChildFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
is AppRoute.Welcome -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = WelcomeComponent.Params(
|
||||
launchMode = route.launchMode,
|
||||
intent = route.intent,
|
||||
),
|
||||
componentFactory = welcomeComponentFactory,
|
||||
)
|
||||
if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = NewWelcomeComponent.Params(
|
||||
launchMode = route.launchMode,
|
||||
intent = route.intent,
|
||||
),
|
||||
componentFactory = newWelcomeComponentFactory,
|
||||
)
|
||||
} else {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = WelcomeComponent.Params(
|
||||
launchMode = route.launchMode,
|
||||
intent = route.intent,
|
||||
),
|
||||
componentFactory = welcomeComponentFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
is AppRoute.WalletSettings -> {
|
||||
createComponentChild(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ package com.tangem.common.ui.userwallet
|
|||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.CardColors
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -22,6 +24,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.common.ui.R
|
||||
|
|
@ -184,6 +187,19 @@ fun CardImage(imageState: UserWalletItemUM.ImageState, modifier: Modifier = Modi
|
|||
radius = TangemTheme.dimens.size2,
|
||||
)
|
||||
}
|
||||
is UserWalletItemUM.ImageState.MobileWallet -> {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.field.focused,
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
)
|
||||
.padding(6.dp),
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_mobile_wallet_icon_24),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
is UserWalletItemUM.ImageState.Image -> {
|
||||
val verifiedArtwork = imageState.artwork.verifiedArtwork
|
||||
if (verifiedArtwork != null) {
|
||||
|
|
@ -364,6 +380,18 @@ private class UserWalletItemUMPreviewProvider : PreviewParameterProvider<UserWal
|
|||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
UserWalletItemUM(
|
||||
id = UserWalletId("user_wallet_3".encodeToByteArray()),
|
||||
name = stringReference("Multi Card"),
|
||||
information = UserWalletItemUM.Information.Failed,
|
||||
balance = UserWalletItemUM.Balance.Loaded(
|
||||
value = "1.2345 BTC",
|
||||
isFlickering = false,
|
||||
),
|
||||
imageState = UserWalletItemUM.ImageState.MobileWallet,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
private fun getInformation(cardCount: Int): UserWalletItemUM.Information.Loaded {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ 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.components.label.entity.LabelStyle
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
|
|
@ -35,6 +35,7 @@ class UserWalletItemUMConverter(
|
|||
private val appCurrency: AppCurrency? = null,
|
||||
private val balance: TotalFiatBalance? = null,
|
||||
private val isBalanceHidden: Boolean = false,
|
||||
private val authMode: Boolean = false,
|
||||
private val endIcon: UserWalletItemUM.EndIcon = UserWalletItemUM.EndIcon.None,
|
||||
private val artwork: ArtworkModel? = null,
|
||||
) : Converter<UserWallet, UserWalletItemUM> {
|
||||
|
|
@ -48,24 +49,38 @@ class UserWalletItemUMConverter(
|
|||
name = stringReference(name),
|
||||
information = getInfo(userWallet = this),
|
||||
balance = getBalanceInfo(userWallet = this),
|
||||
isEnabled = !isLocked,
|
||||
isEnabled = isEnabled(userWallet = this),
|
||||
endIcon = endIcon,
|
||||
onClick = { onClick(value.walletId) },
|
||||
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
|
||||
},
|
||||
imageState = getImageState(userWallet = value),
|
||||
label = getLabelOrNull(userWallet = this),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isEnabled(userWallet: UserWallet): Boolean {
|
||||
return authMode || userWallet.isLocked.not()
|
||||
}
|
||||
|
||||
private fun getLabelOrNull(userWallet: UserWallet): LabelUM? {
|
||||
return if (authMode.not() && userWallet is UserWallet.Hot && !userWallet.backedUp) {
|
||||
LabelUM(
|
||||
text = resourceReference(R.string.hw_backup_no_backup),
|
||||
style = LabelStyle.WARNING,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getImageState(userWallet: UserWallet): UserWalletItemUM.ImageState {
|
||||
return when {
|
||||
userWallet is UserWallet.Hot -> UserWalletItemUM.ImageState.MobileWallet
|
||||
artwork != null -> UserWalletItemUM.ImageState.Image(artworkUMConverter.convert(artwork))
|
||||
else -> UserWalletItemUM.ImageState.Loading
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInfo(userWallet: UserWallet): UserWalletItemUM.Information.Loaded {
|
||||
val text = when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ data class UserWalletItemUM(
|
|||
|
||||
data object Loading : ImageState()
|
||||
|
||||
data object MobileWallet : ImageState()
|
||||
|
||||
data class Image(
|
||||
val artwork: ArtworkUM,
|
||||
) : ImageState()
|
||||
|
|
|
|||
19
core/ui/src/main/res/drawable/ic_mobile_wallet_icon_24.xml
Normal file
19
core/ui/src/main/res/drawable/ic_mobile_wallet_icon_24.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:pathData="M6.839,11.095C7.657,11.095 8.32,11.759 8.32,12.576V20.148C8.32,20.966 7.657,21.629 6.839,21.629H2.864C2.046,21.629 1.383,20.966 1.383,20.148V12.576C1.383,11.759 2.046,11.095 2.864,11.095H6.839ZM14.064,2.37C14.831,2.37 15.401,2.37 15.894,2.502C17.227,2.858 18.268,3.897 18.625,5.226C18.709,5.54 18.74,5.885 18.751,6.291C18.895,6.304 19.034,6.319 19.167,6.337C20.062,6.457 20.853,6.719 21.486,7.351C22.119,7.983 22.382,8.772 22.503,9.665C22.618,10.516 22.617,11.591 22.617,12.895V14.957C22.617,16.261 22.618,17.336 22.503,18.187C22.382,19.08 22.12,19.869 21.486,20.501C20.853,21.133 20.062,21.395 19.167,21.515C18.314,21.629 17.236,21.629 15.929,21.629H9.999C9.987,21.629 9.975,21.628 9.963,21.628C10.176,21.18 10.297,20.678 10.297,20.149V12.577C10.297,10.668 8.749,9.12 6.84,9.12H2.865C2.335,9.12 1.832,9.24 1.383,9.453V5.259C1.383,3.664 2.679,2.37 4.278,2.37H14.064ZM18.406,12.436C17.468,12.436 16.707,13.196 16.707,14.134C16.707,15.073 17.468,15.833 18.406,15.833C19.345,15.833 20.105,15.073 20.105,14.134C20.105,13.196 19.344,12.436 18.406,12.436ZM4.159,12.573C3.723,12.573 3.369,12.926 3.369,13.363C3.369,13.799 3.723,14.153 4.159,14.153H5.542C5.978,14.153 6.332,13.799 6.332,13.363C6.332,12.926 5.978,12.573 5.542,12.573H4.159ZM4.278,4.296C3.745,4.296 3.313,4.727 3.313,5.259C3.313,5.791 3.745,6.222 4.278,6.222H15.929C16.237,6.222 16.533,6.222 16.816,6.224C16.808,5.975 16.791,5.839 16.76,5.724C16.581,5.059 16.061,4.54 15.394,4.362C15.18,4.304 14.889,4.296 13.93,4.296H4.278Z">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:centerX="7.343" android:centerY="0.784" android:gradientRadius="33.786" android:type="radial">
|
||||
|
||||
<item android:color="#FF96999B" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF4D4E53" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
</vector>
|
||||
|
|
@ -7,6 +7,7 @@ import com.tangem.blockchainsdk.BlockchainSDKFactory
|
|||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.data.walletmanager.extensions.makePublicKey
|
||||
import com.tangem.data.walletmanager.extensions.makeWalletManagerForApp
|
||||
import com.tangem.domain.card.configs.Wallet2CardConfig
|
||||
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -41,7 +42,7 @@ internal class WalletManagerFactory(
|
|||
blockchain: Blockchain,
|
||||
derivationPath: DerivationPath?,
|
||||
): WalletManager? {
|
||||
val curve = blockchain.getSupportedCurves().first()
|
||||
val curve = Wallet2CardConfig.primaryCurve(blockchain)
|
||||
val selectedWallet = hotWallet.wallets.orEmpty().firstOrNull { it.curve == curve }
|
||||
?: return null
|
||||
return try {
|
||||
|
|
|
|||
|
|
@ -46,9 +46,8 @@ class HotWalletAccessor @Inject constructor(
|
|||
auth = auth,
|
||||
block = { blockAuth ->
|
||||
block(blockAuth).also {
|
||||
// TODO [REDACTED_TASK_KEY] [Hot Wallet] Authorization by access code
|
||||
// if user has biometry enabled, we set it as the new auth method
|
||||
if (blockAuth is HotAuth.Password /*&& has biometry enabled */) {
|
||||
// Update biometry auth if the original auth was password
|
||||
if (blockAuth is HotAuth.Password) {
|
||||
tangemHotSdk.changeAuth(
|
||||
unlockHotWallet = UnlockHotWallet(
|
||||
walletId = hotWalletId,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class HotUserWalletBuilder @AssistedInject constructor(
|
|||
) {
|
||||
|
||||
suspend fun build(): UserWallet.Hot = withContext(dispatcherProvider.default) {
|
||||
val allNetworks = Blockchain.entries // TODO [REDACTED_TASK_KEY] [Hot Wallet] Derivation config for hot wallet
|
||||
val allNetworks = Blockchain.entries.filter { it.isTestnet().not() }
|
||||
val curves = allNetworks.map { it.getSupportedCurves() }.flatten().toSet()
|
||||
val requests = curves.sortedBy { it.ordinal }.map { curve ->
|
||||
val derivationPaths = allNetworks.filter { curve in it.getSupportedCurves() }
|
||||
|
|
|
|||
|
|
@ -8,4 +8,8 @@ class GetIsBiometricsEnabledUseCase @Inject constructor(
|
|||
) {
|
||||
|
||||
operator fun invoke(): Boolean = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull() ?: false
|
||||
|
||||
fun canUseBiometry(): Boolean {
|
||||
return tangemSdkManager.canUseBiometry
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,11 @@ class SaveWalletUseCase(
|
|||
UserWalletsListRepository.LockMethod.NoLock,
|
||||
)
|
||||
}
|
||||
}.mapLeft { SaveWalletError.DataError(null) }.bind()
|
||||
}.mapLeft {
|
||||
SaveWalletError.DataError(null)
|
||||
}.map {
|
||||
userWalletsListRepository.select(userWallet.walletId)
|
||||
}.bind()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -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)) },
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ interface UserWalletsFetcher {
|
|||
fun create(
|
||||
messageSender: UiMessageSender,
|
||||
onlyMultiCurrency: Boolean,
|
||||
authMode: Boolean,
|
||||
onWalletClick: (UserWalletId) -> Unit,
|
||||
): UserWalletsFetcher
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ internal class WcUserWalletsFetcher(
|
|||
private val userWalletsFetcher = userWalletsFetcherFactory.create(
|
||||
messageSender = messageSender,
|
||||
onlyMultiCurrency = true,
|
||||
authMode = false,
|
||||
onWalletClick = { onWalletSelected(it) },
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
},
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -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,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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 794a8187e6d248ca3c21661df199a34ffeb0037a
|
||||
Subproject commit bc4cd430853ca794614b8d5163c9b28b9ca26112
|
||||
Loading…
Add table
Add a link
Reference in a new issue