Updated on 2026-08-14
This commit is contained in:
commit
5ee5b4f61b
563 changed files with 13917 additions and 4692 deletions
|
|
@ -361,10 +361,10 @@ internal class WalletModel @Inject constructor(
|
|||
val info = tangemPayMainScreenCustomerInfoUseCase()
|
||||
if (info != null) {
|
||||
stateHolder.update(
|
||||
transformer = TangemPayStateTransformer(
|
||||
transformer = TangemPayInitialStateTransformer(
|
||||
value = info,
|
||||
onIssueOrderClick = ::issueOrder,
|
||||
onContinueKycClick = innerWalletRouter::openTangemPayOnboarding,
|
||||
onClickIssue = ::issueOrder,
|
||||
onClickKyc = innerWalletRouter::openTangemPayOnboarding,
|
||||
openDetails = innerWalletRouter::openTangemPayDetails,
|
||||
),
|
||||
)
|
||||
|
|
@ -373,9 +373,9 @@ internal class WalletModel @Inject constructor(
|
|||
|
||||
private fun issueOrder() {
|
||||
modelScope.launch {
|
||||
stateHolder.update(TangemPayStateTransformer(issueProgressState = true))
|
||||
stateHolder.update(TangemPayIssueProgressStateTransformer())
|
||||
tangemPayIssueOrderUseCase().onLeft {
|
||||
stateHolder.update(TangemPayStateTransformer(issueState = true, onIssueOrderClick = ::issueOrder))
|
||||
stateHolder.update(TangemPayIssueAvailableStateTransformer(onClickIssue = ::issueOrder))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,17 +4,15 @@ import arrow.core.getOrElse
|
|||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.nft.analytics.NFTAnalyticsEvent
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.TokensAction
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
|
|
@ -39,15 +37,13 @@ internal interface WalletContentClickIntents {
|
|||
|
||||
fun onDetailsClick()
|
||||
|
||||
fun onManageTokensClick()
|
||||
|
||||
fun onOrganizeTokensClick()
|
||||
|
||||
fun onDismissMarketsOnboarding()
|
||||
|
||||
fun onTokenItemClick(portfolioId: PortfolioId, currencyStatus: CryptoCurrencyStatus)
|
||||
fun onTokenItemClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onTokenItemLongClick(portfolioId: PortfolioId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
fun onTokenItemLongClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onAccountExpandClick(account: Account)
|
||||
|
||||
|
|
@ -81,7 +77,6 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
private val shouldShowMarketsTooltipUseCase: ShouldShowMarketsTooltipUseCase,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val walletEventSender: WalletEventSender,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
|
||||
|
|
@ -122,11 +117,6 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onManageTokensClick() {
|
||||
reduxStateHolder.dispatch(action = TokensAction.SetArgs.ManageAccess)
|
||||
router.openManageTokensScreen(userWalletId = stateHolder.getSelectedWalletId())
|
||||
}
|
||||
|
||||
override fun onOrganizeTokensClick() {
|
||||
router.openOrganizeTokensScreen(userWalletId = stateHolder.getSelectedWalletId())
|
||||
}
|
||||
|
|
@ -138,13 +128,12 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onTokenItemClick(portfolioId: PortfolioId, currencyStatus: CryptoCurrencyStatus) {
|
||||
router.openTokenDetails(portfolioId, currencyStatus)
|
||||
override fun onTokenItemClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) {
|
||||
router.openTokenDetails(userWalletId, currencyStatus)
|
||||
}
|
||||
|
||||
override fun onTokenItemLongClick(portfolioId: PortfolioId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
override fun onTokenItemLongClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
val userWalletId = portfolioId.userWalletId
|
||||
val userWallet = getUserWalletUseCase(userWalletId).getOrElse {
|
||||
Timber.e(
|
||||
"""
|
||||
|
|
@ -160,7 +149,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
getCryptoCurrencyActionsUseCase(userWallet = userWallet, cryptoCurrencyStatus = cryptoCurrencyStatus)
|
||||
.take(count = 1)
|
||||
.collectLatest {
|
||||
showActionsBottomSheet(it, userWallet, portfolioId)
|
||||
showActionsBottomSheet(it, userWallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -175,17 +164,12 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
accountDependencies.expandedAccountsHolder.collapseAccount(userWalletId, account.accountId)
|
||||
}
|
||||
|
||||
private fun showActionsBottomSheet(
|
||||
tokenActionsState: TokenActionsState,
|
||||
userWallet: UserWallet,
|
||||
portfolioId: PortfolioId,
|
||||
) {
|
||||
private fun showActionsBottomSheet(tokenActionsState: TokenActionsState, userWallet: UserWallet) {
|
||||
stateHolder.showBottomSheet(
|
||||
ActionsBottomSheetConfig(
|
||||
actions = MultiWalletCurrencyActionsConverter(
|
||||
userWallet = userWallet,
|
||||
clickIntents = currencyActionsClickIntents,
|
||||
portfolioId = portfolioId,
|
||||
).convert(tokenActionsState),
|
||||
),
|
||||
userWallet.walletId,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ import com.tangem.core.ui.extensions.WrappedList
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.haptic.TangemHapticEffect
|
||||
import com.tangem.core.ui.haptic.VibratorHapticManager
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
import com.tangem.domain.account.status.usecase.SaveCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.extenstions.unwrap
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
|
|
@ -29,8 +32,6 @@ import com.tangem.domain.core.utils.lceError
|
|||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.ReceiveAddressModel
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
|
|
@ -43,12 +44,7 @@ import com.tangem.domain.promo.GetStoryContentUseCase
|
|||
import com.tangem.domain.promo.models.StoryContentIds
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.GetViewedTokenReceiveWarningUseCase
|
||||
import com.tangem.domain.tokens.NeedShowYieldSupplyDepositedWarningUseCase
|
||||
import com.tangem.domain.tokens.SaveViewedTokenReceiveWarningUseCase
|
||||
import com.tangem.domain.tokens.SaveViewedYieldSupplyWarningUseCase
|
||||
import com.tangem.domain.tokens.model.details.TokenAction
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
|
||||
|
|
@ -57,7 +53,8 @@ import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent
|
|||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.AVAILABLE
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText
|
||||
import com.tangem.domain.transaction.usecase.GetEnsNameUseCase
|
||||
import com.tangem.domain.tokens.model.details.TokenAction
|
||||
import com.tangem.domain.transaction.usecase.ReceiveAddressesFactory
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
|
|
@ -70,7 +67,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.WalletFeatureUseCasesFacade
|
||||
import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -78,13 +74,14 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
interface WalletCurrencyActionsClickIntents {
|
||||
|
||||
fun onSendClick(
|
||||
portfolioId: PortfolioId,
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
)
|
||||
|
|
@ -92,32 +89,32 @@ interface WalletCurrencyActionsClickIntents {
|
|||
fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
|
||||
fun onBuyClick(
|
||||
portfolioId: PortfolioId,
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
)
|
||||
|
||||
fun onSwapClick(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
portfolioId: PortfolioId,
|
||||
userWalletId: UserWalletId,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
)
|
||||
|
||||
fun onReceiveClick(
|
||||
portfolioId: PortfolioId,
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
event: AnalyticsEvent? = null,
|
||||
)
|
||||
|
||||
fun onStakeClick(portfolioId: PortfolioId, cryptoCurrencyStatus: CryptoCurrencyStatus, yield: Yield?)
|
||||
fun onStakeClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, yield: Yield?)
|
||||
|
||||
fun onCopyAddressLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus): TextReference?
|
||||
|
||||
fun onCopyAddressClick(portfolioId: PortfolioId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
fun onCopyAddressClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onHideTokensClick(portfolioId: PortfolioId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
fun onHideTokensClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onPerformHideToken(portfolioId: PortfolioId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
fun onPerformHideToken(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onExploreClick()
|
||||
|
||||
|
|
@ -139,7 +136,6 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
|
||||
private val useCasesFacade: WalletFeatureUseCasesFacade,
|
||||
private val getExploreUrlUseCase: GetExploreUrlUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
|
|
@ -152,16 +148,20 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
private val appRouter: AppRouter,
|
||||
private val rampStateManager: RampStateManager,
|
||||
private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle,
|
||||
private val getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase,
|
||||
private val saveViewedTokenReceiveWarningUseCase: SaveViewedTokenReceiveWarningUseCase,
|
||||
private val getEnsNameUseCase: GetEnsNameUseCase,
|
||||
private val receiveAddressesFactory: ReceiveAddressesFactory,
|
||||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
|
||||
private val needShowYieldSupplyDepositedWarningUseCase: NeedShowYieldSupplyDepositedWarningUseCase,
|
||||
private val saveViewedYieldSupplyWarningUseCase: SaveViewedYieldSupplyWarningUseCase,
|
||||
private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase,
|
||||
private val removeCurrencyUseCase: RemoveCurrencyUseCase,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
|
||||
private val saveCryptoCurrenciesUseCase: SaveCryptoCurrenciesUseCase,
|
||||
) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents {
|
||||
|
||||
override fun onSendClick(
|
||||
portfolioId: PortfolioId,
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
) {
|
||||
|
|
@ -185,27 +185,21 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
modelScope.launch {
|
||||
saveViewedYieldSupplyWarningUseCase(cryptoCurrencyStatus.currency.name)
|
||||
stateHolder.hideBottomSheet()
|
||||
navigateToSend(cryptoCurrencyStatus, portfolioId)
|
||||
navigateToSend(cryptoCurrencyStatus, userWalletId)
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
navigateToSend(cryptoCurrencyStatus, portfolioId)
|
||||
navigateToSend(cryptoCurrencyStatus, userWalletId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReceiveClick(
|
||||
portfolioId: PortfolioId,
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
event: AnalyticsEvent?,
|
||||
) {
|
||||
val userWalletId = portfolioId.userWalletId
|
||||
if (portfolioId is PortfolioId.Account) {
|
||||
// todo account find address
|
||||
TODO("account")
|
||||
}
|
||||
|
||||
analyticsEventHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.ButtonWithParams.ButtonReceive(
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
|
|
@ -278,12 +272,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override fun onCopyAddressClick(portfolioId: PortfolioId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
val userWalletId = portfolioId.userWalletId
|
||||
if (portfolioId is PortfolioId.Account) {
|
||||
// todo account find address
|
||||
TODO("account")
|
||||
}
|
||||
override fun onCopyAddressClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
analyticsEventHandler.send(
|
||||
event = TokenReceiveNewAnalyticsEvent.ButtonCopyAddress(
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
|
|
@ -305,7 +294,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onHideTokensClick(portfolioId: PortfolioId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
override fun onHideTokensClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
analyticsEventHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.ButtonRemoveToken(cryptoCurrencyStatus.currency.symbol),
|
||||
)
|
||||
|
|
@ -313,19 +302,19 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
modelScope.launch(dispatchers.main) {
|
||||
walletEventSender.send(
|
||||
event = WalletEvent.ShowAlert(
|
||||
state = getHideTokeAlertConfig(portfolioId, cryptoCurrencyStatus),
|
||||
state = getHideTokeAlertConfig(userWalletId, cryptoCurrencyStatus),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getHideTokeAlertConfig(
|
||||
portfolioId: PortfolioId,
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): WalletAlertState.DefaultAlert {
|
||||
val currency = cryptoCurrencyStatus.currency
|
||||
val isCryptoCurrencyCoinCouldHide = currency is CryptoCurrency.Coin &&
|
||||
!useCasesFacade.isCryptoCurrencyCoinCouldHide(portfolioId = portfolioId, cryptoCurrencyCoin = currency)
|
||||
!isCryptoCurrencyCoinCouldHide(userWalletId = userWalletId, cryptoCurrencyCoin = currency)
|
||||
return if (isCryptoCurrencyCoinCouldHide) {
|
||||
WalletAlertState.DefaultAlert(
|
||||
title = resourceReference(
|
||||
|
|
@ -351,14 +340,30 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
formatArgs = WrappedList(listOf(cryptoCurrencyStatus.currency.name)),
|
||||
),
|
||||
message = resourceReference(R.string.token_details_hide_alert_message),
|
||||
onConfirmClick = { onPerformHideToken(portfolioId, cryptoCurrencyStatus) },
|
||||
onConfirmClick = { onPerformHideToken(userWalletId, cryptoCurrencyStatus) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPerformHideToken(portfolioId: PortfolioId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
override fun onPerformHideToken(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
modelScope.launch(dispatchers.io) {
|
||||
useCasesFacade.removeCurrencyUseCase(portfolioId, cryptoCurrencyStatus.currency)
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
val accountId = getAccountCurrencyStatusUseCase.invokeSync(
|
||||
userWalletId = userWalletId,
|
||||
currency = cryptoCurrencyStatus.currency,
|
||||
)
|
||||
.map { it.account.accountId }
|
||||
.getOrNull()
|
||||
|
||||
if (accountId == null) {
|
||||
Timber.e("Account ID is null, cannot hide currency ${cryptoCurrencyStatus.currency.id}")
|
||||
return@launch
|
||||
}
|
||||
|
||||
saveCryptoCurrenciesUseCase(accountId = accountId, remove = cryptoCurrencyStatus.currency)
|
||||
} else {
|
||||
removeCurrencyUseCase(userWalletId, cryptoCurrencyStatus.currency)
|
||||
}
|
||||
.fold(
|
||||
ifLeft = {
|
||||
walletEventSender.send(
|
||||
|
|
@ -366,7 +371,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
},
|
||||
ifRight = {
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = portfolioId.userWalletId))
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWalletId))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -399,7 +404,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onBuyClick(
|
||||
portfolioId: PortfolioId,
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
) {
|
||||
|
|
@ -415,7 +420,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
appRouter.push(
|
||||
AppRoute.Onramp(
|
||||
portfolioId = portfolioId,
|
||||
userWalletId = userWalletId,
|
||||
currency = cryptoCurrencyStatus.currency,
|
||||
source = OnrampSource.TOKEN_LONG_TAP,
|
||||
),
|
||||
|
|
@ -424,7 +429,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
override fun onSwapClick(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
portfolioId: PortfolioId,
|
||||
userWalletId: UserWalletId,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
) {
|
||||
analyticsEventHandler.send(
|
||||
|
|
@ -447,12 +452,12 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
modelScope.launch {
|
||||
saveViewedYieldSupplyWarningUseCase(cryptoCurrencyStatus.currency.name)
|
||||
stateHolder.hideBottomSheet()
|
||||
navigateToSwap(cryptoCurrencyStatus, portfolioId)
|
||||
navigateToSwap(cryptoCurrencyStatus, userWalletId)
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
navigateToSwap(cryptoCurrencyStatus, portfolioId)
|
||||
navigateToSwap(cryptoCurrencyStatus, userWalletId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -493,15 +498,15 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onStakeClick(portfolioId: PortfolioId, cryptoCurrencyStatus: CryptoCurrencyStatus, yield: Yield?) {
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = portfolioId.userWalletId))
|
||||
override fun onStakeClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, yield: Yield?) {
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWalletId))
|
||||
|
||||
modelScope.launch {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
|
||||
appRouter.push(
|
||||
AppRoute.Staking(
|
||||
portfolioId = portfolioId,
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = cryptoCurrency.id,
|
||||
yieldId = yield?.id ?: return@launch,
|
||||
),
|
||||
|
|
@ -519,15 +524,19 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
override fun onMultiWalletSwapClick(userWalletId: UserWalletId) {
|
||||
val selectedWallet = stateHolder.getSelectedWallet() as? WalletState.MultiCurrency.Content ?: return
|
||||
val tokenListState = selectedWallet.tokensListState as? WalletTokensListState.ContentState.Content ?: return
|
||||
val tokenListState = selectedWallet.tokensListState
|
||||
|
||||
if (tokenListState.items.count { it is TokensListItemUM.Token } < 2) {
|
||||
handleError(
|
||||
alertState = WalletAlertState.InsufficientTokensCountForSwapping,
|
||||
eventCreator = MainScreenAnalyticsEvent::ButtonSwap,
|
||||
when (tokenListState) {
|
||||
is WalletTokensListState.ContentState.Content -> checkSwapCryptoAvailability(
|
||||
tokenCount = tokenListState.items.count { it is TokensListItemUM.Token },
|
||||
)
|
||||
|
||||
return
|
||||
is WalletTokensListState.ContentState.PortfolioContent -> checkSwapCryptoAvailability(
|
||||
tokenCount = tokenListState.items.sumOf { it.tokens.count { it is TokensListItemUM.Token } },
|
||||
)
|
||||
WalletTokensListState.ContentState.Loading,
|
||||
WalletTokensListState.ContentState.Locked,
|
||||
WalletTokensListState.Empty,
|
||||
-> return
|
||||
}
|
||||
|
||||
modelScope.launch {
|
||||
|
|
@ -720,44 +729,10 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun configureReceiveAddresses(cryptoCurrencyStatus: CryptoCurrencyStatus): TokenReceiveConfig? {
|
||||
val networkAddress = cryptoCurrencyStatus.value.networkAddress ?: return null
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
|
||||
val ensName = getEnsNameUseCase.invoke(
|
||||
return receiveAddressesFactory.create(
|
||||
status = cryptoCurrencyStatus,
|
||||
userWalletId = userWalletId,
|
||||
network = cryptoCurrencyStatus.currency.network,
|
||||
address = networkAddress.defaultAddress.value,
|
||||
)
|
||||
|
||||
val receiveAddresses = buildList {
|
||||
ensName?.let { ens ->
|
||||
add(
|
||||
ReceiveAddressModel(
|
||||
nameService = ReceiveAddressModel.NameService.Ens,
|
||||
value = ens,
|
||||
),
|
||||
)
|
||||
}
|
||||
networkAddress.availableAddresses.map { address ->
|
||||
add(
|
||||
ReceiveAddressModel(
|
||||
nameService = when (address.type) {
|
||||
NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default
|
||||
NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy
|
||||
},
|
||||
value = address.value,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return TokenReceiveConfig(
|
||||
shouldShowWarning = cryptoCurrencyStatus.currency.name !in getViewedTokenReceiveWarningUseCase(),
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
userWalletId = userWalletId,
|
||||
showMemoDisclaimer = cryptoCurrencyStatus.currency.network.transactionExtrasType != Network
|
||||
.TransactionExtrasType.NONE,
|
||||
receiveAddress = receiveAddresses,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -766,21 +741,21 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
needShowYieldSupplyDepositedWarningUseCase(cryptoCurrencyStatus)
|
||||
}
|
||||
|
||||
private fun navigateToSend(cryptoCurrencyStatus: CryptoCurrencyStatus, portfolioId: PortfolioId) {
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = portfolioId.userWalletId))
|
||||
private fun navigateToSend(cryptoCurrencyStatus: CryptoCurrencyStatus, userWalletId: UserWalletId) {
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWalletId))
|
||||
val route = AppRoute.Send(
|
||||
currency = cryptoCurrencyStatus.currency,
|
||||
portfolioId = portfolioId,
|
||||
userWalletId = userWalletId,
|
||||
)
|
||||
|
||||
appRouter.push(route)
|
||||
}
|
||||
|
||||
private fun navigateToSwap(cryptoCurrencyStatus: CryptoCurrencyStatus, portfolioId: PortfolioId) {
|
||||
private fun navigateToSwap(cryptoCurrencyStatus: CryptoCurrencyStatus, userWalletId: UserWalletId) {
|
||||
appRouter.push(
|
||||
AppRoute.Swap(
|
||||
currencyFrom = cryptoCurrencyStatus.currency,
|
||||
portfolioId = portfolioId,
|
||||
userWalletId = userWalletId,
|
||||
screenSource = AnalyticsParam.ScreensSources.LongTap.value,
|
||||
),
|
||||
)
|
||||
|
|
@ -808,4 +783,15 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSwapCryptoAvailability(tokenCount: Int) {
|
||||
if (tokenCount < 2) {
|
||||
handleError(
|
||||
alertState = WalletAlertState.InsufficientTokensCountForSwapping,
|
||||
eventCreator = MainScreenAnalyticsEvent::ButtonSwap,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -361,7 +361,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
userWalletId = userWallet.walletId,
|
||||
currency = cryptoCurrency,
|
||||
source = OnrampSource.SEPA_BANNER,
|
||||
launchSepa = true,
|
||||
shouldLaunchSepa = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.tangem.feature.wallet.presentation.common.WalletPreviewData.topBarCon
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal object WalletScreenPreviewData {
|
||||
private val tokenItemState = TokenItemState.Content(
|
||||
|
|
@ -86,15 +87,17 @@ internal object WalletScreenPreviewData {
|
|||
private val portfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
|
||||
items = persistentListOf(
|
||||
TokensListItemUM.Portfolio(
|
||||
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>(),
|
||||
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
|
||||
isExpanded = false,
|
||||
state = AccountItemPreviewData.accountItem
|
||||
isCollapsable = true,
|
||||
tokenItemUM = AccountItemPreviewData.accountItem
|
||||
.copy(iconState = AccountItemPreviewData.accountLetterIcon),
|
||||
),
|
||||
TokensListItemUM.Portfolio(
|
||||
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>(),
|
||||
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
|
||||
isExpanded = true,
|
||||
state = AccountItemPreviewData.accountItem,
|
||||
isCollapsable = true,
|
||||
tokenItemUM = AccountItemPreviewData.accountItem,
|
||||
),
|
||||
),
|
||||
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
|
||||
|
|
|
|||
|
|
@ -3,17 +3,16 @@ package com.tangem.feature.wallet.presentation.router
|
|||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRoute.ManageTokens.Source
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.domain.tokens.model.details.TokenAction
|
||||
|
|
@ -67,12 +66,12 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
urlOpener.openUrl(url)
|
||||
}
|
||||
|
||||
override fun openTokenDetails(portfolioId: PortfolioId, currencyStatus: CryptoCurrencyStatus) {
|
||||
override fun openTokenDetails(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) {
|
||||
val networkAddress = currencyStatus.value.networkAddress
|
||||
if (networkAddress != null && networkAddress.defaultAddress.value.isNotEmpty()) {
|
||||
router.push(
|
||||
AppRoute.CurrencyDetails(
|
||||
portfolioId = portfolioId,
|
||||
userWalletId = userWalletId,
|
||||
currency = currencyStatus.currency,
|
||||
),
|
||||
)
|
||||
|
|
@ -87,10 +86,6 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
return router.stack.lastOrNull() is AppRoute.Wallet
|
||||
}
|
||||
|
||||
override fun openManageTokensScreen(userWalletId: UserWalletId) {
|
||||
router.push(AppRoute.ManageTokens(Source.SETTINGS, userWalletId))
|
||||
}
|
||||
|
||||
override fun openScanFailedDialog(onTryAgain: () -> Unit) {
|
||||
reduxStateHolder.dispatchDialogShow(StateDialog.ScanFailsDialog(StateDialog.ScanFailsSource.MAIN, onTryAgain))
|
||||
}
|
||||
|
|
@ -114,8 +109,8 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
router.push(AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding))
|
||||
}
|
||||
|
||||
override fun openTangemPayDetails(customerWalletAddress: String, cardNumberEnd: String) {
|
||||
router.push(AppRoute.TangemPayDetails(customerWalletAddress, cardNumberEnd))
|
||||
override fun openTangemPayDetails(config: TangemPayDetailsConfig) {
|
||||
router.push(AppRoute.TangemPayDetails(config))
|
||||
}
|
||||
|
||||
override fun openYieldSupplyBottomSheet(
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ package com.tangem.feature.wallet.presentation.router
|
|||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
import com.tangem.domain.tokens.model.details.TokenAction
|
||||
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
|
|
@ -42,7 +42,7 @@ internal interface InnerWalletRouter {
|
|||
fun openUrl(url: String)
|
||||
|
||||
/** Open token details screen */
|
||||
fun openTokenDetails(portfolioId: PortfolioId, currencyStatus: CryptoCurrencyStatus)
|
||||
fun openTokenDetails(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
/** Open stories screen */
|
||||
fun openStoriesScreen()
|
||||
|
|
@ -50,9 +50,6 @@ internal interface InnerWalletRouter {
|
|||
/** Is wallet last screen */
|
||||
fun isWalletLastScreen(): Boolean
|
||||
|
||||
/** Open manage tokens screen */
|
||||
fun openManageTokensScreen(userWalletId: UserWalletId)
|
||||
|
||||
/** Open scan failed dialog */
|
||||
fun openScanFailedDialog(onTryAgain: () -> Unit)
|
||||
|
||||
|
|
@ -63,7 +60,7 @@ internal interface InnerWalletRouter {
|
|||
|
||||
fun openTangemPayOnboarding()
|
||||
|
||||
fun openTangemPayDetails(customerWalletAddress: String, cardNumberEnd: String)
|
||||
fun openTangemPayDetails(config: TangemPayDetailsConfig)
|
||||
|
||||
/** Open BS abput yield supply active and all money deposited in AAVE */
|
||||
fun openYieldSupplyBottomSheet(
|
||||
|
|
|
|||
|
|
@ -73,6 +73,13 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
|
||||
data object ScreenOpened : MainScreen(event = "Screen opened")
|
||||
|
||||
class WalletSelected(val isImported: Boolean) : MainScreen(
|
||||
event = "Wallet Selected",
|
||||
params = mapOf(
|
||||
"Wallet Type" to if (isImported) "Seed Phrase" else "Seedless",
|
||||
),
|
||||
)
|
||||
|
||||
class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreen(
|
||||
event = "Enable Biometric",
|
||||
params = mapOf("State" to state.value),
|
||||
|
|
|
|||
|
|
@ -29,6 +29,14 @@ internal class SelectedWalletAnalyticsSender @Inject constructor(
|
|||
* */
|
||||
private fun getEvent(userWallet: UserWallet): AnalyticsEvent? = when {
|
||||
userWallet.isLocked -> WalletScreenAnalyticsEvent.MainScreen.WalletUnlock
|
||||
else -> null
|
||||
|
||||
else -> WalletScreenAnalyticsEvent.MainScreen.WalletSelected(userWallet.isImported())
|
||||
}
|
||||
|
||||
private fun UserWallet.isImported(): Boolean {
|
||||
return when (this) {
|
||||
is UserWallet.Cold -> isImported
|
||||
is UserWallet.Hot -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ import com.tangem.domain.analytics.model.WalletBalanceState
|
|||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
|
|
@ -38,33 +37,41 @@ internal class TokenListAnalyticsSender @Inject constructor(
|
|||
private val mutex = Mutex()
|
||||
private val loadingTraces = mutableMapOf<UserWalletId, Trace>()
|
||||
|
||||
suspend fun send(displayedUiState: WalletState?, userWallet: UserWallet, tokenList: TokenList) {
|
||||
suspend fun send(
|
||||
displayedUiState: WalletState?,
|
||||
userWallet: UserWallet,
|
||||
totalFiatBalance: TotalFiatBalance,
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
) {
|
||||
if (screenLifecycleProvider.isBackgroundState.value) return
|
||||
if (displayedUiState == null || displayedUiState.pullToRefreshConfig.isRefreshing) return
|
||||
|
||||
if (tokenList.totalFiatBalance is TotalFiatBalance.Loading) {
|
||||
startLoadingTraceIfNeeded(userWallet.walletId, tokenList)
|
||||
if (totalFiatBalance is TotalFiatBalance.Loading) {
|
||||
startLoadingTraceIfNeeded(userWallet.walletId, flattenCurrencies)
|
||||
return
|
||||
}
|
||||
|
||||
if (isTerminalState(tokenList.totalFiatBalance)) {
|
||||
stopLoadingTraceIfNeeded(userWallet.walletId, tokenList.totalFiatBalance)
|
||||
if (isTerminalState(totalFiatBalance)) {
|
||||
stopLoadingTraceIfNeeded(userWallet.walletId, totalFiatBalance)
|
||||
}
|
||||
|
||||
val currenciesStatuses = tokenList.flattenCurrencies()
|
||||
val currenciesStatuses = flattenCurrencies
|
||||
|
||||
sendBalanceLoadedEventIfNeeded(tokenList.totalFiatBalance, currenciesStatuses)
|
||||
sendToppedUpEventIfNeeded(userWallet, tokenList.totalFiatBalance, currenciesStatuses)
|
||||
sendBalanceLoadedEventIfNeeded(totalFiatBalance, currenciesStatuses)
|
||||
sendToppedUpEventIfNeeded(userWallet, totalFiatBalance, currenciesStatuses)
|
||||
sendUnreachableNetworksEventIfNeeded(currenciesStatuses)
|
||||
sendTokenBalancesIfNeeded(currenciesStatuses)
|
||||
}
|
||||
|
||||
private suspend fun startLoadingTraceIfNeeded(userWalletId: UserWalletId, tokenList: TokenList) {
|
||||
private suspend fun startLoadingTraceIfNeeded(
|
||||
userWalletId: UserWalletId,
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
) {
|
||||
mutex.withLock {
|
||||
if (!loadingTraces.containsKey(userWalletId)) {
|
||||
val trace = FirebasePerformance.getInstance().newTrace(BALANCE_LOADED_TRACE_NAME)
|
||||
trace.start()
|
||||
trace.putAttribute(TOKENS_COUNT, tokenList.flattenCurrencies().size.toString())
|
||||
trace.putAttribute(TOKENS_COUNT, flattenCurrencies.size.toString())
|
||||
loadingTraces[userWalletId] = trace
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,17 +9,16 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig.IconTint
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
import com.tangem.domain.card.CardTypesResolver
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import com.tangem.domain.onramp.GetOnrampCountryUseCase
|
||||
|
|
@ -45,6 +44,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -70,72 +70,81 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotification>> {
|
||||
val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver
|
||||
|
||||
val tokenListFlow = if (accountDependencies.accountsFeatureToggles.isFeatureEnabled) {
|
||||
val accountStatusList by lazy {
|
||||
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
|
||||
accountDependencies.singleAccountStatusListSupplier(params)
|
||||
} else {
|
||||
tokenListStore.getOrThrow(userWallet.walletId)
|
||||
.map { it.totalFiatBalance to it.flattenCurrencies() }
|
||||
.map { Lce.Content(it) }
|
||||
}
|
||||
|
||||
fun tokenListFlow(): LceFlow<TokenListError, Pair<TotalFiatBalance, List<CryptoCurrencyStatus>>> {
|
||||
return if (accountDependencies.accountsFeatureToggles.isFeatureEnabled) {
|
||||
accountStatusList
|
||||
} else {
|
||||
runCatching { tokenListStore.getOrThrow(userWallet.walletId) }
|
||||
.map { result -> result.map { lce -> lce.map { it.totalFiatBalance to it.flattenCurrencies() } } }
|
||||
.getOrNull()
|
||||
// in case of runtime change ft in tester menu
|
||||
?: accountStatusList
|
||||
}
|
||||
}
|
||||
|
||||
// val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
|
||||
// val accountStatusListFlow = accountDependencies.singleAccountStatusListSupplier(params)
|
||||
return combine(
|
||||
tokenListFlow,
|
||||
// todo account just use it, after delete accountsFeatureToggles
|
||||
// accountStatusListFlow,
|
||||
isReadyToShowRateAppUseCase(),
|
||||
isNeedToBackupUseCase(userWallet.walletId),
|
||||
seedPhraseNotificationUseCase(userWalletId = userWallet.walletId),
|
||||
shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Referral),
|
||||
shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Sepa),
|
||||
notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key),
|
||||
) { array ->
|
||||
val totalFiatBalance: Lce<TokenListError, TotalFiatBalance>
|
||||
val flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>
|
||||
if (accountDependencies.accountsFeatureToggles.isFeatureEnabled) {
|
||||
val accountStatusList = array[0] as AccountStatusList
|
||||
totalFiatBalance = Lce.Content(accountStatusList.totalFiatBalance)
|
||||
flattenCurrencies = Lce.Content(accountStatusList.flattenCurrencies())
|
||||
} else {
|
||||
val maybeTokenList = array[0] as Lce<TokenListError, TokenList>
|
||||
totalFiatBalance = maybeTokenList.map { it.totalFiatBalance }
|
||||
flattenCurrencies = maybeTokenList.map { it.flattenCurrencies() }
|
||||
) { array -> array }
|
||||
.combine(tokenListFlow()) { array, any: Any -> arrayOf(any).plus(elements = array) }
|
||||
.map { array ->
|
||||
val lceTokens = array[0] as Lce<TokenListError, Pair<TotalFiatBalance, List<CryptoCurrencyStatus>>>
|
||||
val totalFiatBalance = lceTokens.map { it.first }
|
||||
val flattenCurrencies = lceTokens.map { it.second }
|
||||
val isReadyToShowRating = array[1] as Boolean
|
||||
val isNeedToBackup = array[2] as Boolean
|
||||
val seedPhraseIssueStatus = array[3] as SeedPhraseNotificationsStatus
|
||||
val shouldShowReferralPromo = array[4] as Boolean
|
||||
val shouldShowSepaBanner = array[5] as Boolean
|
||||
val shouldShowEnablePushesReminderNotification = array[6] as Boolean
|
||||
|
||||
buildList {
|
||||
addUsedOutdatedDataNotification(totalFiatBalance)
|
||||
|
||||
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
|
||||
|
||||
addFinishWalletActivationNotification(userWallet, totalFiatBalance, clickIntents)
|
||||
|
||||
addReferralPromoNotification(cardTypesResolver, clickIntents, shouldShowReferralPromo)
|
||||
|
||||
addSepaPromoNotification(userWallet, clickIntents, shouldShowSepaBanner)
|
||||
|
||||
addInformationalNotifications(userWallet, cardTypesResolver, flattenCurrencies, clickIntents)
|
||||
|
||||
addWarningNotifications(cardTypesResolver, flattenCurrencies, isNeedToBackup, clickIntents)
|
||||
|
||||
addPushReminderNotification(
|
||||
clickIntents = clickIntents,
|
||||
shouldShowPushReminderBanner = shouldShowEnablePushesReminderNotification &&
|
||||
!notificationsRepository.isUserAllowToSubscribeOnPushNotifications(),
|
||||
)
|
||||
|
||||
addYieldSupplyNotifications(flattenCurrencies)
|
||||
|
||||
val hasCriticalOrWarning = any { notification ->
|
||||
notification is WalletNotification.Critical || notification is WalletNotification.Warning
|
||||
}
|
||||
|
||||
if (!hasCriticalOrWarning) {
|
||||
addRateTheAppNotification(isReadyToShowRating, clickIntents)
|
||||
}
|
||||
}.toImmutableList()
|
||||
}
|
||||
|
||||
val isReadyToShowRating = array[1] as Boolean
|
||||
val isNeedToBackup = array[2] as Boolean
|
||||
val seedPhraseIssueStatus = array[3] as SeedPhraseNotificationsStatus
|
||||
val shouldShowReferralPromo = array[4] as Boolean
|
||||
val shouldShowSepaBanner = array[5] as Boolean
|
||||
val shouldShowEnablePushesReminderNotification = array[6] as Boolean
|
||||
|
||||
buildList {
|
||||
addUsedOutdatedDataNotification(totalFiatBalance)
|
||||
|
||||
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
|
||||
|
||||
addFinishWalletActivationNotification(userWallet, totalFiatBalance, clickIntents)
|
||||
|
||||
addReferralPromoNotification(cardTypesResolver, clickIntents, shouldShowReferralPromo)
|
||||
|
||||
addSepaPromoNotification(userWallet, clickIntents, shouldShowSepaBanner)
|
||||
|
||||
addInformationalNotifications(userWallet, cardTypesResolver, flattenCurrencies, clickIntents)
|
||||
|
||||
addWarningNotifications(cardTypesResolver, flattenCurrencies, isNeedToBackup, clickIntents)
|
||||
|
||||
addPushReminderNotification(
|
||||
clickIntents = clickIntents,
|
||||
shouldShowPushReminderBanner = shouldShowEnablePushesReminderNotification &&
|
||||
!notificationsRepository.isUserAllowToSubscribeOnPushNotifications(),
|
||||
)
|
||||
|
||||
addYieldSupplyNotifications(flattenCurrencies)
|
||||
|
||||
val hasCriticalOrWarning = any { notification ->
|
||||
notification is WalletNotification.Critical || notification is WalletNotification.Warning
|
||||
}
|
||||
|
||||
if (!hasCriticalOrWarning) {
|
||||
addRateTheAppNotification(isReadyToShowRating, clickIntents)
|
||||
}
|
||||
}.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addUsedOutdatedDataNotification(
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ internal class SetCryptoCurrencyActionsTransformer(
|
|||
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
|
||||
onClick = {
|
||||
clickIntents.onBuyClick(
|
||||
portfolioId = portfolioId,
|
||||
userWalletId = portfolioId.userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
unavailabilityReason = action.unavailabilityReason,
|
||||
)
|
||||
|
|
@ -62,7 +62,10 @@ internal class SetCryptoCurrencyActionsTransformer(
|
|||
enabled = true,
|
||||
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
|
||||
onClick = {
|
||||
clickIntents.onReceiveClick(portfolioId, cryptoCurrencyStatus = cryptoCurrencyStatus)
|
||||
clickIntents.onReceiveClick(
|
||||
portfolioId.userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
)
|
||||
},
|
||||
onLongClick = {
|
||||
clickIntents.onCopyAddressLongClick(cryptoCurrencyStatus = cryptoCurrencyStatus)
|
||||
|
|
@ -87,7 +90,7 @@ internal class SetCryptoCurrencyActionsTransformer(
|
|||
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
|
||||
onClick = {
|
||||
clickIntents.onSendClick(
|
||||
portfolioId = portfolioId,
|
||||
userWalletId = portfolioId.userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
unavailabilityReason = action.unavailabilityReason,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import com.tangem.core.ui.format.bigdecimal.crypto
|
|||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.card.common.util.getCardsCount
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.visa.exception.RefreshTokenExpiredException
|
||||
|
|
@ -153,7 +152,7 @@ internal class SetVisaInfoTransformer(
|
|||
dimContent = false,
|
||||
onClick = {
|
||||
clickIntents.onReceiveClick(
|
||||
portfolioId = PortfolioId(userWalletId), // todo account Visa use Main account?
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
event = MainScreenAnalyticsEvent.ButtonReceive,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
|
||||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderStatus.CANCELED
|
||||
import com.tangem.domain.pay.model.OrderStatus.UNKNOWN
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createIssueAvailableState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createIssueProgressState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createKycInProgressState
|
||||
import java.util.Currency
|
||||
|
||||
/**
|
||||
* Hardcode Polygon chain id only for F&F.
|
||||
* Later chain id will be fetched from BFF.
|
||||
*/
|
||||
private const val POLYGON_CHAIN_ID = 137
|
||||
|
||||
internal class TangemPayInitialStateTransformer(
|
||||
private val value: MainScreenCustomerInfo? = null,
|
||||
private val onClickIssue: () -> Unit = {},
|
||||
private val onClickKyc: () -> Unit = {},
|
||||
private val openDetails: (config: TangemPayDetailsConfig) -> Unit = {},
|
||||
) : WalletScreenStateTransformer {
|
||||
|
||||
override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||
val tangemPayState = createInitialState()
|
||||
return prevState.copy(tangemPayState = tangemPayState)
|
||||
}
|
||||
|
||||
private fun createInitialState(): TangemPayState {
|
||||
val cardInfo = value?.info?.cardInfo
|
||||
return when {
|
||||
value == null -> TangemPayState.Empty
|
||||
!value.info.isKycApproved -> createKycInProgressState(onClickKyc)
|
||||
cardInfo != null -> getCardInfoState(cardInfo)
|
||||
value.orderStatus == UNKNOWN || value.orderStatus == CANCELED -> createIssueAvailableState(onClickIssue)
|
||||
else -> createIssueProgressState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCardInfoState(cardInfo: CardInfo): TangemPayState = TangemPayState.Card(
|
||||
lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"),
|
||||
balanceText = TextReference.Str(getBalanceText(cardInfo)),
|
||||
onClick = {
|
||||
openDetails(
|
||||
TangemPayDetailsConfig(
|
||||
customerWalletAddress = cardInfo.customerWalletAddress,
|
||||
cardNumberEnd = cardInfo.lastFourDigits,
|
||||
chainId = POLYGON_CHAIN_ID,
|
||||
depositAddress = cardInfo.depositAddress,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
private fun getBalanceText(cardInfo: CardInfo): String {
|
||||
val currency = Currency.getInstance(cardInfo.currencyCode)
|
||||
return cardInfo.balance.format {
|
||||
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createIssueAvailableState
|
||||
|
||||
internal class TangemPayIssueAvailableStateTransformer(
|
||||
private val onClickIssue: () -> Unit = {},
|
||||
) : WalletScreenStateTransformer {
|
||||
|
||||
override fun transform(prevState: WalletScreenState): WalletScreenState =
|
||||
prevState.copy(tangemPayState = createIssueAvailableState(onClickIssue))
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createIssueProgressState
|
||||
|
||||
internal class TangemPayIssueProgressStateTransformer : WalletScreenStateTransformer {
|
||||
|
||||
override fun transform(prevState: WalletScreenState): WalletScreenState =
|
||||
prevState.copy(tangemPayState = createIssueProgressState())
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
|
||||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderStatus.CANCELED
|
||||
import com.tangem.domain.pay.model.OrderStatus.NOT_ISSUED
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import java.util.Currency
|
||||
|
||||
internal class TangemPayStateTransformer(
|
||||
private val value: MainScreenCustomerInfo? = null,
|
||||
private val onIssueOrderClick: () -> Unit = {},
|
||||
private val onContinueKycClick: () -> Unit = {},
|
||||
private val openDetails: (customerWalletAddress: String, cardNumberEnd: String) -> Unit = { _, _ -> },
|
||||
private val issueProgressState: Boolean = false,
|
||||
private val issueState: Boolean = false,
|
||||
) : WalletScreenStateTransformer {
|
||||
|
||||
override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||
val tangemPayState = when {
|
||||
issueProgressState -> createIssueProgressState()
|
||||
issueState -> createIssueState()
|
||||
else -> createInitialState()
|
||||
}
|
||||
return prevState.copy(tangemPayState = tangemPayState)
|
||||
}
|
||||
|
||||
private fun createInitialState(): TangemPayState {
|
||||
val cardInfo = value?.info?.cardInfo
|
||||
return when {
|
||||
value == null -> TangemPayState.Empty
|
||||
!value.info.isKycApproved() -> createKycInProgressState(onContinueKycClick)
|
||||
cardInfo != null -> getCardInfoState(cardInfo)
|
||||
value.orderStatus == NOT_ISSUED || value.orderStatus == CANCELED -> createIssueState()
|
||||
else -> createIssueProgressState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createIssueProgressState(): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_issue_card_notification_title),
|
||||
buttonText = TextReference.EMPTY,
|
||||
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
|
||||
onButtonClick = {},
|
||||
showProgress = true,
|
||||
)
|
||||
|
||||
private fun createIssueState() = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_issue_card_notification_title),
|
||||
buttonText = TextReference.Res(R.string.common_continue),
|
||||
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
|
||||
onButtonClick = onIssueOrderClick,
|
||||
)
|
||||
|
||||
private fun createKycInProgressState(onContinueKycClick: () -> Unit): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_title),
|
||||
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
|
||||
iconRes = R.drawable.ic_promo_kyc_36,
|
||||
onButtonClick = onContinueKycClick,
|
||||
)
|
||||
|
||||
private fun getCardInfoState(cardInfo: CardInfo): TangemPayState = TangemPayState.Card(
|
||||
lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"),
|
||||
balanceText = TextReference.Str(getBalanceText(cardInfo)),
|
||||
onClick = { openDetails(cardInfo.customerWalletAddress, cardInfo.lastFourDigits) },
|
||||
)
|
||||
|
||||
private fun getBalanceText(cardInfo: CardInfo): String {
|
||||
val currency = Currency.getInstance(cardInfo.currencyCode)
|
||||
return cardInfo.balance.format {
|
||||
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,9 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletCurrencyActionsClickIntents
|
||||
|
|
@ -18,10 +18,11 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
|
||||
internal class MultiWalletCurrencyActionsConverter(
|
||||
private val userWallet: UserWallet,
|
||||
private val portfolioId: PortfolioId,
|
||||
private val clickIntents: WalletCurrencyActionsClickIntents,
|
||||
) : Converter<TokenActionsState, ImmutableList<TokenActionButtonConfig>> {
|
||||
|
||||
private val userWalletId: UserWalletId = userWallet.walletId
|
||||
|
||||
override fun convert(value: TokenActionsState): ImmutableList<TokenActionButtonConfig> {
|
||||
return value.states
|
||||
.filterIfSingleWithToken()
|
||||
|
|
@ -56,17 +57,17 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
is TokenActionsState.ActionState.Buy -> {
|
||||
title = resourceReference(R.string.common_buy)
|
||||
icon = R.drawable.ic_plus_24
|
||||
action = { clickIntents.onBuyClick(portfolioId, cryptoCurrencyStatus, noneReason) }
|
||||
action = { clickIntents.onBuyClick(userWalletId, cryptoCurrencyStatus, noneReason) }
|
||||
}
|
||||
is TokenActionsState.ActionState.Receive -> {
|
||||
title = resourceReference(R.string.common_receive)
|
||||
icon = R.drawable.ic_arrow_down_24
|
||||
action = { clickIntents.onReceiveClick(portfolioId, cryptoCurrencyStatus) }
|
||||
action = { clickIntents.onReceiveClick(userWalletId, cryptoCurrencyStatus) }
|
||||
}
|
||||
is TokenActionsState.ActionState.Stake -> {
|
||||
title = resourceReference(R.string.common_stake)
|
||||
icon = R.drawable.ic_staking_24
|
||||
action = { clickIntents.onStakeClick(portfolioId, cryptoCurrencyStatus, actionsState.yield) }
|
||||
action = { clickIntents.onStakeClick(userWalletId, cryptoCurrencyStatus, actionsState.yield) }
|
||||
}
|
||||
is TokenActionsState.ActionState.Sell -> {
|
||||
title = resourceReference(R.string.common_sell)
|
||||
|
|
@ -76,7 +77,7 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
is TokenActionsState.ActionState.Send -> {
|
||||
title = resourceReference(R.string.common_send)
|
||||
icon = R.drawable.ic_arrow_up_24
|
||||
action = { clickIntents.onSendClick(portfolioId, cryptoCurrencyStatus, noneReason) }
|
||||
action = { clickIntents.onSendClick(userWalletId, cryptoCurrencyStatus, noneReason) }
|
||||
}
|
||||
is TokenActionsState.ActionState.Swap -> {
|
||||
title = resourceReference(R.string.swapping_swap_action)
|
||||
|
|
@ -84,7 +85,7 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
action = {
|
||||
clickIntents.onSwapClick(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
portfolioId = portfolioId,
|
||||
userWalletId = userWalletId,
|
||||
unavailabilityReason = noneReason,
|
||||
)
|
||||
}
|
||||
|
|
@ -92,12 +93,12 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
is TokenActionsState.ActionState.CopyAddress -> {
|
||||
title = resourceReference(R.string.common_copy_address)
|
||||
icon = R.drawable.ic_copy_24
|
||||
action = { clickIntents.onCopyAddressClick(portfolioId, cryptoCurrencyStatus) }
|
||||
action = { clickIntents.onCopyAddressClick(userWalletId, cryptoCurrencyStatus) }
|
||||
}
|
||||
is TokenActionsState.ActionState.HideToken -> {
|
||||
title = resourceReference(R.string.token_details_hide_token)
|
||||
icon = R.drawable.ic_hide_24
|
||||
action = { clickIntents.onHideTokensClick(portfolioId, cryptoCurrencyStatus) }
|
||||
action = { clickIntents.onHideTokensClick(userWalletId, cryptoCurrencyStatus) }
|
||||
}
|
||||
is TokenActionsState.ActionState.Analytics -> {
|
||||
title = resourceReference(R.string.common_analytics)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert
|
|||
|
||||
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
|
||||
import com.tangem.common.ui.tokens.TokenItemStateConverter
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -14,7 +13,6 @@ import com.tangem.domain.models.TotalFiatBalance
|
|||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.tokenlist.TokenList.GroupedByNetwork.NetworkGroup
|
||||
|
|
@ -36,23 +34,22 @@ internal class TokenListStateConverter(
|
|||
private val params: TokenConverterParams,
|
||||
private val selectedWallet: UserWallet,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val apyMap: Map<String, String> = emptyMap(),
|
||||
private val apyMap: Map<String, String>,
|
||||
) : Converter<WalletTokensListState, WalletTokensListState> {
|
||||
|
||||
private val onTokenClick: (accountId: AccountId?, currencyStatus: CryptoCurrencyStatus) -> Unit =
|
||||
{ accountId, currencyStatus ->
|
||||
val id = accountId?.let { PortfolioId(accountId) } ?: PortfolioId(selectedWallet.walletId)
|
||||
clickIntents.onTokenItemClick(id, currencyStatus)
|
||||
clickIntents.onTokenItemClick(selectedWallet.walletId, currencyStatus)
|
||||
}
|
||||
|
||||
private val onTokenLongClick: (accountId: AccountId?, currencyStatus: CryptoCurrencyStatus) -> Unit =
|
||||
{ accountId, currencyStatus ->
|
||||
val id = accountId?.let { PortfolioId(accountId) } ?: PortfolioId(selectedWallet.walletId)
|
||||
clickIntents.onTokenItemLongClick(id, currencyStatus)
|
||||
clickIntents.onTokenItemLongClick(selectedWallet.walletId, currencyStatus)
|
||||
}
|
||||
|
||||
private fun tokenStatusConverter(accountId: AccountId? = null) = TokenItemStateConverter(
|
||||
appCurrency = appCurrency,
|
||||
apyMap = apyMap,
|
||||
onItemClick = { _, status -> onTokenClick(accountId, status) },
|
||||
onItemLongClick = { _, status -> onTokenLongClick(accountId, status) },
|
||||
)
|
||||
|
|
@ -112,9 +109,10 @@ internal class TokenListStateConverter(
|
|||
is WalletTokensListState.Empty -> listOf()
|
||||
}
|
||||
return TokensListItemUM.Portfolio(
|
||||
state = accountItem,
|
||||
tokenItemUM = accountItem,
|
||||
isExpanded = isExtend,
|
||||
tokens = items.filterIsInstance<PortfolioTokensListItemUM>(),
|
||||
isCollapsable = true,
|
||||
tokens = items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -168,55 +166,13 @@ internal class TokenListStateConverter(
|
|||
tokenConverter: TokenItemStateConverter,
|
||||
token: CryptoCurrencyStatus,
|
||||
): List<TokensListItemUM> {
|
||||
val tokenItemState = tokenConverter.convert(token).withEarnApyBadge(token)
|
||||
val tokenItemState = tokenConverter.convert(token)
|
||||
|
||||
add(TokensListItemUM.Token(tokenItemState))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private fun TokenItemState.withEarnApyBadge(cryptoCurrencyStatus: CryptoCurrencyStatus): TokenItemState {
|
||||
val apy: String? = resolveEarnApy(cryptoCurrencyStatus)
|
||||
|
||||
val shouldApply = apy != null && this.titleState is TokenItemState.TitleState.Content
|
||||
|
||||
return if (!shouldApply) {
|
||||
this
|
||||
} else {
|
||||
val contentTitle = this.titleState as TokenItemState.TitleState.Content
|
||||
val newTitle = contentTitle.copy(
|
||||
earnApy = resourceReference(
|
||||
R.string.yield_module_earn_badge,
|
||||
wrappedList(apy),
|
||||
),
|
||||
)
|
||||
|
||||
when (this) {
|
||||
is TokenItemState.Content -> this.copy(titleState = newTitle)
|
||||
is TokenItemState.Unreachable -> this.copy(titleState = newTitle)
|
||||
is TokenItemState.NoAddress -> this.copy(titleState = newTitle)
|
||||
is TokenItemState.Loading -> this.copy(titleState = newTitle)
|
||||
is TokenItemState.Draggable -> this.copy(titleState = newTitle)
|
||||
is TokenItemState.Locked -> this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveEarnApy(cryptoCurrencyStatus: CryptoCurrencyStatus): String? {
|
||||
if (apyMap.isEmpty()) return null
|
||||
|
||||
val isYieldSupplyActive = (cryptoCurrencyStatus.value as? CryptoCurrencyStatus.Loaded)
|
||||
?.yieldSupplyStatus?.isActive == true
|
||||
if (isYieldSupplyActive) return null
|
||||
|
||||
val contract = (cryptoCurrencyStatus.currency as? CryptoCurrency.Token)
|
||||
?.contractAddress
|
||||
?.lowercase() ?: return null
|
||||
|
||||
val yieldSupplyKey = "${cryptoCurrencyStatus.currency.network.backendId}_$contract"
|
||||
return apyMap[yieldSupplyKey]
|
||||
}
|
||||
|
||||
private fun getOrganizeTokensButtonState(tokenList: TokenList): WalletOrganizeTokensButtonConfig? {
|
||||
val currenciesSize = when (tokenList) {
|
||||
TokenList.Empty -> return null
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.util
|
||||
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress
|
||||
|
||||
internal object TangemPayStateCreator {
|
||||
|
||||
fun createKycInProgressState(onClickKyc: () -> Unit): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_title),
|
||||
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
|
||||
iconRes = R.drawable.ic_promo_kyc_36,
|
||||
onButtonClick = onClickKyc,
|
||||
)
|
||||
|
||||
fun createIssueAvailableState(onClickIssue: () -> Unit) = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_issue_card_notification_title),
|
||||
buttonText = TextReference.Res(R.string.common_continue),
|
||||
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
|
||||
onButtonClick = onClickIssue,
|
||||
)
|
||||
|
||||
fun createIssueProgressState(): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_issue_card_notification_title),
|
||||
buttonText = TextReference.EMPTY,
|
||||
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
|
||||
onButtonClick = {},
|
||||
showProgress = true,
|
||||
)
|
||||
}
|
||||
|
|
@ -8,7 +8,9 @@ import com.tangem.domain.core.lce.Lce
|
|||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.utils.getOrElse
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
|
|
@ -63,7 +65,10 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
flow = tokenListFlow(coroutineScope)
|
||||
.onEach { maybeTokenList ->
|
||||
coroutineScope.launch {
|
||||
sendTokenListAnalytics(maybeTokenList)
|
||||
sendTokenListAnalytics(
|
||||
flattenCurrencies = maybeTokenList.getOrNull()?.flattenCurrencies(),
|
||||
totalFiatBalance = maybeTokenList.getOrNull()?.totalFiatBalance,
|
||||
)
|
||||
}.saveIn(sendAnalyticsJobHolder)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
|
@ -140,12 +145,14 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
|
||||
private fun createAccountListFlow(coroutineScope: CoroutineScope): Flow<*> = combine(
|
||||
flow = accountListFlow(coroutineScope)
|
||||
// todo account analytics for account total balance
|
||||
/*.onEach { maybeTokenList ->
|
||||
.onEach { accountStatusList ->
|
||||
coroutineScope.launch {
|
||||
sendTokenListAnalytics(maybeTokenList)
|
||||
sendTokenListAnalytics(
|
||||
flattenCurrencies = accountStatusList.flattenCurrencies(),
|
||||
totalFiatBalance = accountStatusList.totalFiatBalance,
|
||||
)
|
||||
}.saveIn(sendAnalyticsJobHolder)
|
||||
}*/
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.onEach { accountList ->
|
||||
// todo account see[onAccountListReceived]
|
||||
|
|
@ -207,13 +214,17 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun sendTokenListAnalytics(maybeTokenList: Lce<TokenListError, TokenList>) {
|
||||
private suspend fun sendTokenListAnalytics(
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>?,
|
||||
totalFiatBalance: TotalFiatBalance?,
|
||||
) {
|
||||
val displayedState = stateHolder.getWalletStateIfSelected(userWallet.walletId)
|
||||
|
||||
tokenListAnalyticsSender.send(
|
||||
displayedUiState = displayedState,
|
||||
userWallet = userWallet,
|
||||
tokenList = maybeTokenList.getOrNull() ?: return,
|
||||
flattenCurrencies = flattenCurrencies ?: return,
|
||||
totalFiatBalance = totalFiatBalance ?: return,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.tokenlist.PortfolioListItem
|
||||
import com.tangem.core.ui.components.tokenlist.PortfolioTokensListItem
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
|
|
@ -59,6 +60,7 @@ internal fun LazyListScope.portfolioTokensList(
|
|||
contentType = { _, item -> item::class.java },
|
||||
itemContent = { tokenIndex, token ->
|
||||
val indexWithHeader = tokenIndex.inc()
|
||||
val lastIndex = tokens.lastIndex.inc()
|
||||
val isPreview = LocalInspectionMode.current
|
||||
val appear = remember {
|
||||
MutableTransitionState(isPreview).apply { targetState = true }
|
||||
|
|
@ -69,11 +71,12 @@ internal fun LazyListScope.portfolioTokensList(
|
|||
.animateItem()
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = indexWithHeader,
|
||||
lastIndex = tokens.lastIndex.inc(),
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
),
|
||||
visibleState = appear,
|
||||
) {
|
||||
val modifier = if (indexWithHeader == lastIndex) Modifier.padding(bottom = 8.dp) else Modifier
|
||||
PortfolioTokensListItem(
|
||||
state = token,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
|
|
@ -114,10 +117,15 @@ private fun LazyListScope.portfolioItem(
|
|||
modifier = anchorModifier,
|
||||
visibleState = appear,
|
||||
) {
|
||||
val modifier = if (portfolio.tokens.isEmpty()) {
|
||||
Modifier.padding(vertical = 8.dp)
|
||||
} else {
|
||||
Modifier.padding(top = 8.dp)
|
||||
}
|
||||
PortfolioListItem(
|
||||
state = portfolio,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.utils
|
||||
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.tokens.IsCryptoCurrencyCoinCouldHideUseCase
|
||||
import com.tangem.domain.tokens.RemoveCurrencyUseCase
|
||||
import javax.inject.Inject
|
||||
|
||||
class WalletFeatureUseCasesFacade @Inject constructor(
|
||||
private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase,
|
||||
private val removeCurrencyUseCase: RemoveCurrencyUseCase,
|
||||
) {
|
||||
|
||||
suspend fun isCryptoCurrencyCoinCouldHide(portfolioId: PortfolioId, cryptoCurrencyCoin: CryptoCurrency.Coin) =
|
||||
when (portfolioId) {
|
||||
is PortfolioId.Account -> TODO("account")
|
||||
is PortfolioId.Wallet -> isCryptoCurrencyCoinCouldHide(portfolioId.userWalletId, cryptoCurrencyCoin)
|
||||
}
|
||||
|
||||
suspend fun removeCurrencyUseCase(portfolioId: PortfolioId, currency: CryptoCurrency) = when (portfolioId) {
|
||||
is PortfolioId.Account -> TODO("account")
|
||||
is PortfolioId.Wallet -> removeCurrencyUseCase(portfolioId.userWalletId, currency)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.wallet.utils.UserWalletImageFetcher
|
||||
import com.tangem.operations.attestation.ArtworkSize
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
|
@ -17,12 +18,21 @@ import javax.inject.Inject
|
|||
class DefaultUserWalletImageFetcher @Inject constructor(
|
||||
private val getCardImageUseCase: GetCardImageUseCase,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
private val artworkUMConverter: ArtworkUMConverter,
|
||||
) : UserWalletImageFetcher {
|
||||
|
||||
private val smallCache = MutableStateFlow(mapOf<String, ArtworkUM>())
|
||||
private val largeCache = MutableStateFlow(mapOf<String, ArtworkUM>())
|
||||
|
||||
override fun allWallets(size: ArtworkSize): Flow<Map<UserWalletId, UserWalletItemUM.ImageState>> =
|
||||
getWalletsUseCase.invoke()
|
||||
.map { list -> list.map { it.walletId } }
|
||||
.distinctUntilChanged()
|
||||
.map { list -> list.map { walletId -> walletImage(walletId, size).map { image -> walletId to image } } }
|
||||
.flatMapLatest { flows -> combine(flows) { it.toMap() } }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override fun walletImage(wallet: UserWallet, size: ArtworkSize): Flow<UserWalletItemUM.ImageState> = when (wallet) {
|
||||
is UserWallet.Cold -> walletImage(wallet.scanResponse.card, size)
|
||||
is UserWallet.Hot -> flowOf(UserWalletItemUM.ImageState.MobileWallet)
|
||||
|
|
@ -31,11 +41,8 @@ class DefaultUserWalletImageFetcher @Inject constructor(
|
|||
override fun walletsImage(
|
||||
wallets: Collection<UserWallet>,
|
||||
size: ArtworkSize,
|
||||
): Flow<Map<UserWalletId, UserWalletItemUM.ImageState>> = wallets
|
||||
.map { userWallet -> walletImage(userWallet, size).map { imageState -> userWallet.walletId to imageState } }
|
||||
.merge()
|
||||
.runningFold(mapOf<UserWalletId, UserWalletItemUM.ImageState>()) { map, newState -> map.plus(newState) }
|
||||
.filter { it.size >= wallets.size } // prevent spam, waiting full map
|
||||
): Flow<Map<UserWalletId, UserWalletItemUM.ImageState>> = allWallets(size)
|
||||
.map { allWallets -> allWallets.filter { (walletId, _) -> wallets.any { it.walletId == walletId } } }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override fun walletImage(walletId: UserWalletId, size: ArtworkSize): Flow<UserWalletItemUM.ImageState> = flow {
|
||||
|
|
|
|||
|
|
@ -130,6 +130,11 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
|
|||
balance = balance,
|
||||
isBalanceHidden = balanceHidingSettings.isBalanceHidden,
|
||||
artwork = artworks[userWallet.walletId],
|
||||
endIcon = if (isAuthMode.not() && userWallet is UserWallet.Hot && !userWallet.backedUp) {
|
||||
UserWalletItemUM.EndIcon.Warning
|
||||
} else {
|
||||
UserWalletItemUM.EndIcon.None
|
||||
},
|
||||
isAuthMode = isAuthMode,
|
||||
)
|
||||
.convert(userWallet)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue