From 9de7841edda0859b0b83eb257829d7163137d724 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 Sep 2024 11:05:02 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../middlewares/TradeCryptoMiddleware.kt | 18 -- .../common/ui/tokens/TokenActionsUtils.kt | 69 ++++++ .../bottomsheets/TangemBottomSheetConfig.kt | 7 +- .../core/ui/components/token/TokenItem.kt | 48 +++-- .../model/ScenarioUnavailabilityReason.kt | 0 .../tokens/GetCryptoCurrencyActionsUseCase.kt | 17 +- .../domain/tokens/legacy/TradeCryptoAction.kt | 10 +- .../domain/tokens/model/TokenActionsState.kt | 6 +- features/markets/impl/build.gradle.kts | 7 + .../impl/model/MarketsTokenDetailsModel.kt | 15 +- .../impl/model/formatter/Formatters.kt | 11 - .../portfolio/impl/loader/PortfolioData.kt | 13 +- .../impl/loader/PortfolioDataLoader.kt | 40 +++- .../model/AddToPortfolioBSContentUMFactory.kt | 4 +- .../impl/model/MarketsPortfolioModel.kt | 42 +++- ...oUMMFactory.kt => MyPortfolioUMFactory.kt} | 38 ++-- .../impl/model/PortfolioTokenUMConverter.kt | 72 ++++++- .../impl/model/TokenActionsHandler.kt | 199 ++++++++++++++++++ .../impl/model/TokensPortfolioUMConverter.kt | 20 +- .../impl/ui/AddToPortfolioBottomSheet.kt | 19 +- .../markets/portfolio/impl/ui/MyPortfolio.kt | 10 +- .../portfolio/impl/ui/PortfolioItem.kt | 11 +- .../impl/ui/PortfolioQuickActions.kt | 73 ++++--- .../impl/ui/TokenActionsBottomSheet.kt | 18 +- .../PreviewAddToPortfolioBSContentProvider.kt | 15 +- .../preview/PreviewMyPortfolioUMProvider.kt | 39 ++-- .../ui/state/AddToPortfolioBSContentUM.kt | 1 + .../portfolio/impl/ui/state/MyPortfolioUM.kt | 6 +- .../impl/ui/state/PortfolioTokenUM.kt | 12 +- .../portfolio/impl/ui/state/QuickActionUM.kt | 2 + .../impl/ui/state/TokenActionsBSContentUM.kt | 40 +++- .../converters/MarketsTokenItemConverter.kt | 12 +- features/tokendetails/impl/build.gradle.kts | 1 + .../state/factory/TokenDetailsStateFactory.kt | 66 +----- features/wallet/impl/build.gradle.kts | 2 +- .../wallet/presentation/WalletFragment.kt | 6 + .../MultiWalletCurrencyActionsConverter.kt | 2 +- .../WalletCurrencyActionsClickIntents.kt | 88 +------- 38 files changed, 687 insertions(+), 372 deletions(-) create mode 100644 common/ui/src/main/java/com/tangem/common/ui/tokens/TokenActionsUtils.kt rename domain/tokens/{src/main/kotlin => models/src/main/java}/com/tangem/domain/tokens/model/ScenarioUnavailabilityReason.kt (100%) rename features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/{MyPortfolioUMMFactory.kt => MyPortfolioUMFactory.kt} (76%) create mode 100644 features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 9f5d8aecb2..1abdf861b9 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -5,7 +5,6 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.Analytics -import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkAddress @@ -48,11 +47,6 @@ object TradeCryptoMiddleware { is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId) is TradeCryptoAction.Buy -> proceedBuyAction(state, action) is TradeCryptoAction.Sell -> proceedSellAction(action) - is TradeCryptoAction.Stake -> openStaking( - userWalletId = action.userWalletId, - cryptoCurrencyId = action.cryptoCurrencyId, - yield = action.yield, - ) is TradeCryptoAction.SendToken -> handleNewSendToken(action = action) is TradeCryptoAction.SendCoin -> handleNewSendCoin(action = action) } @@ -140,18 +134,6 @@ object TradeCryptoMiddleware { )?.let { store.dispatchOpenUrl(it) } } - private fun openStaking(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID, yield: Yield) { - store.dispatchNavigationAction { - push( - AppRoute.Staking( - userWalletId = userWalletId, - cryptoCurrencyId = cryptoCurrencyId, - yield = yield, - ), - ) - } - } - private fun handleNewSendToken(action: TradeCryptoAction.SendToken) { handleNewSend( userWalletId = action.userWallet.walletId, diff --git a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenActionsUtils.kt b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenActionsUtils.kt new file mode 100644 index 0000000000..d33e9330f1 --- /dev/null +++ b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenActionsUtils.kt @@ -0,0 +1,69 @@ +package com.tangem.common.ui.tokens + +import com.tangem.common.ui.R +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason + +fun ScenarioUnavailabilityReason.getUnavailabilityReasonText(): TextReference { + return when (val unavailabilityReason = this) { + is ScenarioUnavailabilityReason.StakingUnavailable -> { + resourceReference( + id = R.string.token_button_unavailability_reason_staking_unavailable, + formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), + ) + } + is ScenarioUnavailabilityReason.PendingTransaction -> { + when (unavailabilityReason.withdrawalScenario) { + ScenarioUnavailabilityReason.WithdrawalScenario.SEND -> resourceReference( + id = R.string.token_button_unavailability_reason_pending_transaction_send, + formatArgs = wrappedList(unavailabilityReason.networkName), + ) + ScenarioUnavailabilityReason.WithdrawalScenario.SELL -> resourceReference( + id = R.string.token_button_unavailability_reason_pending_transaction_sell, + formatArgs = wrappedList(unavailabilityReason.networkName), + ) + } + } + is ScenarioUnavailabilityReason.EmptyBalance -> { + when (unavailabilityReason.withdrawalScenario) { + ScenarioUnavailabilityReason.WithdrawalScenario.SEND -> resourceReference( + id = R.string.token_button_unavailability_reason_empty_balance_send, + ) + ScenarioUnavailabilityReason.WithdrawalScenario.SELL -> resourceReference( + id = R.string.token_button_unavailability_reason_empty_balance_sell, + ) + } + } + is ScenarioUnavailabilityReason.BuyUnavailable -> { + resourceReference( + id = R.string.token_button_unavailability_reason_buy_unavailable, + formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), + ) + } + is ScenarioUnavailabilityReason.NotExchangeable -> { + resourceReference( + id = R.string.token_button_unavailability_reason_not_exchangeable, + formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), + ) + } + is ScenarioUnavailabilityReason.NotSupportedBySellService -> { + resourceReference( + id = R.string.token_button_unavailability_reason_sell_unavailable, + formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), + ) + } + ScenarioUnavailabilityReason.Unreachable -> { + resourceReference( + id = R.string.token_button_unavailability_generic_description, + ) + } + ScenarioUnavailabilityReason.UnassociatedAsset -> resourceReference( + id = R.string.warning_receive_blocked_hedera_token_association_required_message, + ) + ScenarioUnavailabilityReason.None -> { + throw IllegalArgumentException("The unavailability reason must be other than None") + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt index 0bb0162ccc..d2eb1f9935 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt @@ -11,4 +11,9 @@ data class TangemBottomSheetConfig( val isShow: Boolean, val onDismissRequest: () -> Unit, val content: TangemBottomSheetConfigContent, -) \ No newline at end of file +) { + + companion object { + val Empty = TangemBottomSheetConfig(false, {}, TangemBottomSheetConfigContent.Empty) + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt index 436f507958..0a519a13e1 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt @@ -2,6 +2,7 @@ package com.tangem.core.ui.components.token import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -47,12 +48,18 @@ private enum class LayoutId { * >Figma Component */ @Composable -fun TokenItem(state: TokenItemState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) { +fun TokenItem( + state: TokenItemState, + isBalanceHidden: Boolean, + modifier: Modifier = Modifier, + itemPaddingValues: PaddingValues = PaddingValues(horizontal = TangemTheme.dimens.spacing12), +) { TokenItem( state = state, isBalanceHidden = isBalanceHidden, modifier = modifier, reorderableTokenListState = null, + itemPaddingValues = itemPaddingValues, ) } @@ -63,6 +70,7 @@ fun TokenItem(state: TokenItemState, isBalanceHidden: Boolean, modifier: Modifie * @param isBalanceHidden flag that shows/hides balance * @param reorderableTokenListState reorderable token list state * @param modifier modifier + * @param itemPaddingValues padding values * * @see Figma Component @@ -73,12 +81,15 @@ fun TokenItem( isBalanceHidden: Boolean, reorderableTokenListState: ReorderableLazyListState?, modifier: Modifier = Modifier, + itemPaddingValues: PaddingValues = PaddingValues(horizontal = TangemTheme.dimens.spacing12), ) { val betweenRowsMargin = TangemTheme.dimens.spacing2 CustomContainer( state = state, - modifier = modifier.tokenClickable(state = state), + modifier = modifier + .tokenClickable(state = state) + .padding(itemPaddingValues), ) { CurrencyIcon( state = state.iconState, @@ -163,10 +174,7 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier Layout(content = content, modifier = modifier) { measurables, constraints -> val layoutWidth = constraints.maxWidth - val horizontalPadding = with(density) { dimens.size12.roundToPx() } val verticalPadding = with(density) { dimens.size15.roundToPx() } - val layoutWidthWithoutPaddings = layoutWidth - 2 * horizontalPadding - val titleMinWidth = (layoutWidth * TITLE_MIN_WIDTH_COEFFICIENT).toInt() val priceChangeMinWidth = (layoutWidth * PRICE_MIN_WIDTH_COEFFICIENT).toInt() @@ -202,35 +210,35 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier -> { fiatAmount = measurables.measureFiatAmount( state = state, - maxWidth = layoutWidthWithoutPaddings - icon.width - titleMinWidth, + maxWidth = layoutWidth - icon.width - titleMinWidth, defaultConstraints = constraints, ) cryptoAmount = measurables.measureCryptoAmount( state = state, - maxWidth = layoutWidthWithoutPaddings - icon.width - priceChangeMinWidth, + maxWidth = layoutWidth - icon.width - priceChangeMinWidth, defaultConstraints = constraints, ) - firstRowRemainingFreeSpace = layoutWidthWithoutPaddings - icon.width - fiatAmount.width - secondRowRemainingFreeSpace = layoutWidthWithoutPaddings - icon.width - cryptoAmount.width + firstRowRemainingFreeSpace = layoutWidth - icon.width - fiatAmount.width + secondRowRemainingFreeSpace = layoutWidth - icon.width - cryptoAmount.width } is TokenItemState.Draggable -> { cryptoAmount = measurables.measureCryptoAmount( state = state, - maxWidth = layoutWidthWithoutPaddings - icon.width - nonFiatContent.width, + maxWidth = layoutWidth - icon.width - nonFiatContent.width, defaultConstraints = constraints, ) - firstRowRemainingFreeSpace = layoutWidthWithoutPaddings - icon.width - nonFiatContent.width + firstRowRemainingFreeSpace = layoutWidth - icon.width - nonFiatContent.width } is TokenItemState.NoAddress, is TokenItemState.Unreachable, -> { - firstRowRemainingFreeSpace = layoutWidthWithoutPaddings - icon.width - nonFiatContent.width + firstRowRemainingFreeSpace = layoutWidth - icon.width - nonFiatContent.width if (state.subtitleState != null) { - secondRowRemainingFreeSpace = layoutWidthWithoutPaddings - icon.width - nonFiatContent.width + secondRowRemainingFreeSpace = layoutWidth - icon.width - nonFiatContent.width } } } @@ -262,10 +270,10 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier ) layout(width = constraints.maxWidth, height = layoutHeight) { - icon.placeRelative(x = horizontalPadding, y = (layoutHeight - icon.height).div(other = 2)) + icon.placeRelative(x = 0, y = (layoutHeight - icon.height).div(other = 2)) title.placeRelative( - x = horizontalPadding + icon.width, + x = icon.width, y = when (state) { is TokenItemState.NoAddress, is TokenItemState.Unreachable, @@ -280,23 +288,23 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier }, ) - fiatAmount?.placeRelative(x = layoutWidth - fiatAmount.width - horizontalPadding, y = verticalPadding) + fiatAmount?.placeRelative(x = layoutWidth - fiatAmount.width, y = verticalPadding) priceChange?.placeRelative( - x = horizontalPadding + icon.width, + x = icon.width, y = layoutHeight - priceChange.height - verticalPadding, ) cryptoAmount?.placeRelative( x = when (state) { - is TokenItemState.Draggable -> horizontalPadding + icon.width - else -> layoutWidth - cryptoAmount.width - horizontalPadding + is TokenItemState.Draggable -> icon.width + else -> layoutWidth - cryptoAmount.width }, y = layoutHeight - cryptoAmount.height - verticalPadding, ) nonFiatContent.placeRelative( - x = layoutWidth - nonFiatContent.width - horizontalPadding, + x = layoutWidth - nonFiatContent.width, y = (layoutHeight - nonFiatContent.height).div(other = 2), ) } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/ScenarioUnavailabilityReason.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/ScenarioUnavailabilityReason.kt similarity index 100% rename from domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/ScenarioUnavailabilityReason.kt rename to domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/ScenarioUnavailabilityReason.kt diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt index c83af879ba..99ec404379 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt @@ -130,11 +130,24 @@ class GetCryptoCurrencyActionsUseCase( // staking if (stakingFeatureToggles.isStakingEnabled) { if (isStakingAvailable(cryptoCurrency)) { - activeList.add(TokenActionsState.ActionState.Stake(ScenarioUnavailabilityReason.None)) + val yield = kotlin.runCatching { + stakingRepository.getYield( + cryptoCurrencyId = cryptoCurrency.id, + symbol = cryptoCurrency.symbol, + ) + }.getOrNull() + + activeList.add( + TokenActionsState.ActionState.Stake( + unavailabilityReason = ScenarioUnavailabilityReason.None, + yield = yield, + ), + ) } else { disabledList.add( TokenActionsState.ActionState.Stake( unavailabilityReason = ScenarioUnavailabilityReason.StakingUnavailable(cryptoCurrency.name), + yield = null, ), ) } @@ -254,7 +267,7 @@ class GetCryptoCurrencyActionsUseCase( actionsList.add(TokenActionsState.ActionState.Receive(scenario)) } if (stakingFeatureToggles.isStakingEnabled) { - actionsList.add(TokenActionsState.ActionState.Stake(ScenarioUnavailabilityReason.Unreachable)) + actionsList.add(TokenActionsState.ActionState.Stake(ScenarioUnavailabilityReason.Unreachable, null)) } actionsList.add(TokenActionsState.ActionState.HideToken(ScenarioUnavailabilityReason.None)) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt index 706493a6ea..b32c7a091f 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt @@ -1,10 +1,8 @@ package com.tangem.domain.tokens.legacy -import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId import org.rekotlin.Action import java.math.BigDecimal @@ -24,6 +22,7 @@ sealed class TradeCryptoAction : Action { val appCurrencyCode: String, ) : TradeCryptoAction() + @Deprecated("Use AppRoute instead") data class SendToken( val userWallet: UserWallet, val tokenCurrency: CryptoCurrency.Token, @@ -33,6 +32,7 @@ sealed class TradeCryptoAction : Action { val transactionInfo: TransactionInfo? = null, ) : TradeCryptoAction() + @Deprecated("Use AppRoute instead") data class SendCoin( val userWallet: UserWallet, val coinStatus: CryptoCurrencyStatus, @@ -40,12 +40,6 @@ sealed class TradeCryptoAction : Action { val transactionInfo: TransactionInfo? = null, ) : TradeCryptoAction() - data class Stake( - val userWalletId: UserWalletId, - val cryptoCurrencyId: CryptoCurrency.ID, - val yield: Yield, - ) : TradeCryptoAction() - data class TransactionInfo( val transactionId: String, val destinationAddress: String, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt index a3dbea0959..c6db8e28e2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt @@ -1,5 +1,6 @@ package com.tangem.domain.tokens.model +import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.wallets.models.UserWalletId data class TokenActionsState( @@ -20,7 +21,10 @@ data class TokenActionsState( data class Receive(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState() - data class Stake(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState() + data class Stake( + override val unavailabilityReason: ScenarioUnavailabilityReason, + val yield: Yield?, + ) : ActionState() data class Swap(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState() diff --git a/features/markets/impl/build.gradle.kts b/features/markets/impl/build.gradle.kts index 3fc641b0da..8cccec2563 100644 --- a/features/markets/impl/build.gradle.kts +++ b/features/markets/impl/build.gradle.kts @@ -29,6 +29,13 @@ dependencies { implementation(projects.domain.wallets) implementation(projects.domain.wallets.models) + // FIXME [REDACTED_TASK_KEY] + // Remove the "Buy" and "Sell" actions from the redux middleware. + // Instead, create some kind of interface for such cases. + /* Redux -_- */ + implementation(projects.domain.legacy) + implementation(deps.reKotlin) + /* Compose */ implementation(deps.compose.coil) implementation(deps.compose.foundation) diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt index e899cfeb23..83b17519eb 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt @@ -346,9 +346,7 @@ internal class MarketsTokenDetailsModel @Inject constructor( chartDataProducer.runTransaction { updateLook { - it.copy( - type = getChartTypeByPercent(percent), - ) + it.copy(type = percent.percentChangeType().toChartType()) } } } @@ -356,13 +354,12 @@ internal class MarketsTokenDetailsModel @Inject constructor( private suspend fun updateQuotes(newQuotes: TokenQuotes) { quotesStateUpdater.updateQuotes(newQuotes) - val percent = newQuotes.getPercentByInterval(interval = state.value.selectedInterval) + val percent = newQuotes + .getPercentByInterval(interval = state.value.selectedInterval) chartDataProducer.runTransaction { updateLook { - it.copy( - type = getChartTypeByPercent(percent), - ) + it.copy(type = percent.percentChangeType().toChartType()) } } } @@ -430,7 +427,7 @@ internal class MarketsTokenDetailsModel @Inject constructor( chartDataProducer.runTransaction { updateLook { it.copy( - type = getChartTypeByPercent(percent), + type = percent.percentChangeType().toChartType(), ) } } @@ -477,7 +474,7 @@ internal class MarketsTokenDetailsModel @Inject constructor( launch { while (true) { delay(timeMillis) - // Update quotes only when content visible on the screen + // Update quotes only when content is visible on the screen isVisibleOnScreen.first { it } loadQuotes() diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/formatter/Formatters.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/formatter/Formatters.kt index c4cf1f6e3f..d2f2869872 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/formatter/Formatters.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/formatter/Formatters.kt @@ -87,15 +87,4 @@ internal fun PriceChangeType.toChartType(): MarketChartLook.Type { PriceChangeType.DOWN -> MarketChartLook.Type.Falling PriceChangeType.NEUTRAL -> MarketChartLook.Type.Neutral } -} - -@Suppress("MagicNumber") -internal fun getChartTypeByPercent(percent: BigDecimal?): MarketChartLook.Type { - val scaled = percent?.setScale(4, RoundingMode.HALF_UP) - return when { - scaled == null -> return MarketChartLook.Type.Neutral - scaled > BigDecimal.ZERO -> MarketChartLook.Type.Growing - scaled < BigDecimal.ZERO -> MarketChartLook.Type.Falling - else -> MarketChartLook.Type.Neutral - } } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioData.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioData.kt index 8ac05a4e90..ece4a0a217 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioData.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioData.kt @@ -4,6 +4,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.core.lce.Lce import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.tokens.model.TotalFiatBalance import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId @@ -11,7 +12,7 @@ import com.tangem.domain.wallets.models.UserWalletId /** * Portfolio data. Combined data from all flows that required to setup portfolio * - * @property walletsWithCurrencyStatuses wallets with crypto currency statuses + * @property walletsWithCurrencies wallets with crypto currency statuses * @property appCurrency app currency * @property isBalanceHidden flag that indicates if balance should be hidden * @property walletsWithBalance wallets with total balance @@ -19,8 +20,14 @@ import com.tangem.domain.wallets.models.UserWalletId [REDACTED_AUTHOR] */ internal data class PortfolioData( - val walletsWithCurrencyStatuses: Map>, + val walletsWithCurrencies: Map>, val appCurrency: AppCurrency, val isBalanceHidden: Boolean, val walletsWithBalance: Map>, -) \ No newline at end of file +) { + data class CryptoCurrencyData( + val userWallet: UserWallet, + val status: CryptoCurrencyStatus, + val actions: List, + ) +} \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioDataLoader.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioDataLoader.kt index e2e08f4b70..0685abffb7 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioDataLoader.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioDataLoader.kt @@ -6,9 +6,9 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.core.lce.Lce import com.tangem.domain.tokens.GetAllWalletsCryptoCurrencyStatusesUseCase +import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase import com.tangem.domain.tokens.GetWalletTotalBalanceUseCase import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TotalFiatBalance import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId @@ -32,18 +32,19 @@ internal class PortfolioDataLoader @Inject constructor( private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val getWalletTotalBalanceUseCase: GetWalletTotalBalanceUseCase, + private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase, ) { /** Load data by [currencyRawId] */ @OptIn(ExperimentalCoroutinesApi::class) fun load(currencyRawId: String): Flow { return combine( - flow = getAllWalletsCryptoCurrencyStatusesFlow(currencyRawId = currencyRawId), + flow = getAllWalletsCryptoCurrenciesData(currencyRawId = currencyRawId), flow2 = getSelectedAppCurrencyFlow(), flow3 = getBalanceHidingSettingsFlow(), - ) { walletsWithCurrencyStatuses, appCurrency, isBalanceHidden -> + ) { walletsWithCurrencies, appCurrency, isBalanceHidden -> PortfolioData( - walletsWithCurrencyStatuses = walletsWithCurrencyStatuses, + walletsWithCurrencies = walletsWithCurrencies, appCurrency = appCurrency, isBalanceHidden = isBalanceHidden, walletsWithBalance = emptyMap(), @@ -52,15 +53,16 @@ internal class PortfolioDataLoader @Inject constructor( // setup balances for wallets from walletsWithCurrencyStatuses .flatMapLatest { portfolioData -> getWalletsWithTotalBalanceFlow( - ids = portfolioData.walletsWithCurrencyStatuses.keys.map(UserWallet::walletId), + ids = portfolioData.walletsWithCurrencies.keys.map(UserWallet::walletId), ) .map { portfolioData.copy(walletsWithBalance = it) } } } - private fun getAllWalletsCryptoCurrencyStatusesFlow( + @OptIn(ExperimentalCoroutinesApi::class) + private fun getAllWalletsCryptoCurrenciesData( currencyRawId: String, - ): Flow>> { + ): Flow>> { return getAllWalletsCryptoCurrencyStatusesUseCase(currencyRawId) .distinctUntilChanged() .map { walletsWithMaybeStatuses -> @@ -68,6 +70,30 @@ internal class PortfolioDataLoader @Inject constructor( entry.value.mapNotNull { it.getOrNull() } } } + .flatMapLatest { walletsWithStatuses -> + val actionsFlows = walletsWithStatuses.flatMap { (wallet, statuses) -> + statuses.map { status -> + getCryptoCurrencyActionsUseCase(wallet, status) + .map { + PortfolioData.CryptoCurrencyData( + userWallet = wallet, + status = status, + actions = it.states, + ) + } + } + } + + combine(actionsFlows) { actions -> + walletsWithStatuses.mapValues { entry -> + entry.value.mapNotNull { status -> + actions.firstOrNull { + it.userWallet == entry.key && it.status == status + } + } + } + } + } } private fun getSelectedAppCurrencyFlow(): Flow { diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/AddToPortfolioBSContentUMFactory.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/AddToPortfolioBSContentUMFactory.kt index 6b8140850f..e88ef7d966 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/AddToPortfolioBSContentUMFactory.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/AddToPortfolioBSContentUMFactory.kt @@ -66,6 +66,8 @@ internal class AddToPortfolioBSContentUMFactory( portfolioData = portfolioData, selectedWalletId = selectedWallet.walletId, ), + // TODO maybe do not show wallets with already added token + isWalletBlockVisible = portfolioData.walletsWithBalance.size > 1, ), ) } @@ -86,7 +88,7 @@ internal class AddToPortfolioBSContentUMFactory( isShow = isShow, onDismissRequest = { onWalletSelectorVisibilityChange(false) }, content = WalletSelectorBSContentUM( - userWallets = portfolioData.walletsWithCurrencyStatuses + userWallets = portfolioData.walletsWithCurrencies .filterKeys(UserWallet::isMultiCurrency) .map { it.key } .map { userWallet -> diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt index 5d647ae942..891fc3b8aa 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt @@ -6,6 +6,8 @@ import com.tangem.core.decompose.di.ComponentScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.domain.card.HasMissedDerivationsUseCase +import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.markets.TokenMarketInfo import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network @@ -14,20 +16,24 @@ import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase import com.tangem.features.markets.portfolio.api.MarketsPortfolioComponent import com.tangem.features.markets.portfolio.impl.loader.PortfolioDataLoader import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM +import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.* import timber.log.Timber import javax.inject.Inject +@Suppress("LongParameterList") @Stable @ComponentScoped internal class MarketsPortfolioModel @Inject constructor( paramsContainer: ParamsContainer, + override val dispatchers: CoroutineDispatcherProvider, private val getSelectedWalletUseCase: GetSelectedWalletUseCase, + private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val portfolioDataLoader: PortfolioDataLoader, private val hasMissedDerivationsUseCase: HasMissedDerivationsUseCase, - override val dispatchers: CoroutineDispatcherProvider, + private val tokenActionsIntentsFactory: TokenActionsHandler.Factory, ) : Model() { val state: StateFlow get() = _state @@ -45,7 +51,17 @@ internal class MarketsPortfolioModel @Inject constructor( private val portfolioBSVisibilityModelFlow = MutableStateFlow(value = PortfolioBSVisibilityModel()) - private val factory = MyPortfolioUMMFactory( + private val currentAppCurrency = getSelectedAppCurrencyUseCase() + .map { maybeAppCurrency -> + maybeAppCurrency.getOrElse { AppCurrency.Default } + } + .stateIn( + scope = modelScope, + started = SharingStarted.Eagerly, + initialValue = AppCurrency.Default, + ) + + private val factory = MyPortfolioUMFactory( onAddClick = { onAddToPortfolioBSVisibilityChange(isShow = true) }, onTokenItemClick = ::onTokenItemClick, addToPortfolioBSContentUMFactory = AddToPortfolioBSContentUMFactory( @@ -56,6 +72,15 @@ internal class MarketsPortfolioModel @Inject constructor( onWalletSelect = ::onWalletSelect, onContinueClick = ::onContinueClick, ), + tokenActionsHandler = tokenActionsIntentsFactory.create( + currentAppCurrency = Provider { currentAppCurrency.value }, + updateTokenActionsBSConfig = { updateBlock -> + updateTokensState { it.copy(tokenActionsBSConfig = updateBlock(it.tokenActionsBSConfig)) } + }, + updateTokenReceiveBSConfig = { updateBlock -> + updateTokensState { it.copy(tokenReceiveBSConfig = updateBlock(it.tokenReceiveBSConfig)) } + }, + ), ) init { @@ -128,9 +153,7 @@ internal class MarketsPortfolioModel @Inject constructor( } private fun onTokenItemClick(index: Int, id: CryptoCurrency.ID) { - _state.update { - val state = _state.value as? MyPortfolioUM.Tokens ?: return@update it - + updateTokensState { state -> state.copy( tokens = state.tokens .mapIndexed { i, tokenUM -> @@ -138,7 +161,7 @@ internal class MarketsPortfolioModel @Inject constructor( if (index == i && id.value == tokenUM.tokenItemState.id) { tokenUM.copy(isQuickActionsShown = !tokenUM.isQuickActionsShown) } else { - tokenUM + tokenUM.copy(isQuickActionsShown = false) } } .toImmutableList(), @@ -194,4 +217,11 @@ internal class MarketsPortfolioModel @Inject constructor( it.copy(addToPortfolioBSVisibility = true, walletSelectorBSVisibility = isShow) } } + + private fun updateTokensState(block: (MyPortfolioUM.Tokens) -> MyPortfolioUM) { + _state.update { stateToUpdate -> + val tokensState = stateToUpdate as? MyPortfolioUM.Tokens ?: return@update stateToUpdate + block(tokensState) + } + } } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MyPortfolioUMMFactory.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MyPortfolioUMFactory.kt similarity index 76% rename from features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MyPortfolioUMMFactory.kt rename to features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MyPortfolioUMFactory.kt index 109e54ad83..fb632b9acc 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MyPortfolioUMMFactory.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MyPortfolioUMFactory.kt @@ -16,10 +16,11 @@ import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM * [REDACTED_AUTHOR] */ -internal class MyPortfolioUMMFactory( +internal class MyPortfolioUMFactory( private val onAddClick: () -> Unit, private val onTokenItemClick: (Int, CryptoCurrency.ID) -> Unit, private val addToPortfolioBSContentUMFactory: AddToPortfolioBSContentUMFactory, + private val tokenActionsHandler: TokenActionsHandler, ) { fun create( @@ -31,16 +32,16 @@ internal class MyPortfolioUMMFactory( if (availableNetworks.isEmpty()) return MyPortfolioUM.Unavailable - val walletsWithStatuses = portfolioData.walletsWithCurrencyStatuses + val walletsWithCurrencies = portfolioData.walletsWithCurrencies .filterAvailableNetworks(networks = availableNetworks) - val isPortfolioEmpty = walletsWithStatuses.flatMap { it.value }.isEmpty() + val isPortfolioEmpty = walletsWithCurrencies.flatMap { it.value }.isEmpty() if (isPortfolioEmpty) { - val hasMultiWallets = walletsWithStatuses.filterKeys(UserWallet::isMultiCurrency).isNotEmpty() + val hasMultiWallets = walletsWithCurrencies.filterKeys(UserWallet::isMultiCurrency).isNotEmpty() return if (hasMultiWallets) { MyPortfolioUM.AddFirstToken( - bsConfig = createAddToPortfolioBSConfig( + addToPortfolioBSConfig = createAddToPortfolioBSConfig( portfolioData = portfolioData, portfolioUIData = portfolioUIData, availableNetworks = availableNetworks, @@ -55,7 +56,7 @@ internal class MyPortfolioUMMFactory( return TokensPortfolioUMConverter( appCurrency = portfolioData.appCurrency, isBalanceHidden = portfolioData.isBalanceHidden, - isAllAvailableNetworksAdded = walletsWithStatuses.isAllAvailableNetworksAdded(availableNetworks), + isAllAvailableNetworksAdded = walletsWithCurrencies.isAllAvailableNetworksAdded(availableNetworks), bsConfig = createAddToPortfolioBSConfig( portfolioData = portfolioData, portfolioUIData = portfolioUIData, @@ -63,8 +64,9 @@ internal class MyPortfolioUMMFactory( ), onAddClick = onAddClick, onTokenItemClick = onTokenItemClick, + quickActionsIntents = tokenActionsHandler, ) - .convert(walletsWithStatuses) + .convert(walletsWithCurrencies) } private fun createAddToPortfolioBSConfig( @@ -73,21 +75,21 @@ internal class MyPortfolioUMMFactory( availableNetworks: List, ): TangemBottomSheetConfig { val walletId = portfolioUIData.selectedWalletId - ?: portfolioData.walletsWithCurrencyStatuses.keys.firstOrNull { it.isMultiCurrency }?.walletId + ?: portfolioData.walletsWithCurrencies.keys.firstOrNull { it.isMultiCurrency }?.walletId - val selectedWallet = portfolioData.walletsWithCurrencyStatuses.keys + val selectedWallet = portfolioData.walletsWithCurrencies.keys .firstOrNull { it.walletId == walletId } ?: error("portfolioModel.walletsWithCurrencyStatuses doesn't contain selected wallet: $walletId") val changedNetworks = portfolioUIData.walletsWithChangedNetworks[portfolioUIData.selectedWalletId] val alreadyAddedNetworks = requireNotNull( - value = portfolioData.walletsWithCurrencyStatuses[selectedWallet], + value = portfolioData.walletsWithCurrencies[selectedWallet], lazyMessage = { "portfolioModel.walletsWithCurrencyStatuses doesn't contain selected wallet: $walletId" }, ) .filterAvailableNetworks(availableNetworks) - .map { it.currency.network.backendId } + .map { it.status.currency.network.backendId } return addToPortfolioBSContentUMFactory.create( portfolioData = portfolioData, @@ -117,7 +119,7 @@ internal class MyPortfolioUMMFactory( } } - private fun Map>.isAllAvailableNetworksAdded( + private fun Map>.isAllAvailableNetworksAdded( availableNetworks: List, ): Boolean { val networkIds = availableNetworks.map { it.networkId } @@ -125,26 +127,26 @@ internal class MyPortfolioUMMFactory( return this // User can add currencies only in multi-currency wallets .filterKeys(UserWallet::isMultiCurrency) - .mapValues { it.value.map { it.currency.network.backendId } } + .mapValues { entry -> entry.value.map { it.status.currency.network.backendId } } // Each wallets contains all available networks? .all { it.value.containsAll(networkIds) } } /** Filter map values by available networks [networks] */ - private fun Map>.filterAvailableNetworks( + private fun Map>.filterAvailableNetworks( networks: List, - ): Map> { + ): Map> { return mapValues { entry -> entry.value.filterAvailableNetworks(networks) } } /** Filter list of [CryptoCurrencyStatus] by available networks [networks] */ - private fun List.filterAvailableNetworks( + private fun List.filterAvailableNetworks( networks: List, - ): List { + ): List { val networkIds = networks.map(TokenMarketInfo.Network::networkId) return mapNotNull { - it.takeIf { networkIds.contains(it.currency.network.backendId) } + it.takeIf { networkIds.contains(it.status.currency.network.backendId) } } } } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt index 5e05440a85..cf437c80c1 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt @@ -4,9 +4,15 @@ import com.tangem.common.ui.tokens.TokenItemStateConverter import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason +import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.wallets.models.UserWallet +import com.tangem.features.markets.portfolio.impl.loader.PortfolioData import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioTokenUM +import com.tangem.features.markets.portfolio.impl.ui.state.QuickActionUM +import com.tangem.features.markets.portfolio.impl.ui.state.TokenActionsBSContentUM import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.toImmutableList /** * Converter from [UserWallet] and [CryptoCurrencyStatus] to [PortfolioTokenUM] @@ -17,27 +23,75 @@ internal class PortfolioTokenUMConverter( private val appCurrency: AppCurrency, private val isBalanceHidden: Boolean, private val onTokenItemClick: (CryptoCurrencyStatus) -> Unit, -) : Converter, PortfolioTokenUM> { - - override fun convert(value: Pair): PortfolioTokenUM { - val (userWallet, cryptoCurrencyStatus) = value + private val tokenActionsHandler: TokenActionsHandler, +) : Converter { + override fun convert(value: PortfolioData.CryptoCurrencyData): PortfolioTokenUM { val tokenItemStateConverter = TokenItemStateConverter( appCurrency = appCurrency, - titleStateProvider = { TokenItemState.TitleState.Content(text = userWallet.name) }, + titleStateProvider = { TokenItemState.TitleState.Content(text = value.userWallet.name) }, subtitleStateProvider = { - TokenItemState.SubtitleState.TextContent(value = cryptoCurrencyStatus.currency.name) + TokenItemState.SubtitleState.TextContent(value = value.status.currency.name) }, onItemClick = onTokenItemClick, - onItemLongClick = null, + onItemLongClick = { + tokenActionsHandler.onTokenLongClick(cryptoCurrencyData = value) + }, ) return PortfolioTokenUM( - tokenItemState = tokenItemStateConverter.convert(value = cryptoCurrencyStatus), + tokenItemState = tokenItemStateConverter.convert(value = value.status), isBalanceHidden = isBalanceHidden, isQuickActionsShown = false, + quickActions = quickActions(cryptoData = value), + ) + } + + private fun quickActions(cryptoData: PortfolioData.CryptoCurrencyData): PortfolioTokenUM.QuickActions { + return PortfolioTokenUM.QuickActions( + actions = listOfNotNull( + QuickActionUM.Buy.takeIf { + cryptoData.actions.any { + it is TokenActionsState.ActionState.Buy && + it.unavailabilityReason == ScenarioUnavailabilityReason.None + } + }, + QuickActionUM.Exchange.takeIf { + cryptoData.actions.any { + it is TokenActionsState.ActionState.Swap && + it.unavailabilityReason == ScenarioUnavailabilityReason.None + } + }, + QuickActionUM.Receive.takeIf { + cryptoData.actions.any { + it is TokenActionsState.ActionState.Receive && + it.unavailabilityReason == ScenarioUnavailabilityReason.None + } + }, + ).toImmutableList(), onQuickActionClick = { - // TODO: [REDACTED_JIRA] + when (it) { + QuickActionUM.Buy -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.Buy, + cryptoCurrencyData = cryptoData, + ) + QuickActionUM.Exchange -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.Exchange, + cryptoCurrencyData = cryptoData, + ) + QuickActionUM.Receive -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.Receive, + cryptoCurrencyData = cryptoData, + ) + } + }, + onQuickActionLongClick = { + if (it == QuickActionUM.Receive) { + tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.CopyAddress, + cryptoCurrencyData = cryptoData, + ) + } }, ) } diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt new file mode 100644 index 0000000000..8dea5f71b6 --- /dev/null +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt @@ -0,0 +1,199 @@ +package com.tangem.features.markets.portfolio.impl.model + +import com.tangem.common.routing.AppRoute +import com.tangem.core.decompose.di.ComponentScoped +import com.tangem.core.decompose.navigation.Router +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.ui.clipboard.ClipboardManager +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.message.SnackbarMessage +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.redux.ReduxStateHolder +import com.tangem.domain.tokens.legacy.TradeCryptoAction +import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason +import com.tangem.domain.tokens.model.TokenActionsState +import com.tangem.features.markets.impl.R +import com.tangem.features.markets.portfolio.impl.loader.PortfolioData +import com.tangem.features.markets.portfolio.impl.ui.state.TokenActionsBSContentUM +import com.tangem.utils.Provider +import com.tangem.utils.isNullOrZero +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.collections.immutable.toImmutableList + +@Suppress("LongParameterList") +@ComponentScoped +internal class TokenActionsHandler @AssistedInject constructor( + private val router: Router, + private val clipboardManager: ClipboardManager, + private val uiMessageSender: UiMessageSender, + private val reduxStateHolder: ReduxStateHolder, + @Assisted + private val currentAppCurrency: Provider, + @Assisted("updateTokenReceiveBSConfig") + private val updateTokenReceiveBSConfig: ((TangemBottomSheetConfig) -> TangemBottomSheetConfig) -> Unit, + @Assisted("updateTokenActionsBSConfig") + private val updateTokenActionsBSConfig: ((TangemBottomSheetConfig) -> TangemBottomSheetConfig) -> Unit, +) { + + fun handle(action: TokenActionsBSContentUM.Action, cryptoCurrencyData: PortfolioData.CryptoCurrencyData) { + when (action) { + TokenActionsBSContentUM.Action.Buy -> onBuyClick(cryptoCurrencyData) + TokenActionsBSContentUM.Action.Exchange -> onExchangeClick(cryptoCurrencyData) + TokenActionsBSContentUM.Action.Receive -> onReceiveClick(cryptoCurrencyData) + TokenActionsBSContentUM.Action.CopyAddress -> onCopyAddress(cryptoCurrencyData) + TokenActionsBSContentUM.Action.Sell -> onSellClick(cryptoCurrencyData) + TokenActionsBSContentUM.Action.Send -> onSendClick(cryptoCurrencyData) + TokenActionsBSContentUM.Action.Stake -> onStakeClick(cryptoCurrencyData) + } + } + + fun onTokenLongClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) { + updateTokenActionsBSConfig { + TangemBottomSheetConfig( + isShow = true, + onDismissRequest = { + updateTokenActionsBSConfig { + it.copy(isShow = false) + } + }, + content = TokenActionsBSContentUM( + title = cryptoCurrencyData.userWallet.name, + actions = cryptoCurrencyData.actions + .mapNotNull { it.toAction(cryptoCurrencyData) } + .sortedBy { it.order } + .toImmutableList(), + onActionClick = { action -> + handle(action, cryptoCurrencyData) + updateTokenActionsBSConfig { + it.copy(isShow = false) + } + }, + ), + ) + } + } + + private fun onReceiveClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) { + val cryptoCurrencyStatus = cryptoCurrencyData.status + val currency = cryptoCurrencyStatus.currency + val networkAddress = cryptoCurrencyStatus.value.networkAddress ?: return + + updateTokenReceiveBSConfig { + TangemBottomSheetConfig( + isShow = true, + onDismissRequest = { + updateTokenReceiveBSConfig { + it.copy(isShow = false) + } + }, + content = TokenReceiveBottomSheetConfig( + name = currency.name, + symbol = currency.symbol, + network = currency.network.name, + addresses = networkAddress.availableAddresses + .mapToAddressModels(currency) + .toImmutableList(), + onCopyClick = {}, + onShareClick = {}, + ), + ) + } + } + + private fun onCopyAddress(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) { + val cryptoCurrencyStatus = cryptoCurrencyData.status + val networkAddress = cryptoCurrencyStatus.value.networkAddress ?: return + val addresses = networkAddress.availableAddresses + .mapToAddressModels(cryptoCurrencyStatus.currency) + .toImmutableList() + val defaultAddress = addresses.firstOrNull()?.value ?: return + + clipboardManager.setText(text = defaultAddress) + uiMessageSender.send(SnackbarMessage(resourceReference(R.string.wallet_notification_address_copied))) + } + + private fun onBuyClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) { + reduxStateHolder.dispatch( + TradeCryptoAction.Buy( + userWallet = cryptoCurrencyData.userWallet, + cryptoCurrencyStatus = cryptoCurrencyData.status, + appCurrencyCode = currentAppCurrency().code, + ), + ) + } + + private fun onSellClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) { + reduxStateHolder.dispatch( + TradeCryptoAction.Sell( + cryptoCurrencyStatus = cryptoCurrencyData.status, + appCurrencyCode = currentAppCurrency().code, + ), + ) + } + + private fun onExchangeClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) { + router.push( + AppRoute.Swap( + currency = cryptoCurrencyData.status.currency, + userWalletId = cryptoCurrencyData.userWallet.walletId, + ), + ) + } + + private fun onSendClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) { + router.push( + AppRoute.Send( + userWalletId = cryptoCurrencyData.userWallet.walletId, + currency = cryptoCurrencyData.status.currency, + ), + ) + } + + private fun onStakeClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) { + val yield = cryptoCurrencyData.actions.firstOrNull { it is TokenActionsState.ActionState.Stake } + ?.let { it as TokenActionsState.ActionState.Stake } + ?.yield ?: return + + router.push( + AppRoute.Staking( + userWalletId = cryptoCurrencyData.userWallet.walletId, + cryptoCurrencyId = cryptoCurrencyData.status.currency.id, + yield = yield, + ), + ) + } + + private fun TokenActionsState.ActionState.toAction( + cryptoCurrencyData: PortfolioData.CryptoCurrencyData, + ): TokenActionsBSContentUM.Action? = when (this) { + is TokenActionsState.ActionState.Buy -> TokenActionsBSContentUM.Action.Buy + is TokenActionsState.ActionState.CopyAddress -> TokenActionsBSContentUM.Action.CopyAddress + is TokenActionsState.ActionState.HideToken -> null + is TokenActionsState.ActionState.Receive -> TokenActionsBSContentUM.Action.Receive + is TokenActionsState.ActionState.Sell -> TokenActionsBSContentUM.Action.Sell + is TokenActionsState.ActionState.Send -> { + TokenActionsBSContentUM.Action.Send.takeIf { + cryptoCurrencyData.status.value.amount.isNullOrZero().not() + } + } + is TokenActionsState.ActionState.Stake -> TokenActionsBSContentUM.Action.Stake + is TokenActionsState.ActionState.Swap -> TokenActionsBSContentUM.Action.Exchange + } + .takeIf { this.unavailabilityReason == ScenarioUnavailabilityReason.None } + + @AssistedFactory + interface Factory { + fun create( + currentAppCurrency: Provider, + @Assisted("updateTokenReceiveBSConfig") + updateTokenReceiveBSConfig: ((TangemBottomSheetConfig) -> TangemBottomSheetConfig) -> Unit, + @Assisted("updateTokenActionsBSConfig") + updateTokenActionsBSConfig: ((TangemBottomSheetConfig) -> TangemBottomSheetConfig) -> Unit, + ): TokenActionsHandler + } +} \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt index e9e61f5618..1614ad4ebb 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt @@ -5,6 +5,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet +import com.tangem.features.markets.portfolio.impl.loader.PortfolioData import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM.Tokens.AddButtonState import com.tangem.utils.converter.Converter @@ -15,6 +16,7 @@ import kotlinx.collections.immutable.toImmutableList * [REDACTED_AUTHOR] */ +@Suppress("LongParameterList") internal class TokensPortfolioUMConverter( private val appCurrency: AppCurrency, private val isBalanceHidden: Boolean, @@ -22,25 +24,27 @@ internal class TokensPortfolioUMConverter( private val bsConfig: TangemBottomSheetConfig, private val onAddClick: () -> Unit, private val onTokenItemClick: (index: Int, id: CryptoCurrency.ID) -> Unit, -) : Converter>, MyPortfolioUM.Tokens> { + private val quickActionsIntents: TokenActionsHandler, +) : Converter>, MyPortfolioUM.Tokens> { - override fun convert(value: Map>): MyPortfolioUM.Tokens { + override fun convert(value: Map>): MyPortfolioUM.Tokens { return MyPortfolioUM.Tokens( tokens = value - .flatMap { entry -> - entry.value.map { entry.key to it } - } - .mapIndexed { index, status -> + .flatMap { entry -> entry.value } + .mapIndexed { index, cryptoData -> PortfolioTokenUMConverter( appCurrency = appCurrency, isBalanceHidden = isBalanceHidden, onTokenItemClick = { onTokenItemClick(index, it.currency.id) }, - ).convert(status) + tokenActionsHandler = quickActionsIntents, + ).convert(cryptoData) } .toImmutableList(), buttonState = if (isAllAvailableNetworksAdded) AddButtonState.Unavailable else AddButtonState.Available, - bsConfig = bsConfig, + addToPortfolioBSConfig = bsConfig, onAddClick = onAddClick, + tokenReceiveBSConfig = TangemBottomSheetConfig.Empty, + tokenActionsBSConfig = TangemBottomSheetConfig.Empty, ) } } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt index 315718d001..f04e4206dc 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt @@ -73,15 +73,16 @@ private fun Content(state: AddToPortfolioBSContentUM, modifier: Modifier = Modif .verticalScroll(state = scrollState) .padding(horizontal = TangemTheme.dimens.spacing16), ) { - UserWalletItem( - state = state.selectedWallet, - blockColors = TangemBlockCardColors.copy( - containerColor = TangemTheme.colors.background.action, - disabledContainerColor = TangemTheme.colors.background.action, - ), - ) - - SpacerH12() + if (state.isWalletBlockVisible) { + UserWalletItem( + state = state.selectedWallet, + blockColors = TangemBlockCardColors.copy( + containerColor = TangemTheme.colors.background.action, + disabledContainerColor = TangemTheme.colors.background.action, + ), + ) + SpacerH12() + } NetworkSelection( modifier = Modifier.fillMaxWidth(), diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt index 8c3b6118fd..dda1777df3 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt @@ -16,6 +16,7 @@ import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.components.SmallButtonShimmer import com.tangem.core.ui.components.TextShimmer import com.tangem.core.ui.components.block.information.InformationBlock +import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet import com.tangem.core.ui.components.buttons.SecondarySmallButton import com.tangem.core.ui.components.buttons.SmallButtonConfig import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition @@ -24,8 +25,8 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.markets.impl.R import com.tangem.features.markets.portfolio.impl.ui.preview.PreviewMyPortfolioUMProvider -import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM.Tokens.AddButtonState +import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM @Composable internal fun MyPortfolio(state: MyPortfolioUM, modifier: Modifier = Modifier) { @@ -54,8 +55,8 @@ internal fun MyPortfolio(state: MyPortfolioUM, modifier: Modifier = Modifier) { } when (state) { - is MyPortfolioUM.AddFirstToken -> AddToPortfolioBottomSheet(config = state.bsConfig) - is MyPortfolioUM.Tokens -> AddToPortfolioBottomSheet(config = state.bsConfig) + is MyPortfolioUM.AddFirstToken -> AddToPortfolioBottomSheet(config = state.addToPortfolioBSConfig) + is MyPortfolioUM.Tokens -> AddToPortfolioBottomSheet(config = state.addToPortfolioBSConfig) else -> Unit } } @@ -105,6 +106,9 @@ private fun TokenList(state: MyPortfolioUM.Tokens, modifier: Modifier = Modifier } } } + + TokenReceiveBottomSheet(config = state.tokenReceiveBSConfig) + TokenActionsBottomSheet(config = state.tokenActionsBSConfig) } @Composable diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt index 2b781bd2a7..3b3ed01682 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt @@ -3,6 +3,7 @@ package com.tangem.features.markets.portfolio.impl.ui import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.runtime.* import androidx.compose.ui.Modifier @@ -25,11 +26,15 @@ internal fun PortfolioItem(state: PortfolioTokenUM, lastInList: Boolean, modifie state = state.tokenItemState, isBalanceHidden = state.isBalanceHidden, modifier = Modifier.background(color = TangemTheme.colors.background.action), + itemPaddingValues = PaddingValues( + start = TangemTheme.dimens.spacing10, + end = TangemTheme.dimens.spacing12, + ), ) PortfolioQuickActions( modifier = Modifier - .padding(horizontal = TangemTheme.dimens.spacing12) + .background(color = TangemTheme.colors.background.action) .padding( bottom = if (lastInList) { TangemTheme.dimens.spacing12 @@ -37,8 +42,10 @@ internal fun PortfolioItem(state: PortfolioTokenUM, lastInList: Boolean, modifie TangemTheme.dimens.spacing24 }, ), + actions = state.quickActions.actions, isVisible = state.isQuickActionsShown, - onActionClick = state.onQuickActionClick, + onActionClick = state.quickActions.onQuickActionClick, + onActionLongClick = state.quickActions.onQuickActionLongClick, ) } } diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioQuickActions.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioQuickActions.kt index f498942443..ba5ff99531 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioQuickActions.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioQuickActions.kt @@ -4,9 +4,7 @@ import android.content.res.Configuration import androidx.compose.animation.* import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Button @@ -15,7 +13,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalLayoutDirection @@ -23,6 +20,7 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastForEach import com.tangem.core.ui.components.SpacerH4 import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.haptic.TangemHapticEffect @@ -30,34 +28,33 @@ import com.tangem.core.ui.res.LocalHapticManager import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.markets.portfolio.impl.ui.state.QuickActionUM +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList @Composable internal fun PortfolioQuickActions( + actions: ImmutableList, isVisible: Boolean, onActionClick: (QuickActionUM) -> Unit, + onActionLongClick: (QuickActionUM) -> Unit, modifier: Modifier = Modifier, ) { + if (actions.isEmpty()) return + AnimatedVisibility( visible = isVisible, enter = expandVertically(expandFrom = Alignment.Top), exit = shrinkVertically(shrinkTowards = Alignment.Top), ) { Column(modifier = modifier) { - LineSeparator() - QuickActionItem( - state = QuickActionUM.Buy, - onClick = { onActionClick(QuickActionUM.Buy) }, - ) - LineSeparator() - QuickActionItem( - state = QuickActionUM.Exchange, - onClick = { onActionClick(QuickActionUM.Exchange) }, - ) - LineSeparator() - QuickActionItem( - state = QuickActionUM.Receive, - onClick = { onActionClick(QuickActionUM.Receive) }, - ) + actions.fastForEach { action -> + LineSeparator() + QuickActionItem( + state = action, + onClick = { onActionClick(action) }, + onLongClick = { onActionLongClick(action) }.takeIf { action.longClickAvailable }, + ) + } } } } @@ -68,10 +65,9 @@ private fun AnimatedVisibilityScope.LineSeparator(modifier: Modifier = Modifier) val lineColor = TangemTheme.colors.stroke.primary val strokeWidth = TangemTheme.dimens.size1 val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr - val verticalPadding = TangemTheme.dimens.spacing2 - val startPadding = TangemTheme.dimens.spacing18 + val startPadding = TangemTheme.dimens.spacing30 - val height = TangemTheme.dimens.size16 + verticalPadding * 2 + val height = TangemTheme.dimens.size16 Canvas( modifier = modifier @@ -96,31 +92,42 @@ private fun AnimatedVisibilityScope.LineSeparator(modifier: Modifier = Modifier) drawLine( color = lineColor, - start = Offset(x, verticalPadding.toPx()), - end = Offset(x, size.height - verticalPadding.toPx()), + start = Offset(x, 0f), + end = Offset(x, size.height), strokeWidth = strokeWidth.toPx(), ) } } -@OptIn(ExperimentalAnimationApi::class) +@OptIn(ExperimentalAnimationApi::class, ExperimentalFoundationApi::class) @Composable private fun AnimatedVisibilityScope.QuickActionItem( state: QuickActionUM, onClick: () -> Unit, + onLongClick: (() -> Unit)?, modifier: Modifier = Modifier, ) { val hapticManager = LocalHapticManager.current + val onLongClickInternal: (() -> Unit)? = if (onLongClick != null) { + { + hapticManager.perform(TangemHapticEffect.View.LongPress) + onLongClick() + } + } else { + null + } Row( modifier = modifier .fillMaxWidth() - .clip(TangemTheme.shapes.roundedCornersMedium) - .clickable { - hapticManager.perform(TangemHapticEffect.View.SegmentTick) - onClick() - } - .padding(horizontal = TangemTheme.dimens.spacing2, vertical = TangemTheme.dimens.spacing3), + .combinedClickable( + onLongClick = onLongClickInternal, + onClick = { + hapticManager.perform(TangemHapticEffect.View.SegmentTick) + onClick() + }, + ) + .padding(horizontal = TangemTheme.dimens.spacing14, vertical = TangemTheme.dimens.spacing4), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing18), ) { @@ -190,8 +197,10 @@ private fun Preview() { modifier = Modifier.background(color = TangemTheme.colors.background.action), ) { PortfolioQuickActions( + actions = QuickActionUM.entries.toImmutableList(), isVisible = isVisible, onActionClick = {}, + onActionLongClick = {}, ) } } @@ -205,8 +214,10 @@ private fun PreviewRtl() { TangemThemePreview(rtl = true) { Box(modifier = Modifier.background(color = TangemTheme.colors.background.action)) { PortfolioQuickActions( + actions = QuickActionUM.entries.toImmutableList(), isVisible = true, onActionClick = {}, + onActionLongClick = {}, ) } } diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/TokenActionsBottomSheet.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/TokenActionsBottomSheet.kt index bc725d98f3..57a8133772 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/TokenActionsBottomSheet.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/TokenActionsBottomSheet.kt @@ -2,7 +2,6 @@ package com.tangem.features.markets.portfolio.impl.ui import android.content.res.Configuration import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -10,12 +9,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.components.SimpleSettingsRow import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetTitle -import com.tangem.core.ui.components.inputrow.InputRowChecked -import com.tangem.core.ui.components.inputrow.inner.DividerContainer import com.tangem.core.ui.components.rows.CornersToRound +import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.markets.portfolio.impl.ui.state.TokenActionsBSContentUM @@ -50,16 +49,15 @@ private fun Content(content: TokenActionsBSContentUM) { else -> CornersToRound.ZERO } - DividerContainer( + Box( modifier = Modifier .clip(cornersToRound.getShape()) - .background(TangemTheme.colors.background.action) - .clickable { content.onActionClick(action) }, - showDivider = index != content.actions.lastIndex, + .background(TangemTheme.colors.background.action), ) { - InputRowChecked( - text = action.text, - checked = false, + SimpleSettingsRow( + title = action.text.resolveReference(), + icon = action.iconRes, + onItemsClick = { content.onActionClick(action) }, ) } } diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewAddToPortfolioBSContentProvider.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewAddToPortfolioBSContentProvider.kt index 725f9518be..3b0e811163 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewAddToPortfolioBSContentProvider.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewAddToPortfolioBSContentProvider.kt @@ -3,7 +3,6 @@ package com.tangem.features.markets.portfolio.impl.ui.preview import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.tangem.common.ui.userwallet.state.UserWalletItemUM import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.rows.model.BlockchainRowUM import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.wallets.models.UserWalletId @@ -54,13 +53,10 @@ internal class PreviewAddToPortfolioBSContentProvider : PreviewParameterProvider iconUrl = null, ), isScanCardNotificationVisible = true, + isWalletBlockVisible = true, continueButtonEnabled = true, onContinueButtonClick = {}, - walletSelectorConfig = TangemBottomSheetConfig( - isShow = false, - onDismissRequest = {}, - content = TangemBottomSheetConfigContent.Empty, - ), + walletSelectorConfig = TangemBottomSheetConfig.Empty, ), AddToPortfolioBSContentUM( selectedWallet = userWallet, @@ -81,13 +77,10 @@ internal class PreviewAddToPortfolioBSContentProvider : PreviewParameterProvider iconUrl = null, ), isScanCardNotificationVisible = true, + isWalletBlockVisible = false, continueButtonEnabled = false, onContinueButtonClick = {}, - walletSelectorConfig = TangemBottomSheetConfig( - isShow = false, - onDismissRequest = {}, - content = TangemBottomSheetConfigContent.Empty, - ), + walletSelectorConfig = TangemBottomSheetConfig.Empty, ), ) } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt index 02339b5257..247b98ce7d 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt @@ -2,12 +2,13 @@ package com.tangem.features.markets.portfolio.impl.ui.preview import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioTokenUM +import com.tangem.features.markets.portfolio.impl.ui.state.QuickActionUM import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider { @@ -16,39 +17,29 @@ internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider Unit, diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/MyPortfolioUM.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/MyPortfolioUM.kt index 4f08610f4d..cee5e178a7 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/MyPortfolioUM.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/MyPortfolioUM.kt @@ -10,7 +10,9 @@ internal sealed class MyPortfolioUM { data class Tokens( val tokens: ImmutableList, val buttonState: AddButtonState, - val bsConfig: TangemBottomSheetConfig, + val addToPortfolioBSConfig: TangemBottomSheetConfig, + val tokenReceiveBSConfig: TangemBottomSheetConfig, + val tokenActionsBSConfig: TangemBottomSheetConfig, val onAddClick: () -> Unit, ) : MyPortfolioUM() { @@ -22,7 +24,7 @@ internal sealed class MyPortfolioUM { } data class AddFirstToken( - val bsConfig: TangemBottomSheetConfig, + val addToPortfolioBSConfig: TangemBottomSheetConfig, val onAddClick: () -> Unit, ) : MyPortfolioUM() diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/PortfolioTokenUM.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/PortfolioTokenUM.kt index 59dc297746..b4e01c2896 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/PortfolioTokenUM.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/PortfolioTokenUM.kt @@ -1,10 +1,18 @@ package com.tangem.features.markets.portfolio.impl.ui.state import com.tangem.core.ui.components.token.state.TokenItemState +import kotlinx.collections.immutable.ImmutableList internal data class PortfolioTokenUM( val tokenItemState: TokenItemState, val isBalanceHidden: Boolean, val isQuickActionsShown: Boolean, - val onQuickActionClick: (QuickActionUM) -> Unit, -) \ No newline at end of file + val quickActions: QuickActions, +) { + + data class QuickActions( + val actions: ImmutableList, + val onQuickActionClick: (QuickActionUM) -> Unit, + val onQuickActionLongClick: (QuickActionUM) -> Unit, + ) +} \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/QuickActionUM.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/QuickActionUM.kt index b079b5ce50..dba8458d97 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/QuickActionUM.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/QuickActionUM.kt @@ -11,6 +11,7 @@ internal enum class QuickActionUM( val title: TextReference, val description: TextReference, @DrawableRes val icon: Int, + val longClickAvailable: Boolean = false, ) { Buy( title = resourceReference(R.string.common_buy), @@ -26,5 +27,6 @@ internal enum class QuickActionUM( title = resourceReference(R.string.common_receive), description = resourceReference(R.string.receive_token_description), icon = R.drawable.ic_arrow_down_24, + longClickAvailable = true, ), } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/TokenActionsBSContentUM.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/TokenActionsBSContentUM.kt index 7b2a115f54..83450b1ebc 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/TokenActionsBSContentUM.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/TokenActionsBSContentUM.kt @@ -1,5 +1,6 @@ package com.tangem.features.markets.portfolio.impl.ui.state +import androidx.annotation.DrawableRes import androidx.compose.runtime.Immutable import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.extensions.TextReference @@ -16,13 +17,38 @@ internal data class TokenActionsBSContentUM( @Immutable enum class Action( val text: TextReference, + @DrawableRes val iconRes: Int, ) { - CopyAddress(text = resourceReference(R.string.common_copy_address)), - Receive(text = resourceReference(R.string.common_receive)), - Sell(text = resourceReference(R.string.common_sell)), - Buy(text = resourceReference(R.string.common_buy)), - Send(text = resourceReference(R.string.common_send)), - Exchange(text = resourceReference(R.string.common_exchange)), - Stake(text = resourceReference(R.string.common_stake)), + CopyAddress( + text = resourceReference(R.string.common_copy_address), + iconRes = R.drawable.ic_copy_24, + ), + Send( + text = resourceReference(R.string.common_send), + iconRes = R.drawable.ic_arrow_up_24, + ), + Receive( + text = resourceReference(R.string.common_receive), + iconRes = R.drawable.ic_arrow_down_24, + ), + Buy( + text = resourceReference(R.string.common_buy), + iconRes = R.drawable.ic_plus_24, + ), + Sell( + text = resourceReference(R.string.common_sell), + iconRes = R.drawable.ic_currency_24, + ), + Exchange( + text = resourceReference(R.string.common_exchange), + iconRes = R.drawable.ic_exchange_horizontal_24, + ), + Stake( + text = resourceReference(R.string.common_stake), + iconRes = R.drawable.ic_staking_24, + ), + ; + + val order: Int = ordinal } } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/tokenlist/impl/model/converters/MarketsTokenItemConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/tokenlist/impl/model/converters/MarketsTokenItemConverter.kt index 295cebedcf..0a482edd91 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/tokenlist/impl/model/converters/MarketsTokenItemConverter.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/tokenlist/impl/model/converters/MarketsTokenItemConverter.kt @@ -121,16 +121,18 @@ internal class MarketsTokenItemConverter( } } + @Suppress("MagicNumber") private fun TokenMarket.getTrendType(): PriceChangeType { val percent = when (currentTrendInterval) { TrendInterval.H24 -> tokenQuotesShort.h24ChangePercent TrendInterval.D7 -> tokenQuotesShort.weekChangePercent TrendInterval.M1 -> tokenQuotesShort.monthChangePercent - }.setScale(2, RoundingMode.UP) - - return when (percent.compareTo(BigDecimal.ZERO)) { - 1 -> PriceChangeType.UP - -1 -> PriceChangeType.DOWN + } + val scaled = percent.setScale(4, RoundingMode.HALF_UP) + return when { + scaled == null -> PriceChangeType.NEUTRAL + scaled > BigDecimal.ZERO -> PriceChangeType.UP + scaled < BigDecimal.ZERO -> PriceChangeType.DOWN else -> PriceChangeType.NEUTRAL } } diff --git a/features/tokendetails/impl/build.gradle.kts b/features/tokendetails/impl/build.gradle.kts index de5b9043b9..32efd6bb49 100644 --- a/features/tokendetails/impl/build.gradle.kts +++ b/features/tokendetails/impl/build.gradle.kts @@ -56,6 +56,7 @@ dependencies { implementation(projects.core.deepLinks.global) implementation(projects.core.featuretoggles) implementation(projects.core.decompose) + implementation(projects.common.ui) implementation(projects.libs.crypto) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index a13a7b2a77..934b75a0d1 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -2,6 +2,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory import androidx.paging.PagingData import arrow.core.Either +import com.tangem.common.ui.tokens.getUnavailabilityReasonText import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig @@ -12,7 +13,6 @@ import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.event.triggeredEvent import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.NetworkHasDerivationUseCase @@ -216,7 +216,7 @@ internal class TokenDetailsStateFactory( isShow = true, onDismissRequest = clickIntents::onDismissDialog, content = TokenDetailsDialogConfig.DialogContentConfig.DisabledButtonReasonDialogConfig( - text = getUnavailabilityReasonText(unavailabilityReason), + text = unavailabilityReason.getUnavailabilityReasonText(), onConfirmClick = clickIntents::onDismissDialog, ), ), @@ -408,66 +408,4 @@ internal class TokenDetailsStateFactory( }.toImmutableList(), ) } - - private fun getUnavailabilityReasonText(unavailabilityReason: ScenarioUnavailabilityReason): TextReference { - return when (unavailabilityReason) { - is ScenarioUnavailabilityReason.StakingUnavailable -> { - resourceReference( - id = R.string.token_button_unavailability_reason_staking_unavailable, - formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), - ) - } - is ScenarioUnavailabilityReason.PendingTransaction -> { - when (unavailabilityReason.withdrawalScenario) { - ScenarioUnavailabilityReason.WithdrawalScenario.SEND -> resourceReference( - id = R.string.token_button_unavailability_reason_pending_transaction_send, - formatArgs = wrappedList(unavailabilityReason.networkName), - ) - ScenarioUnavailabilityReason.WithdrawalScenario.SELL -> resourceReference( - id = R.string.token_button_unavailability_reason_pending_transaction_sell, - formatArgs = wrappedList(unavailabilityReason.networkName), - ) - } - } - is ScenarioUnavailabilityReason.EmptyBalance -> { - when (unavailabilityReason.withdrawalScenario) { - ScenarioUnavailabilityReason.WithdrawalScenario.SEND -> resourceReference( - id = R.string.token_button_unavailability_reason_empty_balance_send, - ) - ScenarioUnavailabilityReason.WithdrawalScenario.SELL -> resourceReference( - id = R.string.token_button_unavailability_reason_empty_balance_sell, - ) - } - } - is ScenarioUnavailabilityReason.BuyUnavailable -> { - resourceReference( - id = R.string.token_button_unavailability_reason_buy_unavailable, - formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), - ) - } - is ScenarioUnavailabilityReason.NotExchangeable -> { - resourceReference( - id = R.string.token_button_unavailability_reason_not_exchangeable, - formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), - ) - } - is ScenarioUnavailabilityReason.NotSupportedBySellService -> { - resourceReference( - id = R.string.token_button_unavailability_reason_sell_unavailable, - formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), - ) - } - ScenarioUnavailabilityReason.Unreachable -> { - resourceReference( - id = R.string.token_button_unavailability_generic_description, - ) - } - ScenarioUnavailabilityReason.UnassociatedAsset -> resourceReference( - id = R.string.warning_receive_blocked_hedera_token_association_required_message, - ) - ScenarioUnavailabilityReason.None -> { - throw IllegalArgumentException("The unavailability reason must be other than None") - } - } - } } \ No newline at end of file diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 75b2af5fb3..5aa98a53a1 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -78,7 +78,7 @@ dependencies { implementation(projects.domain.balanceHiding.models) implementation(projects.domain.analytics) implementation(projects.domain.visa) - implementation(projects.domain.staking) + implementation(projects.domain.staking.models) //TODO: Create api/impl modules for onboarding [REDACTED_JIRA] implementation(projects.features.onboarding) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt index 9d9206c566..74b81ff310 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt @@ -5,6 +5,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import com.arkivanov.decompose.defaultComponentContext +import com.tangem.common.routing.AppRouter +import com.tangem.common.routing.utils.asRouter import com.tangem.core.decompose.context.DefaultAppComponentContext import com.tangem.core.decompose.di.DecomposeComponent import com.tangem.core.ui.UiDependencies @@ -44,6 +46,9 @@ internal class WalletFragment : ComposeFragment() { @Inject internal lateinit var marketsFeatureToggles: MarketsFeatureToggles + @Inject + internal lateinit var appRouter: AppRouter + private var marketsEntryComponent: MarketsEntryComponent? = null private val _walletRouter: InnerWalletRouter @@ -60,6 +65,7 @@ internal class WalletFragment : ComposeFragment() { messageHandler = uiDependencies.eventMessageHandler, dispatchers = coroutineDispatcherProvider, hiltComponentBuilder = componentBuilder, + replaceRouter = appRouter.asRouter(), ) marketsEntryComponent = marketsEntryComponentFactory.create(appContext) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt index 95f595916e..f4009e9c73 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt @@ -62,7 +62,7 @@ internal class MultiWalletCurrencyActionsConverter( is TokenActionsState.ActionState.Stake -> { title = resourceReference(R.string.common_stake) icon = R.drawable.ic_staking_24 - action = { clickIntents.onStakeClick(cryptoCurrencyStatus) } + action = { clickIntents.onStakeClick(cryptoCurrencyStatus, actionsState.yield) } } is TokenActionsState.ActionState.Sell -> { title = resourceReference(R.string.common_sell) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt index f00e79aa55..e66128d2c8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt @@ -1,9 +1,9 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents -import arrow.core.getOrElse import com.tangem.blockchain.common.address.AddressType import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter +import com.tangem.common.ui.tokens.getUnavailabilityReasonText import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent @@ -14,7 +14,6 @@ import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModel import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.WrappedList import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.haptic.TangemHapticEffect import com.tangem.core.ui.haptic.VibratorHapticManager import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase @@ -22,7 +21,7 @@ import com.tangem.domain.appcurrency.extenstions.unwrap import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.redux.ReduxStateHolder -import com.tangem.domain.staking.GetYieldUseCase +import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.tokens.* import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.model.CryptoCurrency @@ -67,7 +66,7 @@ interface WalletCurrencyActionsClickIntents { fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus) - fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus) + fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus, yield: Yield?) fun onCopyAddressLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus): TextReference? @@ -100,7 +99,6 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( private val reduxStateHolder: ReduxStateHolder, private val vibratorHapticManager: VibratorHapticManager, private val clipboardManager: ClipboardManager, - private val getYieldUseCase: GetYieldUseCase, private val appRouter: AppRouter, ) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents { @@ -380,25 +378,19 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( showErrorIfDemoModeOrElse(action = ::openExplorer) } - override fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { + override fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus, yield: Yield?) { val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWallet.walletId)) viewModelScope.launch { val userWalletId = stateHolder.getSelectedWalletId() val cryptoCurrency = cryptoCurrencyStatus.currency - val yield = getYieldUseCase.invoke( - cryptoCurrencyId = cryptoCurrency.id, - symbol = cryptoCurrency.symbol, - ).getOrElse { - error("Staking is unavailable for ${cryptoCurrency.name}") - } - reduxStateHolder.dispatch( - TradeCryptoAction.Stake( + appRouter.push( + AppRoute.Staking( userWalletId = userWalletId, cryptoCurrencyId = cryptoCurrency.id, - yield = yield, + yield = yield ?: return@launch, ), ) } @@ -487,14 +479,12 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( private fun handleUnavailabilityReason(unavailabilityReason: ScenarioUnavailabilityReason): Boolean { if (unavailabilityReason == ScenarioUnavailabilityReason.None) return false - val unavailabilityReasonText = getUnavailabilityReasonText(unavailabilityReason) - viewModelScope.launch(dispatchers.main) { walletEventSender.send( event = WalletEvent.ShowAlert( state = WalletAlertState.DefaultAlert( title = null, - message = unavailabilityReasonText, + message = unavailabilityReason.getUnavailabilityReasonText(), onConfirmClick = null, ), ), @@ -503,66 +493,4 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( return true } - - private fun getUnavailabilityReasonText(unavailabilityReason: ScenarioUnavailabilityReason): TextReference { - return when (unavailabilityReason) { - is ScenarioUnavailabilityReason.StakingUnavailable -> { - resourceReference( - id = R.string.token_button_unavailability_reason_staking_unavailable, - formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), - ) - } - is ScenarioUnavailabilityReason.PendingTransaction -> { - when (unavailabilityReason.withdrawalScenario) { - ScenarioUnavailabilityReason.WithdrawalScenario.SEND -> resourceReference( - id = R.string.token_button_unavailability_reason_pending_transaction_send, - formatArgs = wrappedList(unavailabilityReason.networkName), - ) - ScenarioUnavailabilityReason.WithdrawalScenario.SELL -> resourceReference( - id = R.string.token_button_unavailability_reason_pending_transaction_sell, - formatArgs = wrappedList(unavailabilityReason.networkName), - ) - } - } - is ScenarioUnavailabilityReason.EmptyBalance -> { - when (unavailabilityReason.withdrawalScenario) { - ScenarioUnavailabilityReason.WithdrawalScenario.SEND -> resourceReference( - id = R.string.token_button_unavailability_reason_empty_balance_send, - ) - ScenarioUnavailabilityReason.WithdrawalScenario.SELL -> resourceReference( - id = R.string.token_button_unavailability_reason_empty_balance_sell, - ) - } - } - is ScenarioUnavailabilityReason.BuyUnavailable -> { - resourceReference( - id = R.string.token_button_unavailability_reason_buy_unavailable, - formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), - ) - } - is ScenarioUnavailabilityReason.NotExchangeable -> { - resourceReference( - id = R.string.token_button_unavailability_reason_not_exchangeable, - formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), - ) - } - is ScenarioUnavailabilityReason.NotSupportedBySellService -> { - resourceReference( - id = R.string.token_button_unavailability_reason_sell_unavailable, - formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName), - ) - } - ScenarioUnavailabilityReason.Unreachable -> { - resourceReference( - id = R.string.token_button_unavailability_generic_description, - ) - } - ScenarioUnavailabilityReason.UnassociatedAsset -> resourceReference( - id = R.string.warning_receive_blocked_hedera_token_association_required_message, - ) - ScenarioUnavailabilityReason.None -> { - throw IllegalArgumentException("The unavailability reason must be other than None") - } - } - } } \ No newline at end of file