Updated on 2026-08-14

This commit is contained in:
Tangem 2023-12-07 19:52:50 +04:00
parent a7a72506ee
commit 9979885b4e
45 changed files with 651 additions and 192 deletions

View file

@ -22,6 +22,8 @@ interface CardTypesResolver {
fun isWallet2(): Boolean
fun isVisaWallet(): Boolean
fun isRing(): Boolean
fun isTangemTwins(): Boolean

View file

@ -45,6 +45,8 @@ internal class TangemCardTypesResolver(
card.settings.isKeysImportAllowed
}
override fun isVisaWallet(): Boolean = card.firmwareVersion.doubleValue in FirmwareVersion.visaRange
override fun isRing(): Boolean {
return productType == ProductType.Ring
}

View file

@ -3,10 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.loaders.implementors.MultiWalletContentLoaderFactory
import com.tangem.feature.wallet.presentation.wallet.loaders.implementors.SingleWalletContentLoaderFactory
import com.tangem.feature.wallet.presentation.wallet.loaders.implementors.SingleWalletWithTokenContentLoaderFactory
import com.tangem.feature.wallet.presentation.wallet.loaders.implementors.WalletContentLoader
import com.tangem.feature.wallet.presentation.wallet.loaders.implementors.*
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2
import dagger.hilt.android.scopes.ViewModelScoped
import javax.inject.Inject
@ -16,6 +13,7 @@ internal class WalletContentLoaderFactory @Inject constructor(
private val multiWalletContentLoaderFactory: MultiWalletContentLoaderFactory,
private val singleWalletWithTokenContentLoaderFactory: SingleWalletWithTokenContentLoaderFactory,
private val singleWalletContentLoaderFactory: SingleWalletContentLoaderFactory,
private val visaWalletContentLoaderFactory: VisaWalletContentLoaderFactory,
) {
fun create(
@ -31,6 +29,9 @@ internal class WalletContentLoaderFactory @Inject constructor(
userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() -> {
singleWalletWithTokenContentLoaderFactory.create(userWallet, appCurrency, clickIntents)
}
userWallet.scanResponse.cardTypesResolver.isVisaWallet() -> {
visaWalletContentLoaderFactory.create(userWallet, appCurrency, clickIntents, isRefresh)
}
!userWallet.isMultiCurrency -> {
singleWalletContentLoaderFactory.create(userWallet, appCurrency, clickIntents, isRefresh)
}

View file

@ -24,7 +24,7 @@ internal class MultiWalletContentLoader(
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber<*>> {
override fun create(): List<WalletSubscriber> {
return listOf(
TokenListSubscriber(
userWallet = userWallet,

View file

@ -29,7 +29,7 @@ internal class SingleWalletContentLoader(
private val analyticsEventHandler: AnalyticsEventHandler,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber<*>> {
override fun create(): List<WalletSubscriber> {
return listOf(
PrimaryCurrencySubscriber(
userWallet = userWallet,

View file

@ -24,7 +24,7 @@ internal class SingleWalletWithTokenContentLoader(
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber<*>> {
override fun create(): List<WalletSubscriber> {
return listOf(
SingleWalletWithTokenListSubscriber(
userWallet = userWallet,

View file

@ -0,0 +1,53 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateHolderV2
import com.tangem.feature.wallet.presentation.wallet.subscribers.PrimaryCurrencySubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.TxHistorySubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.VisaWalletBalancesAndLimitsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2
@Suppress("LongParameterList")
internal class VisaWalletContentLoader(
private val userWallet: UserWallet,
private val appCurrency: AppCurrency,
private val clickIntents: WalletClickIntentsV2,
private val isRefresh: Boolean,
private val stateHolder: WalletStateHolderV2,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase,
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> {
return listOf(
PrimaryCurrencySubscriber(
userWallet = userWallet,
appCurrency = appCurrency,
stateHolder = stateHolder,
getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase,
setWalletWithFundsFoundUseCase = setWalletWithFundsFoundUseCase,
analyticsEventHandler = analyticsEventHandler,
),
VisaWalletBalancesAndLimitsSubscriber(userWallet, stateHolder, clickIntents),
TxHistorySubscriber(
userWallet = userWallet,
isRefresh = isRefresh,
stateHolder = stateHolder,
clickIntents = clickIntents,
getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase,
txHistoryItemsCountUseCase = txHistoryItemsCountUseCase,
txHistoryItemsUseCase = txHistoryItemsUseCase,
),
)
}
}

View file

@ -0,0 +1,45 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateHolderV2
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2
import dagger.hilt.android.scopes.ViewModelScoped
import javax.inject.Inject
@ViewModelScoped
@Suppress("LongParameterList")
internal class VisaWalletContentLoaderFactory @Inject constructor(
private val stateHolder: WalletStateHolderV2,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase,
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
) {
fun create(
userWallet: UserWallet,
appCurrency: AppCurrency,
clickIntents: WalletClickIntentsV2,
isRefresh: Boolean,
): WalletContentLoader {
return VisaWalletContentLoader(
userWallet,
appCurrency,
clickIntents,
isRefresh,
stateHolder,
getPrimaryCurrencyStatusUpdatesUseCase,
setWalletWithFundsFoundUseCase,
txHistoryItemsCountUseCase,
txHistoryItemsUseCase,
analyticsEventHandler,
)
}
}

View file

@ -13,7 +13,7 @@ import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscribe
internal abstract class WalletContentLoader(val id: UserWalletId) {
/** Loader's subscribers */
val subscribers: List<WalletSubscriber<*>> get() = create()
val subscribers: List<WalletSubscriber> get() = create()
protected abstract fun create(): List<WalletSubscriber<*>>
protected abstract fun create(): List<WalletSubscriber>
}

View file

@ -139,6 +139,84 @@ internal sealed class WalletState {
)
}
}
internal sealed class Visa : WalletState() {
abstract val balancesAndLimitBlockState: BalancesAndLimitsBlockState?
abstract val txHistoryState: TxHistoryState
data class Content(
override val pullToRefreshConfig: WalletPullToRefreshConfig,
override val walletCardState: WalletCardState,
override val warnings: ImmutableList<WalletNotification>,
override val bottomSheetConfig: TangemBottomSheetConfig?,
override val balancesAndLimitBlockState: BalancesAndLimitsBlockState,
override val txHistoryState: TxHistoryState,
val depositButtonState: DepositButtonState,
) : Visa()
data class Locked(
override val walletCardState: WalletCardState,
val onUnlockNotificationClick: () -> Unit,
val isBottomSheetShow: Boolean = false,
val onBottomSheetDismiss: () -> Unit = {},
val onUnlockClick: () -> Unit,
val onScanClick: () -> Unit,
val onExploreClick: () -> Unit,
) : Visa() {
override val pullToRefreshConfig: WalletPullToRefreshConfig
get() = WalletPullToRefreshConfig(isRefreshing = false, onRefresh = {})
override val warnings: ImmutableList<WalletNotification> = persistentListOf(
WalletNotification.UnlockWallets(onUnlockNotificationClick),
)
override val bottomSheetConfig = TangemBottomSheetConfig(
isShow = isBottomSheetShow,
onDismissRequest = onBottomSheetDismiss,
content = WalletBottomSheetConfig.UnlockWallets(
onUnlockClick = onUnlockClick,
onScanClick = onScanClick,
),
)
override val balancesAndLimitBlockState: BalancesAndLimitsBlockState? = null
override val txHistoryState: TxHistoryState = TxHistoryState.Content(
contentItems = MutableStateFlow(
value = PagingData.from(
data = listOf(
TxHistoryState.TxHistoryItemState.Title(onExploreClick = onExploreClick),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Locked(txHash = "LOCKED_TX_HASH"),
),
),
),
),
)
}
sealed class BalancesAndLimitsBlockState {
object Loading : BalancesAndLimitsBlockState()
object Error : BalancesAndLimitsBlockState()
data class Content(
val availableBalance: String,
val currencySymbol: String,
val limitDays: Int,
val isEnabled: Boolean,
val onClick: () -> Unit,
) : BalancesAndLimitsBlockState()
}
data class DepositButtonState(
val isEnabled: Boolean,
val onClick: () -> Unit,
)
}
}
internal sealed class WalletTokensListState {

View file

@ -15,6 +15,10 @@ internal class CloseBottomSheetTransformer(userWalletId: UserWalletId) : WalletS
prevState.copy(bottomSheetConfig = prevState.bottomSheetConfig?.copy(isShow = false))
}
is WalletState.SingleCurrency.Locked -> prevState.copy(isBottomSheetShow = false)
is WalletState.Visa.Content -> prevState.copy(
bottomSheetConfig = prevState.bottomSheetConfig?.copy(isShow = false),
)
is WalletState.Visa.Locked -> prevState.copy(isBottomSheetShow = false)
}
}
}

View file

@ -72,6 +72,15 @@ internal class InitializeWalletsTransformer(
onExploreClick = clickIntents::onExploreClick,
)
},
visaWalletCreator = {
WalletState.Visa.Locked(
walletCardState = userWallet.toLockedWalletCardState(),
onUnlockNotificationClick = clickIntents::onOpenUnlockWalletsBottomSheetClick,
onUnlockClick = clickIntents::onUnlockWalletClick,
onScanClick = clickIntents::onScanToUnlockWalletClick,
onExploreClick = clickIntents::onExploreClick,
)
},
)
}

View file

@ -37,6 +37,17 @@ internal class OpenBottomSheetTransformer(
is WalletState.SingleCurrency.Locked -> {
prevState.copy(isBottomSheetShow = true, onBottomSheetDismiss = onDismissBottomSheet)
}
is WalletState.Visa.Content -> prevState.copy(
bottomSheetConfig = TangemBottomSheetConfig(
isShow = true,
onDismissRequest = onDismissBottomSheet,
content = content,
),
)
is WalletState.Visa.Locked -> prevState.copy(
isBottomSheetShow = true,
onBottomSheetDismiss = onDismissBottomSheet,
)
}
}
}

View file

@ -17,8 +17,12 @@ internal class RenameWalletTransformer(
is WalletState.SingleCurrency.Content -> {
prevState.copy(walletCardState = prevState.walletCardState.copySealed(title = newName))
}
is WalletState.Visa.Content -> {
prevState.copy(walletCardState = prevState.walletCardState.copySealed(title = newName))
}
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
-> {
Timber.e("Impossible to rename wallet in locked state")
prevState

View file

@ -0,0 +1,32 @@
package com.tangem.feature.wallet.presentation.wallet.state2.transformers
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState.Visa.BalancesAndLimitsBlockState
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2
internal class SetBalancesAndLimitsTransformer(
userWallet: UserWallet,
private val clickIntents: WalletClickIntentsV2,
) : WalletStateTransformer(userWallet.walletId) {
override fun transform(prevState: WalletState): WalletState {
return prevState.transformWhenInState<WalletState.Visa.Content> { state ->
state.copy(
balancesAndLimitBlockState = state.balancesAndLimitBlockState.toLoadedState(),
)
}
}
// TODO: Implement in [REDACTED_JIRA]
@Suppress("UnusedReceiverParameter")
private fun BalancesAndLimitsBlockState.toLoadedState(): BalancesAndLimitsBlockState {
return BalancesAndLimitsBlockState.Content(
availableBalance = "400.00",
currencySymbol = "USDT",
limitDays = 7,
isEnabled = true,
onClick = clickIntents::onBalancesAndLimitsClick,
)
}
}

View file

@ -21,14 +21,16 @@ internal class SetCryptoCurrencyActionsTransformer(
is WalletState.SingleCurrency.Content -> {
prevState.copy(buttons = tokenActionsState.toManageButtons())
}
is WalletState.SingleCurrency.Locked,
-> {
Timber.e("Impossible to load primary currency status for locked wallet")
is WalletState.SingleCurrency.Locked -> {
Timber.w("Impossible to load primary currency status for locked wallet")
prevState
}
is WalletState.MultiCurrency,
-> {
Timber.e("Impossible to load crypto currency actions for multi-currency wallet")
is WalletState.MultiCurrency -> {
Timber.w("Impossible to load crypto currency actions for multi-currency wallet")
prevState
}
is WalletState.Visa -> {
Timber.w("Impossible to load crypto currency actions for VISA wallet")
prevState
}
}

View file

@ -8,11 +8,12 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCard
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState
import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.SingleWalletCardStateConverter
import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.SingleWalletMarketPriceConverter
import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.VisaWalletCardStateConverter
import timber.log.Timber
internal class SetPrimaryCurrencyTransformer(
private val userWallet: UserWallet,
private val status: CryptoCurrencyStatus.Status,
private val status: CryptoCurrencyStatus,
private val appCurrency: AppCurrency,
) : WalletStateTransformer(userWallet.walletId) {
@ -20,28 +21,38 @@ internal class SetPrimaryCurrencyTransformer(
return when (prevState) {
is WalletState.SingleCurrency.Content -> {
prevState.copy(
walletCardState = prevState.walletCardState.toLoadedState(),
walletCardState = prevState.walletCardState.toLoadedSingleCurrencyState(),
marketPriceBlockState = prevState.marketPriceBlockState.toLoadedState(),
)
}
is WalletState.Visa.Content -> {
prevState.copy(
walletCardState = prevState.walletCardState.toLoadedVisaState(),
depositButtonState = prevState.depositButtonState.copy(isEnabled = true),
)
}
is WalletState.Visa.Locked,
is WalletState.SingleCurrency.Locked,
-> {
Timber.e("Impossible to load primary currency status for locked wallet")
Timber.w("Impossible to load primary currency status for locked wallet")
prevState
}
is WalletState.MultiCurrency,
-> {
Timber.e("Impossible to load primary currency status for multi-currency wallet")
is WalletState.MultiCurrency -> {
Timber.w("Impossible to load primary currency status for multi-currency wallet")
prevState
}
}
}
private fun WalletCardState.toLoadedState(): WalletCardState {
return SingleWalletCardStateConverter(status, userWallet, appCurrency).convert(value = this)
private fun WalletCardState.toLoadedSingleCurrencyState(): WalletCardState {
return SingleWalletCardStateConverter(status.value, userWallet, appCurrency).convert(value = this)
}
private fun WalletCardState.toLoadedVisaState(): WalletCardState {
return VisaWalletCardStateConverter(status, userWallet, appCurrency).convert(value = this)
}
private fun MarketPriceBlockState.toLoadedState(): MarketPriceBlockState {
return SingleWalletMarketPriceConverter(status, appCurrency).convert(value = this)
return SingleWalletMarketPriceConverter(status.value, appCurrency).convert(value = this)
}
}

View file

@ -4,6 +4,8 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState.Visa.BalancesAndLimitsBlockState
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState.Visa.DepositButtonState
import com.tangem.feature.wallet.presentation.wallet.state2.WalletTokensListState
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.mutate
@ -27,8 +29,16 @@ internal class SetRefreshStateTransformer(
buttons = prevState.buttons.toUpdatedState(),
)
}
is WalletState.Visa.Content -> {
prevState.copy(
pullToRefreshConfig = prevState.pullToRefreshConfig.toUpdatedState(isRefreshing),
depositButtonState = prevState.depositButtonState.toUpdatedState(isRefreshing),
balancesAndLimitBlockState = prevState.balancesAndLimitBlockState.toUpdatedState(isRefreshing),
)
}
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
-> prevState
}
}
@ -64,4 +74,17 @@ internal class SetRefreshStateTransformer(
}
}
}
private fun DepositButtonState.toUpdatedState(isRefreshing: Boolean): DepositButtonState {
return copy(isEnabled = !isRefreshing)
}
private fun BalancesAndLimitsBlockState.toUpdatedState(isRefreshing: Boolean): BalancesAndLimitsBlockState {
return when (this) {
is BalancesAndLimitsBlockState.Content -> copy(isEnabled = !isRefreshing)
is BalancesAndLimitsBlockState.Error,
is BalancesAndLimitsBlockState.Loading,
-> this
}
}
}

View file

@ -18,14 +18,16 @@ internal class SetTokenListErrorTransformer(
is WalletState.MultiCurrency.Content -> {
prevState.copy(tokensListState = WalletTokensListState.Empty)
}
is WalletState.MultiCurrency.Locked,
-> {
Timber.e("Impossible to load tokens list for locked wallet")
is WalletState.MultiCurrency.Locked -> {
Timber.w("Impossible to load tokens list for locked wallet")
prevState
}
is WalletState.SingleCurrency,
-> {
Timber.e("Impossible to load tokens list for single-currency wallet")
is WalletState.SingleCurrency -> {
Timber.w("Impossible to load tokens list for single-currency wallet")
prevState
}
is WalletState.Visa -> {
Timber.w("Impossible to load tokens list for VISA wallet")
prevState
}
}

View file

@ -29,14 +29,14 @@ internal class SetTokenListTransformer(
manageTokensButtonConfig = createManageTokensButtonConfig(),
)
}
is WalletState.MultiCurrency.Locked,
-> {
Timber.e("Impossible to load tokens list for locked wallet")
is WalletState.MultiCurrency.Locked -> {
Timber.w("Impossible to load tokens list for locked wallet")
prevState
}
is WalletState.Visa,
is WalletState.SingleCurrency,
-> {
Timber.e("Impossible to load tokens list for single-currency wallet")
Timber.w("Impossible to load tokens list for single-currency wallet")
prevState
}
}

View file

@ -29,40 +29,37 @@ internal class SetTxHistoryCountErrorTransformer(
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.SingleCurrency.Content -> prevState.toErrorState()
is WalletState.SingleCurrency.Content -> prevState.copy(txHistoryState = createErrorState())
is WalletState.Visa.Content -> prevState.copy(txHistoryState = createErrorState())
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
-> {
Timber.e("Impossible to load transactions history for locked wallet")
Timber.w("Impossible to load transactions history for locked wallet")
prevState
}
is WalletState.MultiCurrency,
-> {
Timber.e("Impossible to load transactions history for multi-currency wallet")
is WalletState.MultiCurrency -> {
Timber.w("Impossible to load transactions history for multi-currency wallet")
prevState
}
}
}
private fun WalletState.SingleCurrency.Content.toErrorState(): WalletState {
return copy(
txHistoryState = when (error) {
is TxHistoryStateError.EmptyTxHistories -> {
TxHistoryState.Empty(onExploreClick = clickIntents::onExploreClick)
}
is TxHistoryStateError.DataError -> {
TxHistoryState.Error(
onReloadClick = clickIntents::onReloadClick,
onExploreClick = clickIntents::onExploreClick,
)
}
is TxHistoryStateError.TxHistoryNotImplemented -> {
TxHistoryState.NotSupported(
pendingTransactions = txHistoryItemConverter.convertList(pendingTransactions)
.toImmutableList(),
onExploreClick = clickIntents::onExploreClick,
)
}
},
)
private fun createErrorState(): TxHistoryState = when (error) {
is TxHistoryStateError.EmptyTxHistories -> {
TxHistoryState.Empty(onExploreClick = clickIntents::onExploreClick)
}
is TxHistoryStateError.DataError -> {
TxHistoryState.Error(
onReloadClick = clickIntents::onReloadClick,
onExploreClick = clickIntents::onExploreClick,
)
}
is TxHistoryStateError.TxHistoryNotImplemented -> {
TxHistoryState.NotSupported(
pendingTransactions = txHistoryItemConverter.convertList(pendingTransactions)
.toImmutableList(),
onExploreClick = clickIntents::onExploreClick,
)
}
}
}

View file

@ -18,34 +18,40 @@ internal class SetTxHistoryCountTransformer(
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.SingleCurrency.Content -> prevState.toLoadingState()
is WalletState.SingleCurrency.Content -> prevState.copy(
txHistoryState = prevState.txHistoryState.toLoadingState(),
)
is WalletState.Visa.Content -> prevState.copy(
txHistoryState = prevState.txHistoryState.toLoadingState(),
)
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
-> {
Timber.e("Impossible to load transactions history for locked wallet")
Timber.w("Impossible to load transactions history for locked wallet")
prevState
}
is WalletState.MultiCurrency,
-> {
Timber.e("Impossible to load transactions history for multi-currency wallet")
is WalletState.MultiCurrency -> {
Timber.w("Impossible to load transactions history for multi-currency wallet")
prevState
}
}
}
private fun WalletState.SingleCurrency.Content.toLoadingState(): WalletState {
return if (txHistoryState is TxHistoryState.Content) {
(txHistoryState as? TxHistoryState.Content)?.contentItems?.update {
Timber.d("Load transactions history: $transactionsCount")
PagingData.from(data = createLoadingItems())
}
this
} else {
val txHistoryContent = TxHistoryState.Content(
contentItems = MutableStateFlow(
value = PagingData.from(data = createLoadingItems()),
),
private fun TxHistoryState.toLoadingState(): TxHistoryState {
return if (this is TxHistoryState.Content) {
Timber.d("Load transactions history: $transactionsCount")
copy(
contentItems = contentItems.apply {
update {
PagingData.from(data = createLoadingItems())
}
},
)
} else {
TxHistoryState.Content(
contentItems = MutableStateFlow(PagingData.from(createLoadingItems())),
)
copy(txHistoryState = txHistoryContent)
}
}

View file

@ -15,27 +15,27 @@ internal class SetTxHistoryItemsErrorTransformer(
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.SingleCurrency.Content -> {
prevState.copy(
txHistoryState = when (error) {
is TxHistoryListError.DataError -> {
TxHistoryState.Error(
onReloadClick = clickIntents::onReloadClick,
onExploreClick = clickIntents::onExploreClick,
)
}
},
)
}
is WalletState.SingleCurrency.Content -> prevState.copy(txHistoryState = createErrorState())
is WalletState.Visa.Content -> prevState.copy(txHistoryState = createErrorState())
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
-> {
Timber.e("Impossible to load transactions history for locked wallet")
Timber.w("Impossible to load transactions history for locked wallet")
prevState
}
is WalletState.MultiCurrency -> {
Timber.e("Impossible to load transactions history for multi-currency wallet")
Timber.w("Impossible to load transactions history for multi-currency wallet")
prevState
}
}
}
private fun createErrorState(): TxHistoryState.Error = when (error) {
is TxHistoryListError.DataError -> {
TxHistoryState.Error(
onReloadClick = clickIntents::onReloadClick,
onExploreClick = clickIntents::onExploreClick,
)
}
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.state2.transformers
import androidx.paging.PagingData
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState
@ -17,25 +18,32 @@ internal class SetTxHistoryItemsTransformer(
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.SingleCurrency.Content -> {
val converter = TxHistoryItemFlowConverter(
userWallet = userWallet,
currentState = prevState,
clickIntents = clickIntents,
)
prevState.copy(
txHistoryState = converter.convert(value = flow),
)
}
is WalletState.SingleCurrency.Content -> prevState.copy(
txHistoryState = prevState.txHistoryState.toContentState(),
)
is WalletState.Visa.Content -> prevState.copy(
txHistoryState = prevState.txHistoryState.toContentState(),
)
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
-> {
Timber.e("Impossible to load transactions history for locked wallet")
Timber.w("Impossible to load transactions history for locked wallet")
prevState
}
is WalletState.MultiCurrency -> {
Timber.e("Impossible to load transactions history for multi-currency wallet")
Timber.w("Impossible to load transactions history for multi-currency wallet")
prevState
}
}
}
private fun TxHistoryState.toContentState(): TxHistoryState {
val converter = TxHistoryItemFlowConverter(
userWallet = userWallet,
currentState = this,
clickIntents = clickIntents,
)
return converter.convert(flow)
}
}

View file

@ -15,10 +15,12 @@ internal class SetWarningsTransformer(
return when (prevState) {
is WalletState.MultiCurrency.Content -> prevState.copy(warnings = warnings)
is WalletState.SingleCurrency.Content -> prevState.copy(warnings = warnings)
is WalletState.Visa.Content -> prevState.copy(warnings = warnings)
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
-> {
Timber.e("Impossible to update notifications for locked wallet")
Timber.w("Impossible to update notifications for locked wallet")
prevState
}
}

View file

@ -41,9 +41,11 @@ internal class UnlockWalletTransformer(
return when (prevState) {
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
-> walletLoadingStateFactory.create(userWallet = unlockedWallet)
is WalletState.MultiCurrency.Content,
is WalletState.SingleCurrency.Content,
is WalletState.Visa.Content,
-> {
Timber.e("Impossible to unlock wallet with content state")
prevState

View file

@ -20,8 +20,12 @@ internal class UpdateWalletCardsCountTransformer(
is WalletState.SingleCurrency.Content -> {
prevState.copy(walletCardState = prevState.walletCardState.toUpdatedState())
}
is WalletState.Visa.Content -> {
prevState.copy(walletCardState = prevState.walletCardState.toUpdatedState())
}
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
-> {
Timber.e("Impossible to update wallet cards count for locked wallet")
prevState

View file

@ -4,6 +4,7 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state2.WalletScreenState
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState
import kotlinx.collections.immutable.toImmutableList
import timber.log.Timber
internal abstract class WalletStateTransformer(
protected val userWalletId: UserWalletId,
@ -20,4 +21,13 @@ internal abstract class WalletStateTransformer(
.toImmutableList(),
)
}
protected inline fun <reified S : WalletState> WalletState.transformWhenInState(
transform: (state: S) -> WalletState,
): WalletState = if (this is S) {
transform(this)
} else {
Timber.w("Impossible to transform ${this::class.simpleName} because current is ${S::class.simpleName}")
this
}
}

View file

@ -9,7 +9,6 @@ import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2
import com.tangem.utils.converter.Converter
import kotlinx.coroutines.CoroutineScope
@ -21,7 +20,7 @@ private val scope = CoroutineScope(Dispatchers.IO)
internal class TxHistoryItemFlowConverter(
private val userWallet: UserWallet,
private val currentState: WalletState.SingleCurrency.Content,
private val currentState: TxHistoryState,
private val clickIntents: WalletClickIntentsV2,
) : Converter<Flow<PagingData<TxHistoryItem>>, TxHistoryState?> {
@ -35,7 +34,7 @@ internal class TxHistoryItemFlowConverter(
}
override fun convert(value: Flow<PagingData<TxHistoryItem>>): TxHistoryState {
val txHistoryContent = currentState.txHistoryState as? TxHistoryState.Content
val txHistoryContent = currentState as? TxHistoryState.Content
?: TxHistoryState.Content(contentItems = MutableStateFlow(PagingData.empty()))
// FIXME: TxHistoryRepository should send loading transactions

View file

@ -0,0 +1,89 @@
package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletAdditionalInfo
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
import com.tangem.utils.converter.Converter
internal class VisaWalletCardStateConverter(
private val status: CryptoCurrencyStatus,
private val selectedWallet: UserWallet,
private val appCurrency: AppCurrency,
) : Converter<WalletCardState, WalletCardState> {
override fun convert(value: WalletCardState): WalletCardState {
return when (status.value) {
is CryptoCurrencyStatus.Loading -> value.toLoadingState()
is CryptoCurrencyStatus.Custom,
is CryptoCurrencyStatus.MissedDerivation,
is CryptoCurrencyStatus.Unreachable,
-> value.toErrorState()
is CryptoCurrencyStatus.NoQuote,
is CryptoCurrencyStatus.Loaded,
is CryptoCurrencyStatus.NoAccount,
is CryptoCurrencyStatus.NoAmount,
-> value.toContentState(status)
}
}
private fun WalletCardState.toLoadingState(): WalletCardState {
return WalletCardState.Loading(
id = id,
title = title,
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
)
}
private fun WalletCardState.toErrorState(): WalletCardState {
return WalletCardState.Error(
id = id,
title = title,
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
)
}
private fun WalletCardState.toContentState(status: CryptoCurrencyStatus): WalletCardState {
return WalletCardState.Content(
id = id,
title = title,
additionalInfo = createAdditionalInfo(status),
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
balance = formatAmount(status),
cardCount = selectedWallet.getCardsCount(),
)
}
private fun createAdditionalInfo(status: CryptoCurrencyStatus): WalletAdditionalInfo {
val fiatAmount = BigDecimalFormatter.formatFiatAmount(
status.value.fiatAmount,
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
val infoContent = stringReference(
value = buildString {
append(fiatAmount)
append("")
append(status.currency.network.name)
},
)
return WalletAdditionalInfo(hideable = true, infoContent)
}
private fun formatAmount(status: CryptoCurrencyStatus): String {
val amount = status.value.amount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatCryptoAmount(amount, status.currency)
}
}

View file

@ -7,10 +7,17 @@ import com.tangem.feature.wallet.presentation.wallet.state2.WalletState
internal inline fun UserWallet.createStateByWalletType(
multiCurrencyCreator: () -> WalletState.MultiCurrency,
singleCurrencyCreator: () -> WalletState.SingleCurrency,
): WalletState {
return if (isWalletWithTokens()) multiCurrencyCreator() else singleCurrencyCreator()
visaWalletCreator: () -> WalletState.Visa,
): WalletState = when {
isVisaWallet() -> visaWalletCreator()
isWalletWithTokens() -> multiCurrencyCreator()
else -> singleCurrencyCreator()
}
private fun UserWallet.isWalletWithTokens(): Boolean {
return isMultiCurrency || scanResponse.cardTypesResolver.isSingleWalletWithToken()
}
private fun UserWallet.isVisaWallet(): Boolean {
return scanResponse.cardTypesResolver.isVisaWallet()
}

View file

@ -11,6 +11,8 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletMana
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig
import com.tangem.feature.wallet.presentation.wallet.state2.ManageTokensButtonConfig
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState.Visa.BalancesAndLimitsBlockState
import com.tangem.feature.wallet.presentation.wallet.state2.WalletState.Visa.DepositButtonState
import com.tangem.feature.wallet.presentation.wallet.state2.WalletTokensListState
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2
import kotlinx.collections.immutable.PersistentList
@ -28,6 +30,7 @@ internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIn
return userWallet.createStateByWalletType(
multiCurrencyCreator = { createLoadingMultiCurrencyContent(userWallet) },
singleCurrencyCreator = { createLoadingSingleCurrencyContent(userWallet) },
visaWalletCreator = { createLoadingVisaWalletContent(userWallet) },
)
}
@ -59,6 +62,22 @@ internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIn
)
}
private fun createLoadingVisaWalletContent(userWallet: UserWallet): WalletState.Visa.Content {
return WalletState.Visa.Content(
pullToRefreshConfig = createPullToRefreshConfig(),
walletCardState = userWallet.toLoadingWalletCardState(),
warnings = persistentListOf(),
bottomSheetConfig = null,
balancesAndLimitBlockState = BalancesAndLimitsBlockState.Loading,
txHistoryState = TxHistoryState.Content(
contentItems = MutableStateFlow(
value = TxHistoryState.getDefaultLoadingTransactions(clickIntents::onExploreClick),
),
),
depositButtonState = DepositButtonState(isEnabled = false, clickIntents::onDepositClick),
)
}
private fun createPullToRefreshConfig(): WalletPullToRefreshConfig {
return WalletPullToRefreshConfig(onRefresh = clickIntents::onRefreshSwipe, isRefreshing = false)
}

View file

@ -12,19 +12,15 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import kotlin.coroutines.CoroutineContext
internal class MultiWalletWarningsSubscriber(
private val userWalletId: UserWalletId,
private val stateHolder: WalletStateHolderV2,
private val clickIntents: WalletClickIntentsV2,
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
) : WalletSubscriber<ImmutableList<WalletNotification>>(name = "multi_wallet_warnings") {
) : WalletSubscriber() {
override fun create(
coroutineScope: CoroutineScope,
uiDispatcher: CoroutineContext,
): Flow<ImmutableList<WalletNotification>> {
override fun create(coroutineScope: CoroutineScope): Flow<ImmutableList<WalletNotification>> {
return getMultiWalletWarningsFactory.create(clickIntents)
.conflate()
.distinctUntilChanged()

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import arrow.core.Either
import arrow.core.getOrElse
import com.tangem.common.extensions.isZero
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
@ -18,8 +19,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.math.BigDecimal
import kotlin.coroutines.CoroutineContext
internal class PrimaryCurrencySubscriber(
private val userWallet: UserWallet,
@ -28,35 +29,35 @@ internal class PrimaryCurrencySubscriber(
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
) : WalletSubscriber<Either<CurrencyStatusError, CryptoCurrencyStatus>>(name = "primary_currency") {
) : WalletSubscriber() {
override fun create(
coroutineScope: CoroutineScope,
uiDispatcher: CoroutineContext,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
override fun create(coroutineScope: CoroutineScope): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return getPrimaryCurrencyStatusUpdatesUseCase(userWallet.walletId)
.conflate()
.distinctUntilChanged()
.onEach(::updateContent)
.onEach(::sendAnalyticsEvent)
.onEach(::checkWalletWithFunds)
.onEach { maybeCurrencyStatus ->
val status = maybeCurrencyStatus.getOrElse {
Timber.e("Unable to get primary currency status: $it")
return@onEach
}
updateContent(status)
sendAnalyticsEvent(status)
checkWalletWithFunds(status)
}
}
private fun updateContent(maybeCurrencyStatus: Either<CurrencyStatusError, CryptoCurrencyStatus>) {
val status = (maybeCurrencyStatus as? Either.Right)?.value ?: return
private fun updateContent(status: CryptoCurrencyStatus) {
stateHolder.update(
SetPrimaryCurrencyTransformer(
status = status.value,
status = status,
userWallet = userWallet,
appCurrency = appCurrency,
),
)
}
private fun sendAnalyticsEvent(maybeCurrencyStatus: Either<CurrencyStatusError, CryptoCurrencyStatus>) {
val status = (maybeCurrencyStatus as? Either.Right)?.value ?: return
private fun sendAnalyticsEvent(status: CryptoCurrencyStatus) {
val fiatAmount = status.value.fiatAmount
val cardBalanceState = when (status.value) {
is CryptoCurrencyStatus.Loaded,
@ -84,9 +85,7 @@ internal class PrimaryCurrencySubscriber(
}
}
private suspend fun checkWalletWithFunds(maybeCurrencyStatus: Either<CurrencyStatusError, CryptoCurrencyStatus>) {
val status = (maybeCurrencyStatus as? Either.Right)?.value ?: return
private suspend fun checkWalletWithFunds(status: CryptoCurrencyStatus) {
if (status.value.amount?.isZero() == false) setWalletWithFundsFoundUseCase()
}
}

View file

@ -10,7 +10,6 @@ import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetCryp
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlin.coroutines.CoroutineContext
internal class SingleWalletButtonsSubscriber(
private val userWallet: UserWallet,
@ -18,9 +17,9 @@ internal class SingleWalletButtonsSubscriber(
private val clickIntents: WalletClickIntentsV2,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
) : WalletSubscriber<TokenActionsState>(name = "single_wallet_buttons") {
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope, uiDispatcher: CoroutineContext): Flow<TokenActionsState> {
override fun create(coroutineScope: CoroutineScope): Flow<TokenActionsState> {
return channelFlow {
getPrimaryCurrencyStatusUpdatesUseCase.collectLatest(userWalletId = userWallet.walletId) { status ->
getCryptoCurrencyActionsUseCase(userWallet = userWallet, cryptoCurrencyStatus = status)

View file

@ -12,7 +12,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import kotlin.coroutines.CoroutineContext
/**
[REDACTED_AUTHOR]
@ -22,12 +21,9 @@ internal class SingleWalletNotificationsSubscriber(
private val stateHolder: WalletStateHolderV2,
private val getSingleWalletWarningsFactory: GetSingleWalletWarningsFactory,
private val clickIntents: WalletClickIntentsV2,
) : WalletSubscriber<ImmutableList<WalletNotification>>(name = "single_wallet_warnings") {
) : WalletSubscriber() {
override fun create(
coroutineScope: CoroutineScope,
uiDispatcher: CoroutineContext,
): Flow<ImmutableList<WalletNotification>> {
override fun create(coroutineScope: CoroutineScope): Flow<ImmutableList<WalletNotification>> {
return getSingleWalletWarningsFactory.create(clickIntents)
.conflate()
.distinctUntilChanged()

View file

@ -17,7 +17,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import kotlin.coroutines.CoroutineContext
@Suppress("LongParameterList")
internal class SingleWalletWithTokenListSubscriber(
@ -28,12 +27,9 @@ internal class SingleWalletWithTokenListSubscriber(
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
private val walletWithFundsChecker: WalletWithFundsChecker,
private val getCardTokensListUseCase: GetCardTokensListUseCase,
) : WalletSubscriber<Either<TokenListError, TokenList>>(name = "single_wallet_with_token_list") {
) : WalletSubscriber() {
override fun create(
coroutineScope: CoroutineScope,
uiDispatcher: CoroutineContext,
): Flow<Either<TokenListError, TokenList>> {
override fun create(coroutineScope: CoroutineScope): Flow<Either<TokenListError, TokenList>> {
return getCardTokensListUseCase(userWalletId = userWallet.walletId)
.conflate()
.distinctUntilChanged()

View file

@ -17,7 +17,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import kotlin.coroutines.CoroutineContext
typealias MaybeTokenListFlow = Flow<Either<TokenListError, TokenList>>
@ -30,12 +29,9 @@ internal class TokenListSubscriber(
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
private val walletWithFundsChecker: WalletWithFundsChecker,
private val getTokenListUseCase: GetTokenListUseCase,
) : WalletSubscriber<Either<TokenListError, TokenList>>(name = "token_list") {
) : WalletSubscriber() {
override fun create(
coroutineScope: CoroutineScope,
uiDispatcher: CoroutineContext,
): Flow<Either<TokenListError, TokenList>> {
override fun create(coroutineScope: CoroutineScope): Flow<Either<TokenListError, TokenList>> {
return getTokenListUseCase(userWalletId = userWallet.walletId)
.conflate()
.distinctUntilChanged()

View file

@ -21,7 +21,6 @@ import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletCl
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlin.coroutines.CoroutineContext
typealias MaybeTxHistoryCount = Either<TxHistoryStateError, Int>
typealias MaybeTxHistoryItems = Either<TxHistoryListError, Flow<PagingData<TxHistoryItem>>>
@ -35,12 +34,9 @@ internal class TxHistorySubscriber(
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
) : WalletSubscriber<PagingData<TxHistoryItem>>(name = "tx_history") {
) : WalletSubscriber() {
override fun create(
coroutineScope: CoroutineScope,
uiDispatcher: CoroutineContext,
): Flow<PagingData<TxHistoryItem>> {
override fun create(coroutineScope: CoroutineScope): Flow<PagingData<TxHistoryItem>> {
return flow {
getPrimaryCurrencyStatusUpdatesUseCase.collectLatest(userWalletId = userWallet.walletId) { status ->
val maybeTxHistoryItemCount = txHistoryItemsCountUseCase(

View file

@ -0,0 +1,23 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateHolderV2
import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetBalancesAndLimitsTransformer
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
internal class VisaWalletBalancesAndLimitsSubscriber(
private val userWallet: UserWallet,
private val stateHolder: WalletStateHolderV2,
private val clickIntents: WalletClickIntentsV2,
) : WalletSubscriber() {
// TODO: Implement in [REDACTED_JIRA]
override fun create(coroutineScope: CoroutineScope): Flow<*> = suspend {
delay(timeMillis = 500)
stateHolder.update(SetBalancesAndLimitsTransformer(userWallet, clickIntents))
}.asFlow()
}

View file

@ -7,23 +7,20 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
/**
* Component for implementation of flow subscription
*
* @property name unique name of subscriber
* [T] - type of flow
*
[REDACTED_AUTHOR]
*/
internal abstract class WalletSubscriber<T>(val name: String) {
internal abstract class WalletSubscriber {
protected abstract fun create(coroutineScope: CoroutineScope, uiDispatcher: CoroutineContext): Flow<T>
protected abstract fun create(coroutineScope: CoroutineScope): Flow<*>
fun subscribe(coroutineScope: CoroutineScope, dispatchers: CoroutineDispatcherProvider): Job {
Timber.d("Subscribe on $name")
return create(coroutineScope, dispatchers.main)
Timber.d("Subscribe on ${this::class.simpleName}")
return create(coroutineScope)
.flowOn(dispatchers.main)
.launchIn(coroutineScope)
}

View file

@ -46,5 +46,11 @@ internal fun LazyListScope.contentItemsV2(
is WalletStateV2.SingleCurrency -> {
txHistoryItems(state.txHistoryState, txHistoryItems, isBalanceHidden, modifier)
}
is WalletStateV2.Visa.Content -> {
// TODO: Will be implemented soon
}
is WalletStateV2.Visa.Locked -> {
// TODO: Will be implemented soon
}
}
}

View file

@ -97,25 +97,27 @@ internal class WalletViewModelV2 @Inject constructor(
shouldSaveUserWalletsUseCase()
.conflate()
.distinctUntilChanged()
.collectLatest { shouldSaveUserWallet ->
getWalletsUseCase()
.distinctUntilChanged()
.conflate()
.map {
walletsUpdateActionResolver.resolve(
wallets = it,
currentState = stateHolder.value,
canSaveWallets = shouldSaveUserWallet,
)
}
.onEach(::updateWallets)
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
.saveIn(walletsUpdateJobHolder)
}
.collectLatest(::subscribeToUserWalletsUpdates)
}
}
private fun subscribeToUserWalletsUpdates(shouldSaveUserWallet: Boolean) {
getWalletsUseCase()
.distinctUntilChanged()
.conflate()
.map {
walletsUpdateActionResolver.resolve(
wallets = it,
currentState = stateHolder.value,
canSaveWallets = shouldSaveUserWallet,
)
}
.onEach(::updateWallets)
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
.saveIn(walletsUpdateJobHolder)
}
private fun subscribeOnBalanceHiding() {
getBalanceHidingSettingsUseCase()
.conflate()

View file

@ -0,0 +1,26 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
import com.tangem.core.ui.extensions.stringReference
import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletEventSender
import javax.inject.Inject
internal interface VisaWalletIntents {
fun onDepositClick()
fun onBalancesAndLimitsClick()
}
internal class VisaWalletIntentsImplementor @Inject constructor(
private val eventSender: WalletEventSender,
) : VisaWalletIntents {
override fun onDepositClick() {
eventSender.send(WalletEvent.ShowToast(stringReference(value = "Not implemented yet")))
}
override fun onBalancesAndLimitsClick() {
eventSender.send(WalletEvent.ShowToast(stringReference(value = "Not implemented yet")))
}
}

View file

@ -30,6 +30,7 @@ internal class WalletClickIntentsV2 @Inject constructor(
private val warningsClickIntentsImplementer: WalletWarningsClickIntentsImplementer,
private val currencyActionsClickIntentsImplementor: WalletCurrencyActionsClickIntentsImplementor,
private val contentClickIntentsImplementor: WalletContentClickIntentsImplementor,
private val visaWalletIntentsImplementor: VisaWalletIntentsImplementor,
private val stateHolder: WalletStateHolderV2,
private val walletScreenContentLoader: WalletScreenContentLoader,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
@ -45,7 +46,8 @@ internal class WalletClickIntentsV2 @Inject constructor(
WalletCardClickIntents by walletCardClickIntentsImplementor,
WalletWarningsClickIntents by warningsClickIntentsImplementer,
WalletCurrencyActionsClickIntents by currencyActionsClickIntentsImplementor,
WalletContentClickIntents by contentClickIntentsImplementor {
WalletContentClickIntents by contentClickIntentsImplementor,
VisaWalletIntents by visaWalletIntentsImplementor {
override fun initialize(router: InnerWalletRouter, coroutineScope: CoroutineScope) {
super.initialize(router, coroutineScope)
@ -83,16 +85,23 @@ internal class WalletClickIntentsV2 @Inject constructor(
analyticsEventHandler.send(PortfolioEvent.Refreshed)
refreshMultiCurrencyContent()
}
is WalletState.SingleCurrency.Content -> {
is WalletState.SingleCurrency.Content,
is WalletState.Visa.Content,
-> {
analyticsEventHandler.send(PortfolioEvent.Refreshed)
refreshSingleCurrencyContent()
}
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
-> Unit
}
}
fun onReloadClick() {
refreshSingleCurrencyContent()
}
private fun refreshMultiCurrencyContent() {
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
@ -117,10 +126,6 @@ internal class WalletClickIntentsV2 @Inject constructor(
}
}
fun onReloadClick() {
refreshSingleCurrencyContent()
}
// FIXME: refreshSingleCurrencyContent mustn't update the TxHistory and Buttons. It only must fetch primary
// currency. Now it not works because GetPrimaryCurrency's subscriber uses .distinctUntilChanged()
private fun refreshSingleCurrencyContent() {