Updated on 2026-08-14

This commit is contained in:
Tangem 2024-11-21 12:09:33 +03:00
commit 85423227c4
390 changed files with 11140 additions and 1722 deletions

View file

@ -5,6 +5,7 @@ import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.impl.R
@ -76,7 +77,7 @@ internal object WalletPreviewData {
isGrayscale = false,
showCustomBadge = false,
),
titleState = TokenItemState.TitleState.Content(text = "Polygon"),
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Polygon")),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "3 172,14 $"),
)
}
@ -108,7 +109,7 @@ internal object WalletPreviewData {
tokenItemState = tokenItemDragState.copy(
id = "${group.id}_token_$tokenNumber",
titleState = TokenItemState.TitleState.Content(
text = "Token $tokenNumber from $networkNumber network",
text = stringReference(value = "Token $tokenNumber from $networkNumber network"),
),
),
groupId = group.id,

View file

@ -19,7 +19,7 @@ internal object WalletScreenPreviewData {
private val tokenItemState = TokenItemState.Content(
id = "1",
iconState = CurrencyIconState.Locked,
titleState = TokenItemState.TitleState.Content(text = "Bitcoin"),
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Bitcoin")),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "12 368,14 \$"),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "0,35853044 BTC"),
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
@ -45,7 +45,7 @@ internal object WalletScreenPreviewData {
TokensListItemUM.Token(
state = tokenItemState.copy(
id = "2",
titleState = TokenItemState.TitleState.Content(text = "Ethereum"),
titleState = TokenItemState.TitleState.Content(text = stringReference("Ethereum")),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "3 340,79 \$"),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "1,856660295 ETH"),
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
@ -59,7 +59,7 @@ internal object WalletScreenPreviewData {
state = TokenItemState.Unreachable(
id = "3",
iconState = CurrencyIconState.Locked,
titleState = TokenItemState.TitleState.Content(text = "Polygon"),
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Polygon")),
onItemClick = {},
onItemLongClick = {},
),
@ -67,7 +67,7 @@ internal object WalletScreenPreviewData {
TokensListItemUM.Token(
state = tokenItemState.copy(
id = "4",
titleState = TokenItemState.TitleState.Content(text = "Shiba Inu"),
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Shiba Inu")),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "48,64 \$"),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "6 200 220,00 SHIB"),
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
@ -119,6 +119,7 @@ internal object WalletScreenPreviewData {
onRefresh = {},
),
walletCardState = miltiUnreachableCard,
buttons = persistentListOf(buyButton),
warnings = persistentListOf(
WalletNotification.Warning.SomeNetworksUnreachable,
),

View file

@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.it
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.components.token.state.TokenItemState
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
@ -46,7 +47,7 @@ internal class CryptoCurrencyToDraggableItemConverter(
return TokenItemState.Draggable(
id = getTokenItemId(currency.id),
iconState = iconStateConverter.convert(currencyStatus),
titleState = TokenItemState.TitleState.Content(text = currency.name),
titleState = TokenItemState.TitleState.Content(text = stringReference(currency.name)),
subtitle2State = if (currencyStatus.value.isError) {
TokenItemState.Subtitle2State.Unreachable
} else {

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
@ -10,10 +11,12 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletActionsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletTokenListSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
@Suppress("LongParameterList")
internal class MultiWalletContentLoader(
@ -28,10 +31,12 @@ internal class MultiWalletContentLoader(
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
private val rampStateManager: RampStateManager,
private val walletFeatureToggles: WalletFeatureToggles,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> {
return listOf(
return listOfNotNull(
MultiWalletTokenListSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,
@ -50,6 +55,16 @@ internal class MultiWalletContentLoader(
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
),
createMultiWalletButtonsSubscriber(),
)
}
private fun createMultiWalletButtonsSubscriber(): MultiWalletActionsSubscriber? {
return MultiWalletActionsSubscriber(
userWallet = userWallet,
rampStateManager = rampStateManager,
stateController = stateHolder,
)
.takeIf { walletFeatureToggles.isMainActionButtonsEnabled }
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
@ -11,6 +12,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenList
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import dagger.hilt.android.scopes.ViewModelScoped
import javax.inject.Inject
@ -26,6 +28,8 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
private val rampStateManager: RampStateManager,
private val walletFeatureToggles: WalletFeatureToggles,
) {
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): WalletContentLoader {
@ -41,6 +45,8 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
rampStateManager = rampStateManager,
walletFeatureToggles = walletFeatureToggles,
)
}
}

View file

@ -38,13 +38,16 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
override val enabled: Boolean,
override val dimContent: Boolean,
override val onClick: () -> Unit,
val isInProgress: Boolean = false,
) :
WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_buy),
iconResId = R.drawable.ic_plus_24,
onClick = onClick,
enabled = enabled,
dimContent = dimContent,
isInProgress = isInProgress,
),
)
@ -64,6 +67,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
text = TextReference.Res(id = R.string.common_send),
iconResId = R.drawable.ic_arrow_up_24,
onClick = onClick,
enabled = enabled,
dimContent = dimContent,
),
)
@ -83,6 +87,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_receive),
iconResId = R.drawable.ic_arrow_down_24,
enabled = enabled,
dimContent = dimContent,
onClick = onClick,
onLongClick = onLongClick,
@ -98,6 +103,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
text = TextReference.Res(id = R.string.common_stake),
iconResId = R.drawable.ic_staking_24,
onClick = onClick,
enabled = enabled,
dimContent = dimContent,
),
)
@ -113,12 +119,15 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
override val enabled: Boolean,
override val dimContent: Boolean,
override val onClick: () -> Unit,
val isInProgress: Boolean = false,
) : WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_sell),
iconResId = R.drawable.ic_currency_24,
onClick = onClick,
enabled = enabled,
dimContent = dimContent,
isInProgress = isInProgress,
),
)
@ -133,12 +142,15 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
override val enabled: Boolean,
override val dimContent: Boolean,
override val onClick: () -> Unit,
val isInProgress: Boolean = false,
) : WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.swapping_swap_action),
iconResId = R.drawable.ic_exchange_vertical_24,
onClick = onClick,
enabled = enabled,
dimContent = dimContent,
isInProgress = isInProgress,
),
)
}

