Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-01 14:34:13 +05:00
parent 4c12570008
commit 97f41bd800
38 changed files with 768 additions and 163 deletions

View file

@ -38,6 +38,8 @@ import com.tangem.domain.settings.*
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.tokensync.usecase.StartTokenSyncUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.domain.wallets.usecase.*
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyUpdateUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
@ -123,6 +125,8 @@ internal class WalletModel @Inject constructor(
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val uiMessageSender: UiMessageSender,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val startTokenSyncUseCase: StartTokenSyncUseCase,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
) : Model() {
@ -155,6 +159,7 @@ internal class WalletModel @Inject constructor(
subscribeTangemPayOnWalletState()
subscribeToMainScreenQrScanning()
enableNotificationsIfNeeded()
applyPendingTokenSyncs()
clickIntents.initialize(innerWalletRouter, modelScope)
@ -819,6 +824,12 @@ internal class WalletModel @Inject constructor(
}
}
private fun applyPendingTokenSyncs() {
if (hotWalletFeatureToggles.isTokenSyncEnabled) {
startTokenSyncUseCase.applyPendingSyncs()
}
}
private fun enableNotificationsIfNeeded() {
modelScope.launch {
val isUserAllowToEnableNotifications = notificationsRepository.isUserAllowToSubscribeOnPushNotifications()

View file

@ -19,6 +19,7 @@ import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.hotwallet.CloseHotWalletUpgradeBannerUseCase
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
@ -39,6 +40,7 @@ import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.Program
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.PromotionBannerClicked
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.tokensync.usecase.AcknowledgeTokenSyncCompletionUseCase
import com.tangem.domain.wallets.usecase.*
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic
@ -94,6 +96,10 @@ internal interface WalletWarningsClickIntents {
fun onUpgradeHotWalletClick(userWalletId: UserWalletId)
fun onCloseUpgradeBannerClick(userWalletId: UserWalletId)
fun onDismissTokenSyncNotification(userWalletId: UserWalletId)
fun onTokenSyncManageClick(userWalletId: UserWalletId)
}
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
@ -126,6 +132,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val uiMessageSender: UiMessageSender,
private val reviewManager: ReviewManager,
private val closeHotWalletUpgradeBannerUseCase: CloseHotWalletUpgradeBannerUseCase,
private val acknowledgeTokenSyncCompletionUseCase: AcknowledgeTokenSyncCompletionUseCase,
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
override fun onAddBackupCardClick() {
@ -501,6 +508,17 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
}
override fun onDismissTokenSyncNotification(userWalletId: UserWalletId) {
acknowledgeTokenSyncCompletionUseCase(userWalletId)
}
override fun onTokenSyncManageClick(userWalletId: UserWalletId) {
acknowledgeTokenSyncCompletionUseCase(userWalletId)
router.openManageTokensScreen(
AccountId.forMainCryptoPortfolio(userWalletId),
)
}
private companion object {
const val VISA_PROMO_LINK = "https://tangem.com/en/cardwaitlist/?utm_source=tangem-app-banner" +
"&utm_medium=banner" +

View file

@ -20,7 +20,9 @@ internal object WalletPreviewDataLegacy {
balance = "8923,05312312312312312312331231231233432423423424234 $",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("3 cards • Seed phrase3 cards • Seed phrasephrasephrasephrase"),
content = WalletAdditionalInfo.Content.Text(
TextReference.Str("3 cards • Seed phrase3 cards • Seed phrasephrasephrasephrase"),
),
),
imageResId = R.drawable.ill_wallet2_cards3_120_106,
dropDownItems = persistentListOf(),

View file

@ -160,7 +160,7 @@ internal object WalletScreenPreviewDataLegacy {
title = "Note",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("Locked"),
content = WalletAdditionalInfo.Content.Text(TextReference.Str("Locked")),
),
imageResId = R.drawable.ill_note_btc_120_106,
dropDownItems = persistentListOf(),
@ -172,7 +172,7 @@ internal object WalletScreenPreviewDataLegacy {
title = "Wallet 1",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("Seed phrase"),
content = WalletAdditionalInfo.Content.Text(TextReference.Str("Seed phrase")),
),
imageResId = R.drawable.ill_wallet2_cards3_120_106,
cardCount = 3,

View file

@ -27,6 +27,9 @@ import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
import com.tangem.domain.tokensync.model.TokenSyncProgress
import com.tangem.domain.tokensync.usecase.ObserveTokenSyncUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
@ -43,6 +46,7 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import javax.inject.Inject
@ -61,6 +65,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private val shouldShowUpgradeHotWalletBannerUseCase: ShouldShowUpgradeHotWalletBannerUseCase,
private val getUpgradeBannerClosureTimestampUseCase: GetUpgradeBannerClosureTimestampUseCase,
private val checkHotWalletUpgradeBannerUseCase: CheckHotWalletUpgradeBannerUseCase,
private val observeTokenSyncUseCase: ObserveTokenSyncUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) {
@Suppress("UNCHECKED_CAST", "MagicNumber", "LongMethod", "CastNullableToNonNullableType")
@ -69,6 +75,12 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
val accountStatusListFlow = accountDependencies.singleAccountStatusListSupplier(params)
val tokenSyncProgressFlow = if (hotWalletFeatureToggles.isTokenSyncEnabled && userWallet is UserWallet.Hot) {
observeTokenSyncUseCase(userWallet.walletId).distinctUntilChanged()
} else {
flowOf(TokenSyncProgress.Idle)
}
return combine(
accountStatusListFlow,
isReadyToShowRateAppUseCase().distinctUntilChanged(),
@ -84,6 +96,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
.distinctUntilChanged(),
getUpgradeBannerClosureTimestampUseCase(userWallet.walletId)
.distinctUntilChanged(),
tokenSyncProgressFlow,
) { array -> array }
.map { array ->
val accountStatusList = array[0] as AccountStatusList
@ -95,6 +108,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val shouldShowYieldPromo = array[6] as Boolean
val shouldShowUpgradeBanner = array[7] as Boolean
val closureTimestamp = array[8] as? Long
val tokenSyncProgress = array[9] as TokenSyncProgress
val flattenCurrencies = accountStatusList.flattenCurrencies()
val paymentAccountStatus = accountStatusList.accountStatuses
@ -139,6 +153,12 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
clickIntents = clickIntents,
)
addTokenSyncCompletedNotification(
userWallet = userWallet,
tokenSyncProgress = tokenSyncProgress,
clickIntents = clickIntents,
)
addPushReminderNotification(
clickIntents = clickIntents,
shouldShowPushReminderBanner = shouldShowEnablePushesReminderNotification &&
@ -384,6 +404,20 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
// }
// }
private fun MutableList<WalletNotification>.addTokenSyncCompletedNotification(
userWallet: UserWallet,
tokenSyncProgress: TokenSyncProgress,
clickIntents: WalletClickIntents,
) {
addIf(
element = WalletNotification.TokenSyncCompleted(
onCloseClick = { clickIntents.onDismissTokenSyncNotification(userWallet.walletId) },
onManageTokensClick = { clickIntents.onTokenSyncManageClick(userWallet.walletId) },
),
condition = tokenSyncProgress is TokenSyncProgress.Completed,
)
}
private fun MutableList<WalletNotification>.addRateTheAppNotification(
isReadyToShowRating: Boolean,
clickIntents: WalletClickIntents,

View file

@ -10,6 +10,7 @@ import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.card.common.util.getCardsCount
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
import java.math.BigDecimal
@ -28,7 +29,11 @@ internal object WalletAdditionalInfoFactory {
* @param wallet current wallet
* @param currencyAmount amount of currency
*/
fun resolve(wallet: UserWallet, currencyAmount: BigDecimal? = null): WalletAdditionalInfo {
fun resolve(
wallet: UserWallet,
currencyAmount: BigDecimal? = null,
syncProgress: TokenSyncProgressUM = TokenSyncProgressUM.Idle,
): WalletAdditionalInfo {
return when (wallet) {
is UserWallet.Cold -> {
if (wallet.isMultiCurrency) {
@ -37,19 +42,26 @@ internal object WalletAdditionalInfoFactory {
wallet.resolveSingleCurrencyInfo(currencyAmount)
}
}
is UserWallet.Hot -> wallet.resolveAdditionalInfo()
is UserWallet.Hot -> wallet.resolveAdditionalInfo(syncProgress)
}
}
private fun UserWallet.Hot.resolveAdditionalInfo(): WalletAdditionalInfo {
private fun UserWallet.Hot.resolveAdditionalInfo(syncProgress: TokenSyncProgressUM): WalletAdditionalInfo {
val content = if (syncProgress is TokenSyncProgressUM.InProgress) {
WalletAdditionalInfo.Content.SyncProgress(syncProgress.progressPercent)
} else {
WalletAdditionalInfo.Content.Text(
TextReference.Res(R.string.hw_mobile_wallet) +
when {
isLocked -> DIVIDER + TextReference.Res(R.string.common_locked)
backedUp.not() -> DIVIDER + TextReference.Res(R.string.hw_backup_no_backup)
else -> TextReference.Str("")
},
)
}
return WalletAdditionalInfo(
hideable = false,
content = TextReference.Res(R.string.hw_mobile_wallet) +
when {
isLocked -> DIVIDER + TextReference.Res(R.string.common_locked)
backedUp.not() -> DIVIDER + TextReference.Res(R.string.hw_backup_no_backup)
else -> TextReference.Str("")
},
content = content,
isHotBackedUp = backedUp,
)
}
@ -58,9 +70,11 @@ internal object WalletAdditionalInfoFactory {
return if (isLocked) {
WalletAdditionalInfo(
hideable = false,
content = getBackupInfoWithDivider(
backupCardsCount = getCardsCount(),
) + TextReference.Res(R.string.common_locked),
content = WalletAdditionalInfo.Content.Text(
getBackupInfoWithDivider(
backupCardsCount = getCardsCount(),
) + TextReference.Res(R.string.common_locked),
),
)
} else {
val cardTypeResolver = scanResponse.cardTypesResolver
@ -76,8 +90,10 @@ internal object WalletAdditionalInfoFactory {
return if (isImported) {
WalletAdditionalInfo(
hideable = false,
content = getBackupInfoWithDivider(backupCardsCount = getCardsCount()) + TextReference.Res(
id = R.string.common_seed_phrase,
content = WalletAdditionalInfo.Content.Text(
getBackupInfoWithDivider(backupCardsCount = getCardsCount()) + TextReference.Res(
id = R.string.common_seed_phrase,
),
),
)
} else {
@ -94,7 +110,7 @@ internal object WalletAdditionalInfoFactory {
}
private fun getBackupInfo(backupCardsCount: Int?): WalletAdditionalInfo {
val content = if (backupCardsCount != null) {
val ref = if (backupCardsCount != null) {
getBackupInfoTextReference(count = backupCardsCount)
} else {
TextReference.EMPTY
@ -102,7 +118,7 @@ internal object WalletAdditionalInfoFactory {
return WalletAdditionalInfo(
hideable = false,
content = content,
content = WalletAdditionalInfo.Content.Text(ref),
)
}
@ -118,7 +134,7 @@ internal object WalletAdditionalInfoFactory {
return if (isLocked) {
WalletAdditionalInfo(
hideable = false,
content = TextReference.Res(R.string.common_locked),
content = WalletAdditionalInfo.Content.Text(TextReference.Res(R.string.common_locked)),
)
} else {
val blockchain = scanResponse.cardTypesResolver.getBlockchain()
@ -126,7 +142,7 @@ internal object WalletAdditionalInfoFactory {
WalletAdditionalInfo(
hideable = true,
content = TextReference.Str(value = amount.orEmpty()),
content = WalletAdditionalInfo.Content.Text(TextReference.Str(value = amount.orEmpty())),
)
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -17,10 +18,12 @@ internal class MultiWalletContentLoader @AssistedInject constructor(
private val walletNotificationsSubscriberFactory: WalletNotificationsSubscriber.Factory,
private val multiWalletActionButtonsSubscriberFactory: MultiWalletActionButtonsSubscriber.Factory,
private val tangemPayMainSubscriberFactory: TangemPayMainSubscriber.Factory,
private val tokenSyncSubscriberFactory: TokenSyncSubscriber.Factory,
private val designFeatureToggles: DesignFeatureToggles,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> = listOf(
override fun create(): List<WalletSubscriber> = listOfNotNull(
accountListSubscriberFactory.create(userWallet),
walletNFTListSubscriberFactory.create(userWallet),
checkWalletWithFundsSubscriberFactory.create(userWallet),
@ -31,6 +34,11 @@ internal class MultiWalletContentLoader @AssistedInject constructor(
},
multiWalletActionButtonsSubscriberFactory.create(userWallet),
tangemPayMainSubscriberFactory.create(userWallet),
if (hotWalletFeatureToggles.isTokenSyncEnabled && userWallet is UserWallet.Hot) {
tokenSyncSubscriberFactory.create(userWallet)
} else {
null
},
)
@AssistedFactory

View file

@ -0,0 +1,13 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.compose.runtime.Immutable
@Immutable
internal sealed class TokenSyncProgressUM {
data object Idle : TokenSyncProgressUM()
data class InProgress(val progressPercent: Int) : TokenSyncProgressUM()
data object Completed : TokenSyncProgressUM()
}

View file

@ -6,7 +6,12 @@ import com.tangem.core.ui.extensions.TextReference
@Immutable
data class WalletAdditionalInfo(
val hideable: Boolean,
val content: TextReference,
val content: Content,
val isHotBackedUp: Boolean = false,
val shouldShowProgress: Boolean = false,
)
) {
@Immutable
sealed interface Content {
data class Text(val text: TextReference) : Content
data class SyncProgress(val progressPercent: Int) : Content
}
}

View file

@ -85,7 +85,10 @@ internal sealed interface WalletCardState {
private companion object {
val defaultAdditionalInfo: WalletAdditionalInfo
get() = WalletAdditionalInfo(hideable = true, content = EMPTY_BALANCE_TEXT)
get() = WalletAdditionalInfo(
hideable = true,
content = WalletAdditionalInfo.Content.Text(EMPTY_BALANCE_TEXT),
)
}
}

View file

@ -27,6 +27,7 @@ internal sealed interface WalletState : WalletStateHolder {
abstract val tangemPayState: TangemPayState
abstract val tangemPayMainUM: TangemPayMainUM
abstract val isTangemPayRefactorEnabled: Boolean // TANGEM_PAY_ACCOUNTS_REFACTOR_ENABLED
abstract val tokenSyncProgressUM: TokenSyncProgressUM
data class Content(
override val pullToRefreshConfig: PullToRefreshConfig,
@ -40,6 +41,7 @@ internal sealed interface WalletState : WalletStateHolder {
override val tangemPayState: TangemPayState,
override val tangemPayMainUM: TangemPayMainUM,
override val isTangemPayRefactorEnabled: Boolean,
override val tokenSyncProgressUM: TokenSyncProgressUM = TokenSyncProgressUM.Idle,
) : MultiCurrency()
data class Locked(
@ -61,6 +63,7 @@ internal sealed interface WalletState : WalletStateHolder {
override val tangemPayState: TangemPayState = TangemPayState.Empty
override val tangemPayMainUM: TangemPayMainUM = TangemPayMainUM.Empty
override val isTangemPayRefactorEnabled: Boolean = false
override val tokenSyncProgressUM: TokenSyncProgressUM = TokenSyncProgressUM.Idle
}
}

View file

@ -61,7 +61,7 @@ internal class SetTokenListErrorTransformer(
walletsBalanceUM = walletUM.walletsBalanceUM.toLoadedState(),
tokensListUM = WalletTokensListUM.Empty(
onEmptyClick = {
clickIntents.onManageTokensClick(walletUM.walletsBalanceUM.id)
clickIntents.onTokenSyncManageClick(walletUM.walletsBalanceUM.id)
},
),
buttons = walletUM.disableButtons(),

View file

@ -122,7 +122,7 @@ internal class SetTokenListTransformer(
if (params !is TokenConverterParams.Account) {
return WalletTokensListUM.Empty(
onEmptyClick = {
clickIntents.onManageTokensClick(userWallet.walletId)
clickIntents.onTokenSyncManageClick(userWallet.walletId)
},
)
}

View file

@ -1,51 +1,34 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
internal class SetTokenSyncProgressTransformer(
userWalletId: UserWalletId,
private val progressPercent: Int,
) : WalletStateTransformer(userWalletId) {
private val userWallet: UserWallet,
private val progress: TokenSyncProgressUM,
) : WalletStateTransformer(userWallet.walletId) {
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.MultiCurrency.Content -> {
val updatedCardState = updateCardState(prevState.walletCardState)
prevState.copy(walletCardState = updatedCardState)
}
else -> {
prevState
}
is WalletState.MultiCurrency.Content -> prevState.copy(
walletCardState = updateCardState(prevState.walletCardState),
tokenSyncProgressUM = progress,
)
else -> prevState
}
}
override fun transform(walletUM: WalletUM): WalletUM {
return walletUM
}
override fun transform(walletUM: WalletUM): WalletUM = walletUM
private fun updateCardState(cardState: WalletCardState): WalletCardState {
val additionalInfo = WalletAdditionalInfo(
hideable = false,
content = resourceReference(
id = R.string.initial_wallet_sync_restore_progress,
formatArgs = wrappedList(progressPercent),
),
shouldShowProgress = true,
)
val additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = userWallet, syncProgress = progress)
return when (cardState) {
is WalletCardState.Loading -> {
cardState.copy(additionalInfo = additionalInfo)
}
is WalletCardState.Content -> {
cardState.copy(additionalInfo = additionalInfo)
}
is WalletCardState.Loading -> cardState.copy(additionalInfo = additionalInfo)
is WalletCardState.Content -> cardState.copy(additionalInfo = additionalInfo)
else -> cardState
}
}

View file

@ -4,6 +4,7 @@ import com.tangem.domain.card.common.util.getCardsCount
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
@ -17,7 +18,9 @@ internal class UpdateWalletCardsCountTransformer(
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.MultiCurrency.Content -> {
prevState.copy(walletCardState = prevState.walletCardState.toUpdatedState())
prevState.copy(
walletCardState = prevState.walletCardState.toUpdatedState(prevState.tokenSyncProgressUM),
)
}
is WalletState.SingleCurrency.Content -> {
prevState.copy(walletCardState = prevState.walletCardState.toUpdatedState())
@ -35,10 +38,12 @@ internal class UpdateWalletCardsCountTransformer(
return walletUM // todo redesign main
}
private fun WalletCardState.toUpdatedState(): WalletCardState {
private fun WalletCardState.toUpdatedState(
syncProgress: TokenSyncProgressUM = TokenSyncProgressUM.Idle,
): WalletCardState {
return when (this) {
is WalletCardState.Content -> copy(
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = userWallet),
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = userWallet, syncProgress = syncProgress),
imageResId = walletImageResolver.resolve(userWallet = userWallet),
cardCount = (userWallet as? UserWallet.Cold)?.getCardsCount(),
)

View file

@ -0,0 +1,45 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.tokensync.model.TokenSyncProgress
import com.tangem.domain.tokensync.usecase.ObserveTokenSyncUseCase
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenSyncProgressTransformer
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.onEach
internal class TokenSyncSubscriber @AssistedInject constructor(
@Assisted private val userWallet: UserWallet,
private val stateController: WalletStateController,
private val observeTokenSyncUseCase: ObserveTokenSyncUseCase,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<*> {
return observeTokenSyncUseCase(userWallet.walletId)
.onEach { current -> handleProgress(userWallet, current) }
}
private fun handleProgress(userWallet: UserWallet, current: TokenSyncProgress) {
val progressUM = when (current) {
is TokenSyncProgress.InProgress -> TokenSyncProgressUM.InProgress(current.progressPercent)
is TokenSyncProgress.Completed -> TokenSyncProgressUM.Completed
is TokenSyncProgress.Idle -> TokenSyncProgressUM.Idle
}
stateController.update(
SetTokenSyncProgressTransformer(
userWallet = userWallet,
progress = progressUM,
),
)
}
@AssistedFactory
interface Factory {
fun create(userWallet: UserWallet): TokenSyncSubscriber
}
}

View file

@ -41,6 +41,9 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.feature.wallet.impl.R
import com.tangem.core.ui.res.TangemDimens
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@ -156,16 +159,10 @@ private fun CardContainer(state: WalletCardState, isBalanceHidden: Boolean, item
.padding(vertical = TangemTheme.dimens.spacing8),
)
val additionalText by remember(state.additionalInfo, isBalanceHidden) {
mutableStateOf(
state.additionalInfo?.content?.orMaskWithStars(
maskWithStars = state.additionalInfo?.hideable == true && isBalanceHidden,
),
)
}
AdditionalInfo(
text = additionalText,
showProgress = state.additionalInfo?.shouldShowProgress == true,
content = state.additionalInfo?.content,
hideable = state.additionalInfo?.hideable == true,
isBalanceHidden = isBalanceHidden,
modifier = Modifier.conditional(
state.imageResId == null,
) { fillMaxWidth() },
@ -300,28 +297,53 @@ private fun Modifier.nonContentBalanceSize(dimens: TangemDimens): Modifier {
}
@Composable
private fun AdditionalInfo(text: TextReference?, showProgress: Boolean, modifier: Modifier = Modifier) {
private fun AdditionalInfo(
content: WalletAdditionalInfo.Content?,
hideable: Boolean,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
AnimatedContent(
targetState = text,
targetState = content,
contentKey = { con ->
when (con) {
is WalletAdditionalInfo.Content.Text -> con
is WalletAdditionalInfo.Content.SyncProgress -> WalletAdditionalInfo.Content.SyncProgress::class
null -> null
}
},
label = "Update the additional text",
modifier = modifier,
transitionSpec = {
fadeIn(animationSpec = tween(durationMillis = 220, delayMillis = 90)) togetherWith
fadeOut(animationSpec = tween(durationMillis = 90))
},
) { animatedText ->
if (animatedText != null) {
) { animatedContent ->
if (animatedContent != null) {
Row(
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
AdditionalInfoText(text = animatedText)
if (showProgress) {
CircularProgressIndicator(
modifier = Modifier
.size(TangemTheme.dimens.size16),
color = TangemTheme.colors.icon.accent,
strokeWidth = TangemTheme.dimens.size2,
)
when (animatedContent) {
is WalletAdditionalInfo.Content.Text -> {
AdditionalInfoText(
text = animatedContent.text.orMaskWithStars(
maskWithStars = hideable && isBalanceHidden,
),
)
}
is WalletAdditionalInfo.Content.SyncProgress -> {
AdditionalInfoText(
text = resourceReference(
id = R.string.initial_wallet_sync_restore_progress,
formatArgs = wrappedList(animatedContent.progressPercent),
),
)
CircularProgressIndicator(
modifier = Modifier.size(TangemTheme.dimens.size16),
color = TangemTheme.colors.icon.accent,
strokeWidth = TangemTheme.dimens.size2,
)
}
}
}
} else {
@ -396,7 +418,7 @@ private class WalletCardStateProvider : CollectionPreviewParameterProvider<Walle
title = "Title",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("3 cards"),
content = WalletAdditionalInfo.Content.Text(TextReference.Str("3 cards")),
),
),
WalletPreviewDataLegacy.walletCardContentState.copy(