View file

@ -24,6 +24,7 @@ internal sealed interface WalletState : WalletStateHolder {
data class Content(
override val pullToRefreshConfig: PullToRefreshConfig,
override val walletCardState: WalletCardState,
override val buttons: PersistentList<WalletManageButton>,
override val warnings: ImmutableList<WalletNotification>,
override val bottomSheetConfig: TangemBottomSheetConfig?,
override val tokensListState: WalletTokensListState,
@ -31,13 +32,15 @@ internal sealed interface WalletState : WalletStateHolder {
data class Locked(
override val walletCardState: WalletCardState,
override val buttons: PersistentList<WalletManageButton>,
override val bottomSheetConfig: TangemBottomSheetConfig?,
val onUnlockNotificationClick: () -> Unit,
) : MultiCurrency(),
WalletStateHolder by LockedWalletStateHolder(
walletCardState,
bottomSheetConfig,
onUnlockNotificationClick,
walletCardState = walletCardState,
buttons = buttons,
bottomSheetConfig = bottomSheetConfig,
onUnlockNotificationClick = onUnlockNotificationClick,
) {
override val tokensListState = WalletTokensListState.ContentState.Locked
@ -46,7 +49,6 @@ internal sealed interface WalletState : WalletStateHolder {
sealed class SingleCurrency : WalletState, TxHistoryStateHolder {
abstract val buttons: PersistentList<WalletManageButton>
abstract val marketPriceBlockState: MarketPriceBlockState?
data class Content(
@ -68,9 +70,10 @@ internal sealed interface WalletState : WalletStateHolder {
) : SingleCurrency(),
TxHistoryStateHolder by LockedTxHistoryStateHolder(onExploreClick),
WalletStateHolder by LockedWalletStateHolder(
walletCardState,
bottomSheetConfig,
onUnlockNotificationClick,
walletCardState = walletCardState,
buttons = buttons,
bottomSheetConfig = bottomSheetConfig,
onUnlockNotificationClick = onUnlockNotificationClick,
) {
override val marketPriceBlockState: MarketPriceBlockState? = null
@ -84,6 +87,7 @@ internal sealed interface WalletState : WalletStateHolder {
data class Content(
override val pullToRefreshConfig: PullToRefreshConfig,
override val walletCardState: WalletCardState,
override val buttons: PersistentList<WalletManageButton>,
override val warnings: ImmutableList<WalletNotification>,
override val bottomSheetConfig: TangemBottomSheetConfig?,
override val balancesAndLimitBlockState: BalancesAndLimitsBlockState,
@ -93,15 +97,17 @@ internal sealed interface WalletState : WalletStateHolder {
data class Locked(
override val walletCardState: WalletCardState,
val onUnlockNotificationClick: () -> Unit,
override val buttons: PersistentList<WalletManageButton>,
override val bottomSheetConfig: TangemBottomSheetConfig?,
val onUnlockNotificationClick: () -> Unit,
val onExploreClick: () -> Unit,
) : Visa(),
TxHistoryStateHolder by LockedTxHistoryStateHolder(onExploreClick),
WalletStateHolder by LockedWalletStateHolder(
walletCardState,
bottomSheetConfig,
onUnlockNotificationClick,
walletCardState = walletCardState,
buttons = buttons,
bottomSheetConfig = bottomSheetConfig,
onUnlockNotificationClick = onUnlockNotificationClick,
) {
override val balancesAndLimitBlockState: BalancesAndLimitsBlockState? = null

View file

@ -3,20 +3,24 @@ package com.tangem.feature.wallet.presentation.wallet.state.model.holder
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
internal interface WalletStateHolder {
val pullToRefreshConfig: PullToRefreshConfig
val walletCardState: WalletCardState
val buttons: PersistentList<WalletManageButton>
val warnings: ImmutableList<WalletNotification>
val bottomSheetConfig: TangemBottomSheetConfig?
}
internal class LockedWalletStateHolder(
override val walletCardState: WalletCardState,
override val buttons: PersistentList<WalletManageButton>,
override val bottomSheetConfig: TangemBottomSheetConfig?,
onUnlockNotificationClick: () -> Unit,
) : WalletStateHolder {

View file

@ -5,18 +5,21 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import kotlinx.collections.immutable.toImmutableList
internal class AddWalletTransformer(
private val userWallet: UserWallet,
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val walletFeatureToggles: WalletFeatureToggles,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
WalletLoadingStateFactory(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
walletFeatureToggles = walletFeatureToggles,
)
}

View file

@ -7,6 +7,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
import com.tangem.feature.wallet.presentation.wallet.state.utils.createStateByWalletType
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@ -16,10 +17,15 @@ internal class InitializeWalletsTransformer(
private val wallets: List<UserWallet>,
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val walletFeatureToggles: WalletFeatureToggles,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
WalletLoadingStateFactory(clickIntents = clickIntents, walletImageResolver = walletImageResolver)
WalletLoadingStateFactory(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
walletFeatureToggles = walletFeatureToggles,
)
}
override fun transform(prevState: WalletScreenState): WalletScreenState {
@ -52,6 +58,7 @@ internal class InitializeWalletsTransformer(
multiCurrencyCreator = {
WalletState.MultiCurrency.Locked(
walletCardState = userWallet.toLockedWalletCardState(),
buttons = createMultiWalletDisabledButtons(),
bottomSheetConfig = null,
onUnlockNotificationClick = clickIntents::onOpenUnlockWalletsBottomSheetClick,
)
@ -68,6 +75,7 @@ internal class InitializeWalletsTransformer(
visaWalletCreator = {
WalletState.Visa.Locked(
walletCardState = userWallet.toLockedWalletCardState(),
buttons = createMultiWalletDisabledButtons(),
bottomSheetConfig = null,
onUnlockNotificationClick = clickIntents::onOpenUnlockWalletsBottomSheetClick,
onExploreClick = clickIntents::onExploreClick,
@ -87,6 +95,17 @@ internal class InitializeWalletsTransformer(
)
}
private fun createMultiWalletDisabledButtons(): PersistentList<WalletManageButton> {
if (!walletFeatureToggles.isMainActionButtonsEnabled) return persistentListOf()
return persistentListOf(
WalletManageButton.Receive(enabled = false, dimContent = false, onClick = {}, onLongClick = null),
WalletManageButton.Send(enabled = false, dimContent = false, onClick = {}),
WalletManageButton.Buy(enabled = false, dimContent = false, onClick = {}),
WalletManageButton.Sell(enabled = false, dimContent = false, onClick = {}),
)
}
private fun createDisabledButtons(): PersistentList<WalletManageButton> {
return persistentListOf(
WalletManageButton.Receive(enabled = false, dimContent = false, onClick = {}, onLongClick = null),

View file

@ -6,6 +6,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import kotlinx.collections.immutable.toImmutableList
/**
@ -22,10 +23,15 @@ internal class ReinitializeWalletTransformer(
private val newUserWallet: UserWallet,
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val walletFeatureToggles: WalletFeatureToggles,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
WalletLoadingStateFactory(clickIntents = clickIntents, walletImageResolver = walletImageResolver)
WalletLoadingStateFactory(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
walletFeatureToggles = walletFeatureToggles,
)
}
override fun transform(prevState: WalletScreenState): WalletScreenState {

View file

@ -7,6 +7,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenSta
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import kotlinx.collections.immutable.toImmutableList
import timber.log.Timber
@ -14,10 +15,15 @@ internal class UnlockWalletTransformer(
private val unlockedWallets: List<UserWallet>,
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val walletFeatureToggles: WalletFeatureToggles,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
WalletLoadingStateFactory(clickIntents = clickIntents, walletImageResolver = walletImageResolver)
WalletLoadingStateFactory(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
walletFeatureToggles = walletFeatureToggles,
)
}
override fun transform(prevState: WalletScreenState): WalletScreenState {

View file

@ -0,0 +1,64 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.core.lce.Lce
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.toPersistentList
import timber.log.Timber
internal class UpdateMultiWalletActionsTransformer(
userWalletId: UserWalletId,
private val buyStatus: Lce<Throwable, Any>,
private val sellStatus: Lce<Throwable, Any>,
private val swapStatus: Lce<Throwable, Any>,
) : WalletStateTransformer(userWalletId = userWalletId) {
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.MultiCurrency.Content -> {
prevState.copy(buttons = prevState.buttons.updateButtons())
}
is WalletState.MultiCurrency.Locked -> {
Timber.w("Impossible to load primary currency status for locked wallet")
prevState
}
is WalletState.SingleCurrency -> {
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
}
}
}
private fun PersistentList<WalletManageButton>.updateButtons(): PersistentList<WalletManageButton> {
return map {
when (it) {
is WalletManageButton.Buy -> {
it.copy(
enabled = buyStatus.isContent(),
dimContent = !buyStatus.isContent(),
)
}
is WalletManageButton.Sell -> {
it.copy(
enabled = sellStatus.isContent(),
dimContent = !sellStatus.isContent(),
)
}
is WalletManageButton.Swap -> {
it.copy(
enabled = swapStatus.isContent(),
dimContent = !swapStatus.isContent(),
)
}
else -> it
}
}
.toPersistentList()
}
}

View file

@ -27,8 +27,8 @@ internal class TokenListStateConverter(
private val tokenStatusConverter = TokenItemStateConverter(
appCurrency = appCurrency,
onItemClick = clickIntents::onTokenItemClick,
onItemLongClick = clickIntents::onTokenItemLongClick,
onItemClick = { _, status -> clickIntents.onTokenItemClick(status) },
onItemLongClick = { _, status -> clickIntents.onTokenItemLongClick(status) },
)
override fun convert(value: WalletTokensListState): WalletTokensListState {

View file

@ -9,6 +9,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfo
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
@ -21,6 +22,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
internal class WalletLoadingStateFactory(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val walletFeatureToggles: WalletFeatureToggles,
) {
fun create(userWallet: UserWallet): WalletState {
@ -35,6 +37,7 @@ internal class WalletLoadingStateFactory(
return WalletState.MultiCurrency.Content(
pullToRefreshConfig = createPullToRefreshConfig(),
walletCardState = userWallet.toLoadingWalletCardState(),
buttons = createMultiWalletActions(userWallet),
warnings = persistentListOf(),
bottomSheetConfig = null,
tokensListState = WalletTokensListState.ContentState.Loading,
@ -62,6 +65,7 @@ internal class WalletLoadingStateFactory(
return WalletState.Visa.Content(
pullToRefreshConfig = createPullToRefreshConfig(),
walletCardState = userWallet.toLoadingWalletCardState(),
buttons = createMultiWalletActions(userWallet),
warnings = persistentListOf(),
bottomSheetConfig = null,
balancesAndLimitBlockState = BalancesAndLimitsBlockState.Loading,
@ -89,6 +93,28 @@ internal class WalletLoadingStateFactory(
)
}
private fun createMultiWalletActions(userWallet: UserWallet): PersistentList<WalletManageButton> {
if (!walletFeatureToggles.isMainActionButtonsEnabled) return persistentListOf()
return persistentListOf(
WalletManageButton.Buy(
enabled = false,
dimContent = true,
onClick = { clickIntents.onMultiWalletBuyClick(userWalletId = userWallet.walletId) },
),
WalletManageButton.Swap(
enabled = false,
dimContent = true,
onClick = { clickIntents.onMultiWalletSwapClick(userWalletId = userWallet.walletId) },
),
WalletManageButton.Sell(
enabled = false,
dimContent = true,
onClick = { clickIntents.onMultiWalletSellClick(userWalletId = userWallet.walletId) },
),
)
}
private fun createDimmedButtons(): PersistentList<WalletManageButton> {
return persistentListOf(
WalletManageButton.Receive(enabled = true, dimContent = true, onClick = {}, onLongClick = null),

View file

@ -0,0 +1,52 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.core.lce.Lce
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.UpdateMultiWalletActionsTransformer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onEach
/**
* Multi-currency wallet subscriber for actions state updating
*
* @property userWallet user wallet
* @property rampStateManager ramp state manager
* @property stateController state controller
*
[REDACTED_AUTHOR]
*/
internal class MultiWalletActionsSubscriber(
private val userWallet: UserWallet,
private val rampStateManager: RampStateManager,
private val stateController: WalletStateController,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<*> {
return combine(
flow = rampStateManager.getBuyInitializationStatus(),
flow2 = rampStateManager.getSellInitializationStatus(),
flow3 = rampStateManager.getSwapInitializationStatus(userWalletId = userWallet.walletId),
transform = ::RampStatuses,
)
.onEach { statuses ->
stateController.update(
UpdateMultiWalletActionsTransformer(
userWalletId = userWallet.walletId,
buyStatus = statuses.buy,
sellStatus = statuses.sell,
swapStatus = statuses.swap,
),
)
}
}
private data class RampStatuses(
val buy: Lce<Throwable, Any>,
val sell: Lce<Throwable, Any>,
val swap: Lce<Throwable, Any>,
)
}

View file

@ -75,8 +75,8 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.PushNotificat
import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.*
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.actions
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeTokensButton
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.controlButtons
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.marketPriceBlock
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.BalancesAndLimitsBottomSheet
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.VisaTxDetailsBottomSheet
@ -191,12 +191,23 @@ private fun WalletContent(
)
}
(selectedWallet as? WalletState.SingleCurrency)?.let {
controlButtons(
configs = it.buttons,
selectedWalletIndex = selectedWalletIndex,
modifier = movableItemModifier.padding(top = betweenItemsPadding),
)
when (selectedWallet) {
is WalletState.MultiCurrency,
is WalletState.Visa,
-> {
actions(
actions = selectedWallet.buttons,
selectedWalletIndex = selectedWalletIndex,
modifier = movableItemModifier.padding(top = betweenItemsPadding),
)
}
is WalletState.SingleCurrency -> {
lazyActions(
actions = selectedWallet.buttons,
selectedWalletIndex = selectedWalletIndex,
modifier = movableItemModifier.padding(top = betweenItemsPadding),
)
}
}
notifications(configs = selectedWallet.warnings, modifier = itemModifier)

View file

@ -0,0 +1,74 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.buttons.HorizontalActionChips
import com.tangem.core.ui.components.buttons.actions.ActionButton
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
import com.tangem.feature.wallet.presentation.wallet.ui.components.fastForEach
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
private const val ACTIONS_CONTENT_TYPE = "Actions"
/**
* Actions in scrollable container. Like, "Buy", "Sell", etc
*
* @param actions list of buttons
* @param selectedWalletIndex selected wallet index for creating a unique key
* @param modifier modifier
*
[REDACTED_AUTHOR]
*/
internal fun LazyListScope.lazyActions(
actions: ImmutableList<WalletManageButton>,
selectedWalletIndex: Int,
modifier: Modifier = Modifier,
) {
item(key = ACTIONS_CONTENT_TYPE + selectedWalletIndex, contentType = ACTIONS_CONTENT_TYPE) {
HorizontalActionChips(
buttons = actions.map(WalletManageButton::config).toImmutableList(),
modifier = modifier.animateItem(),
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16),
)
}
}
/**
* Action buttons. Like, "Buy", "Sell", etc
*
* @param actions list of buttons
* @param selectedWalletIndex selected wallet index for creating a unique key
* @param modifier modifier
*
[REDACTED_AUTHOR]
*/
internal fun LazyListScope.actions(
actions: ImmutableList<WalletManageButton>,
selectedWalletIndex: Int,
modifier: Modifier = Modifier,
) {
if (actions.isEmpty()) return
item(key = ACTIONS_CONTENT_TYPE + selectedWalletIndex, contentType = ACTIONS_CONTENT_TYPE) {
Row(
modifier = modifier
.padding(horizontal = 16.dp)
.fillMaxWidth()
.animateItem(),
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing8),
verticalAlignment = Alignment.CenterVertically,
) {
actions.fastForEach {
key(it::class.java) {
ActionButton(config = it.config, modifier = Modifier.weight(1f))
}
}
}
}
}

View file

@ -1,37 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.ui.Modifier
import com.tangem.core.ui.components.buttons.HorizontalActionChips
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
private const val CONTROL_BUTTONS_CONTENT_TYPE = "ControlButtons"
/**
* Single currency control buttons. Like, "Buy", "Sell", etc
*
* @param configs list of buttons
* @param selectedWalletIndex selected wallet index for creating a unique key
* @param modifier modifier
*
[REDACTED_AUTHOR]
*/
@OptIn(ExperimentalFoundationApi::class)
internal fun LazyListScope.controlButtons(
configs: ImmutableList<WalletManageButton>,
selectedWalletIndex: Int,
modifier: Modifier = Modifier,
) {
item(key = CONTROL_BUTTONS_CONTENT_TYPE + selectedWalletIndex, contentType = CONTROL_BUTTONS_CONTENT_TYPE) {
HorizontalActionChips(
buttons = configs.map(WalletManageButton::config).toImmutableList(),
modifier = modifier.animateItemPlacement(),
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16),
)
}
}

View file

@ -31,6 +31,7 @@ import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvid
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
@ -69,6 +70,7 @@ internal class WalletViewModel @Inject constructor(
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase,
private val walletImageResolver: WalletImageResolver,
private val tokenListStore: MultiWalletTokenListStore,
private val walletFeatureToggles: WalletFeatureToggles,
analyticsEventsHandler: AnalyticsEventHandler,
) : ViewModel() {
@ -300,6 +302,7 @@ internal class WalletViewModel @Inject constructor(
wallets = action.wallets,
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
walletFeatureToggles = walletFeatureToggles,
),
)
@ -334,6 +337,7 @@ internal class WalletViewModel @Inject constructor(
newUserWallet = action.selectedWallet,
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
walletFeatureToggles = walletFeatureToggles,
),
)
}
@ -350,6 +354,7 @@ internal class WalletViewModel @Inject constructor(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
walletFeatureToggles = walletFeatureToggles,
),
)
@ -401,6 +406,7 @@ internal class WalletViewModel @Inject constructor(
unlockedWallets = action.unlockedWallets,
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
walletFeatureToggles = walletFeatureToggles,
),
)

View file

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.extenstions.unwrap
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.settings.NeverToShowWalletsScrollPreview
import com.tangem.domain.tokens.FetchCardTokenListUseCase
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
@ -20,6 +21,8 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetToken
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -41,6 +44,7 @@ internal class WalletClickIntents @Inject constructor(
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val neverToShowWalletsScrollPreview: NeverToShowWalletsScrollPreview,
private val rampStateManager: RampStateManager,
private val dispatchers: CoroutineDispatcherProvider,
) : BaseWalletClickIntents(),
WalletCardClickIntents by walletCardClickIntentsImplementor,
@ -116,6 +120,12 @@ internal class WalletClickIntents @Inject constructor(
fetchTokenListUseCase(userWalletId = userWallet.walletId, mode = RefreshMode.FULL)
}
listOf(
async { rampStateManager.fetchBuyServiceData() },
async { rampStateManager.fetchSellServiceData() },
)
.awaitAll()
maybeFetchResult.onLeft {
stateHolder.update(
SetTokenListErrorTransformer(

View file

@ -81,6 +81,12 @@ interface WalletCurrencyActionsClickIntents {
fun onExploreClick()
fun onAnalyticsClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onMultiWalletBuyClick(userWalletId: UserWalletId)
fun onMultiWalletSellClick(userWalletId: UserWalletId)
fun onMultiWalletSwapClick(userWalletId: UserWalletId)
}
@Suppress("LongParameterList", "LargeClass")
@ -374,7 +380,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
appRouter.push(
AppRoute.Swap(
currency = cryptoCurrencyStatus.currency,
currencyFrom = cryptoCurrencyStatus.currency,
userWalletId = userWalletId,
),
)
@ -432,6 +438,21 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
}
}
override fun onMultiWalletSellClick(userWalletId: UserWalletId) {
// TODO [REDACTED_JIRA]
appRouter.push(route = AppRoute.SellCrypto(userWalletId = userWalletId))
}
override fun onMultiWalletSwapClick(userWalletId: UserWalletId) {
// TODO [REDACTED_JIRA]
appRouter.push(route = AppRoute.SwapCrypto(userWalletId = userWalletId))
}
override fun onMultiWalletBuyClick(userWalletId: UserWalletId) {
// TODO [REDACTED_JIRA]
appRouter.push(route = AppRoute.BuyCrypto(userWalletId = userWalletId))
}
private fun openExplorer() {
val userWalletId = stateHolder.getSelectedWalletId()

View file

@ -62,10 +62,6 @@ internal interface WalletWarningsClickIntents {
fun onCloseRingPromoClick()
fun onTravalaPromoClick(link: String?)
fun onCloseTravalaPromoClick()
fun onSupportClick()
fun onNoteMigrationButtonClick(url: String)
@ -89,7 +85,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
private val shouldShowRingPromoUseCase: ShouldShowRingPromoUseCase,
private val shouldShowTravalaPromoWalletUseCase: ShouldShowTravalaPromoWalletUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
@ -261,34 +256,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
}
override fun onTravalaPromoClick(link: String?) {
analyticsEventHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Main,
programName = TokenSwapPromoAnalyticsEvent.ProgramName.Travala,
action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Clicked,
),
)
link?.let {
viewModelScope.launch(dispatchers.main) {
router.openUrl(link)
}
}
}
override fun onCloseTravalaPromoClick() {
analyticsEventHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Main,
programName = TokenSwapPromoAnalyticsEvent.ProgramName.Travala,
action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
),
)
viewModelScope.launch(dispatchers.main) {
shouldShowTravalaPromoWalletUseCase.neverToShow()
}
}
override fun onSupportClick() {
val scanResponse = getSelectedUserWallet()?.scanResponse ?: return
val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return