Updated on 2026-08-14

This commit is contained in:
Tangem 2024-04-17 12:32:52 +01:00
commit e8ccd67098
295 changed files with 4414 additions and 2971 deletions

View file

@ -0,0 +1,20 @@
package com.tangem.feature.wallet.di
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
import com.tangem.feature.wallet.featuretoggle.WalletFeatureToggles
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object FeatureTogglesModule {
@Provides
@Singleton
fun provideWalletFeatureToggles(featureTogglesManager: FeatureTogglesManager): WalletFeatureToggles {
return WalletFeatureToggles(featureTogglesManager)
}
}

View file

@ -0,0 +1,11 @@
package com.tangem.feature.wallet.featuretoggle
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
internal class WalletFeatureToggles(
private val featureTogglesManager: FeatureTogglesManager,
) {
val isTokenListLceFlowEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled("TOKEN_LIST_LCE_ENABLED")
}

View file

@ -7,12 +7,14 @@ import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.core.utils.getOrElse
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.ToggleTokenListGroupingUseCase
import com.tangem.domain.tokens.ToggleTokenListSortingUseCase
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.featuretoggle.WalletFeatureToggles
import com.tangem.feature.wallet.presentation.organizetokens.analytics.PortfolioOrganizeTokensAnalyticsEvent
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
@ -39,6 +41,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val analyticsEventsHandler: AnalyticsEventHandler,
private val walletFeatureToggles: WalletFeatureToggles,
private val dispatchers: CoroutineDispatcherProvider,
savedStateHandle: SavedStateHandle,
) : ViewModel(), DefaultLifecycleObserver, OrganizeTokensIntents {
@ -65,7 +68,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
UserWalletId(userWalletIdValue)
}
private var tokenList: TokenList? = null
private var cachedTokenList: TokenList? = null
val uiState: StateFlow<OrganizeTokensState> = stateHolder.stateFlow
@ -89,7 +92,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
}
override fun onSortClick() {
val list = tokenList ?: return
val list = cachedTokenList ?: return
if (list.sortedBy == TokenList.SortType.BALANCE) return
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance)
@ -99,14 +102,14 @@ internal class OrganizeTokensViewModel @Inject constructor(
ifLeft = stateHolder::updateStateWithError,
ifRight = {
stateHolder.updateStateAfterTokenListSorting(it)
tokenList = it
cachedTokenList = it
},
)
}
}
override fun onGroupClick() {
val list = tokenList ?: return
val list = cachedTokenList ?: return
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group)
@ -115,7 +118,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
ifLeft = stateHolder::updateStateWithError,
ifRight = {
stateHolder.updateStateAfterTokenListSorting(it)
tokenList = it
cachedTokenList = it
},
)
}
@ -138,7 +141,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
val result = applyTokenListSortingUseCase(
userWalletId = userWalletId,
sortedTokensIds = resolver.resolve(listState, tokenList),
sortedTokensIds = resolver.resolve(listState, cachedTokenList),
isGroupedByNetwork = isGroupedByNetwork,
isSortedByBalance = isSortedByBalance,
)
@ -161,16 +164,41 @@ internal class OrganizeTokensViewModel @Inject constructor(
private fun bootstrapTokenList() {
viewModelScope.launch(dispatchers.default) {
val maybeTokenList = getTokenListUseCase(userWalletId)
.first { it.getOrNull()?.totalFiatBalance !is TokenList.FiatBalance.Loading }
val tokenList = getTokenList() ?: return@launch
maybeTokenList.fold(
ifLeft = stateHolder::updateStateWithError,
ifRight = {
stateHolder.updateStateWithTokenList(it)
tokenList = it
},
)
stateHolder.updateStateWithTokenList(tokenList)
cachedTokenList = tokenList
}
}
private suspend fun getTokenList(): TokenList? {
return if (walletFeatureToggles.isTokenListLceFlowEnabled) {
val tokenList = getTokenListUseCase.launchLce(userWalletId)
.transform { maybeTokenList ->
val tokenList = maybeTokenList.getOrElse(
ifLoading = { return@transform },
ifError = { error ->
stateHolder.updateStateWithError(error)
return@transform
},
)
emit(tokenList)
}
tokenList.firstOrNull()
} else {
val maybeTokenList = getTokenListUseCase.launch(userWalletId)
.first { maybeTokenList ->
maybeTokenList.getOrNull()?.totalFiatBalance !is TokenList.FiatBalance.Loading
}
maybeTokenList.getOrElse { error ->
stateHolder.updateStateWithError(error)
null
}
}
}
@ -189,7 +217,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
if (dragOperationType !is DragAndDropAdapter.DragOperation.Type.End) return
if (uiState.value.header.isSortedByBalance && dragOperationType.isItemsOrderChanged) {
tokenList = tokenList?.disableSortingByBalance()
cachedTokenList = cachedTokenList?.disableSortingByBalance()
stateHolder.disableSortingByBalance()
}
}

View file

@ -2,12 +2,12 @@ package com.tangem.feature.wallet.presentation.wallet.analytics.utils
import arrow.core.getOrElse
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.common.extensions.isZero
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.analytics.CheckIsWalletToppedUpUseCase
import com.tangem.domain.analytics.model.WalletBalanceState
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.NetworkGroup
import com.tangem.domain.tokens.model.TokenList

View file

@ -47,7 +47,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val promoFlow = flow { emit(promoRepository.getChangellyPromoBanner()) }
return combine(
flow = getTokenListUseCase(userWallet.walletId).conflate(),
flow = getTokenListUseCase.launch(userWallet.walletId).conflate(),
flow2 = isReadyToShowRateAppUseCase().conflate(),
flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(),
flow4 = shouldShowSwapPromoWalletUseCase().conflate(),

View file

@ -1,11 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.featuretoggle.WalletFeatureToggles
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
@ -13,7 +13,6 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsCheck
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletTokenListSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletConnectNetworksSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
@ -29,7 +28,7 @@ internal class MultiWalletContentLoader(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val reduxStateHolder: ReduxStateHolder,
private val walletFeatureToggles: WalletFeatureToggles,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
) : WalletContentLoader(id = userWallet.walletId) {
@ -43,6 +42,7 @@ internal class MultiWalletContentLoader(
walletWithFundsChecker = walletWithFundsChecker,
getTokenListUseCase = getTokenListUseCase,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
walletFeatureToggles = walletFeatureToggles,
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
),
@ -53,11 +53,6 @@ internal class MultiWalletContentLoader(
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
),
WalletConnectNetworksSubscriber(
userWallet = userWallet,
getTokenListUseCase = getTokenListUseCase,
reduxStateHolder = reduxStateHolder,
),
)
}
}

View file

@ -1,11 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.featuretoggle.WalletFeatureToggles
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
@ -25,8 +25,8 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
private val getTokenListUseCase: GetTokenListUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
private val reduxStateHolder: ReduxStateHolder,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletFeatureToggles: WalletFeatureToggles,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
) {
@ -40,9 +40,9 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
getTokenListUseCase = getTokenListUseCase,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
reduxStateHolder = reduxStateHolder,
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
walletFeatureToggles = walletFeatureToggles,
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
)
}

View file

@ -28,7 +28,7 @@ internal sealed interface WalletAlertState {
}
data class DefaultAlert(
override val title: TextReference,
override val title: TextReference?,
override val message: TextReference,
override val onConfirmClick: (() -> Unit)?,
) : Basic()

View file

@ -18,6 +18,9 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
/** Is click enabled */
abstract val enabled: Boolean
/** Whether to dim content */
abstract val dimContent: Boolean
/** Lambda be invoked when manage button is clicked */
abstract val onClick: () -> Unit
@ -25,29 +28,40 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
* Buy
*
* @property enabled button click availability
* @property dimContent determines whether the button content will be dimmed
* @property onClick lambda be invoked when Buy button is clicked
*/
data class Buy(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_buy),
iconResId = R.drawable.ic_plus_24,
onClick = onClick,
enabled = enabled,
),
)
data class Buy(
override val enabled: Boolean,
override val dimContent: Boolean,
override val onClick: () -> Unit,
) :
WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_buy),
iconResId = R.drawable.ic_plus_24,
onClick = onClick,
dimContent = dimContent,
),
)
/**
* Send
*
* @property enabled button click availability
* @property dimContent determines whether the button content will be dimmed
* @property onClick lambda be invoked when Send button is clicked
*/
data class Send(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton(
data class Send(
override val enabled: Boolean,
override val dimContent: Boolean,
override val onClick: () -> Unit,
) : WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_send),
iconResId = R.drawable.ic_arrow_up_24,
onClick = onClick,
enabled = enabled,
dimContent = dimContent,
),
)
@ -56,12 +70,16 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
*
* @property onClick lambda be invoked when Receive button is clicked
*/
data class Receive(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton(
data class Receive(
override val enabled: Boolean,
override val dimContent: Boolean,
override val onClick: () -> Unit,
) : WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_receive),
iconResId = R.drawable.ic_arrow_down_24,
onClick = onClick,
enabled = enabled,
dimContent = dimContent,
),
)
@ -69,14 +87,19 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
* Sell
*
* @property enabled button click availability
* @property dimContent determines whether the button content will be dimmed
* @property onClick lambda be invoked when Sell button is clicked
*/
data class Sell(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton(
data class Sell(
override val enabled: Boolean,
override val dimContent: Boolean,
override val onClick: () -> Unit,
) : WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_sell),
iconResId = R.drawable.ic_currency_24,
onClick = onClick,
enabled = enabled,
dimContent = dimContent,
),
)
@ -84,14 +107,19 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
* Swap
*
* @property enabled button click availability
* @property dimContent determines whether the button content will be dimmed
* @property onClick lambda be invoked when Swap button is clicked
*/
data class Swap(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton(
data class Swap(
override val enabled: Boolean,
override val dimContent: Boolean,
override val onClick: () -> Unit,
) : WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.swapping_swap_action),
iconResId = R.drawable.ic_exchange_vertical_24,
onClick = onClick,
enabled = enabled,
dimContent = dimContent,
),
)
}

View file

@ -85,10 +85,10 @@ internal class InitializeWalletsTransformer(
private fun createDisabledButtons(): PersistentList<WalletManageButton> {
return persistentListOf(
WalletManageButton.Buy(enabled = false, onClick = {}),
WalletManageButton.Send(enabled = false, onClick = {}),
WalletManageButton.Receive(enabled = false, onClick = {}),
WalletManageButton.Sell(enabled = false, onClick = {}),
WalletManageButton.Receive(enabled = false, dimContent = false, onClick = {}),
WalletManageButton.Send(enabled = false, dimContent = false, onClick = {}),
WalletManageButton.Buy(enabled = false, dimContent = false, onClick = {}),
WalletManageButton.Sell(enabled = false, dimContent = false, onClick = {}),
)
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
@ -43,26 +44,47 @@ internal class SetCryptoCurrencyActionsTransformer(
when (action) {
is TokenActionsState.ActionState.Buy -> {
WalletManageButton.Buy(
enabled = action.enabled,
onClick = { clickIntents.onBuyClick(cryptoCurrencyStatus) },
enabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None,
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
onClick = {
clickIntents.onBuyClick(
cryptoCurrencyStatus = cryptoCurrencyStatus,
unavailabilityReason = action.unavailabilityReason,
)
},
)
}
is TokenActionsState.ActionState.Receive -> {
WalletManageButton.Receive(
enabled = action.enabled,
onClick = { clickIntents.onReceiveClick(cryptoCurrencyStatus) },
enabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None,
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
onClick = {
clickIntents.onReceiveClick(cryptoCurrencyStatus = cryptoCurrencyStatus)
},
)
}
is TokenActionsState.ActionState.Sell -> {
WalletManageButton.Sell(
enabled = action.enabled,
onClick = { clickIntents.onSellClick(cryptoCurrencyStatus) },
enabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None,
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
onClick = {
clickIntents.onSellClick(
cryptoCurrencyStatus = cryptoCurrencyStatus,
unavailabilityReason = action.unavailabilityReason,
)
},
)
}
is TokenActionsState.ActionState.Send -> {
WalletManageButton.Send(
enabled = action.enabled,
onClick = { clickIntents.onSendClick(cryptoCurrencyStatus) },
enabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None,
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
onClick = {
clickIntents.onSendClick(
cryptoCurrencyStatus = cryptoCurrencyStatus,
unavailabilityReason = action.unavailabilityReason,
)
},
)
}
else -> {

View file

@ -3,6 +3,7 @@ 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.common.util.cardTypesResolver
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.wallets.models.UserWallet
@ -51,7 +52,7 @@ internal class MultiWalletCurrencyActionsConverter(
is TokenActionsState.ActionState.Buy -> {
title = resourceReference(R.string.common_buy)
icon = R.drawable.ic_plus_24
action = { clickIntents.onBuyClick(cryptoCurrencyStatus) }
action = { clickIntents.onBuyClick(cryptoCurrencyStatus, ScenarioUnavailabilityReason.None) }
}
is TokenActionsState.ActionState.Receive -> {
title = resourceReference(R.string.common_receive)
@ -61,17 +62,17 @@ internal class MultiWalletCurrencyActionsConverter(
is TokenActionsState.ActionState.Sell -> {
title = resourceReference(R.string.common_sell)
icon = R.drawable.ic_currency_24
action = { clickIntents.onSellClick(cryptoCurrencyStatus) }
action = { clickIntents.onSellClick(cryptoCurrencyStatus, ScenarioUnavailabilityReason.None) }
}
is TokenActionsState.ActionState.Send -> {
title = resourceReference(R.string.common_send)
icon = R.drawable.ic_arrow_up_24
action = { clickIntents.onSendClick(cryptoCurrencyStatus) }
action = { clickIntents.onSendClick(cryptoCurrencyStatus, ScenarioUnavailabilityReason.None) }
}
is TokenActionsState.ActionState.Swap -> {
title = resourceReference(R.string.swapping_swap_action)
icon = R.drawable.ic_exchange_horizontal_24
action = { clickIntents.onSwapClick(cryptoCurrencyStatus) }
action = { clickIntents.onSwapClick(cryptoCurrencyStatus, ScenarioUnavailabilityReason.None) }
}
is TokenActionsState.ActionState.CopyAddress -> {
title = resourceReference(R.string.common_copy_address)
@ -90,7 +91,7 @@ internal class MultiWalletCurrencyActionsConverter(
iconResId = icon,
onClick = action,
isWarning = actionsState is TokenActionsState.ActionState.HideToken,
enabled = actionsState.enabled,
enabled = actionsState.unavailabilityReason == ScenarioUnavailabilityReason.None,
)
}
}

View file

@ -10,7 +10,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.utils.converter.Converter
internal class SingleWalletCardStateConverter(
private val status: CryptoCurrencyStatus.Status,
private val status: CryptoCurrencyStatus.Value,
private val selectedWallet: UserWallet,
private val appCurrency: AppCurrency,
) : Converter<WalletCardState, WalletCardState> {
@ -50,7 +50,7 @@ internal class SingleWalletCardStateConverter(
)
}
private fun WalletCardState.toContentState(status: CryptoCurrencyStatus.Status): WalletCardState {
private fun WalletCardState.toContentState(status: CryptoCurrencyStatus.Value): WalletCardState {
return WalletCardState.Content(
id = id,
title = title,
@ -66,7 +66,7 @@ internal class SingleWalletCardStateConverter(
)
}
private fun formatFiatAmount(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String {
private fun formatFiatAmount(status: CryptoCurrencyStatus.Value, appCurrency: AppCurrency): String {
val fiatAmount = status.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatFiatAmount(

View file

@ -10,7 +10,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.utils.converter.Converter
internal class SingleWalletMarketPriceConverter(
private val status: CryptoCurrencyStatus.Status,
private val status: CryptoCurrencyStatus.Value,
private val appCurrency: AppCurrency,
) : Converter<MarketPriceBlockState, MarketPriceBlockState> {
@ -44,7 +44,7 @@ internal class SingleWalletMarketPriceConverter(
)
}
private fun formatPrice(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String {
private fun formatPrice(status: CryptoCurrencyStatus.Value, appCurrency: AppCurrency): String {
val fiatRate = status.fiatRate ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatFiatAmount(
@ -54,13 +54,13 @@ internal class SingleWalletMarketPriceConverter(
)
}
private fun formatPriceChange(status: CryptoCurrencyStatus.Status): String {
private fun formatPriceChange(status: CryptoCurrencyStatus.Value): String {
val priceChange = status.priceChange ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatPercent(percent = priceChange, useAbsoluteValue = true)
}
private fun getPriceChangeType(status: CryptoCurrencyStatus.Status): PriceChangeType {
private fun getPriceChangeType(status: CryptoCurrencyStatus.Value): PriceChangeType {
return PriceChangeConverter.fromBigDecimal(status.priceChange)
}
}

View file

@ -45,7 +45,7 @@ internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIn
walletCardState = userWallet.toLoadingWalletCardState(),
warnings = persistentListOf(),
bottomSheetConfig = null,
buttons = createDisabledButtons(),
buttons = createDimmedButtons(),
marketPriceBlockState = MarketPriceBlockState.Loading(currencySymbol = currencySymbol),
txHistoryState = TxHistoryState.Content(
contentItems = MutableStateFlow(
@ -86,12 +86,12 @@ internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIn
)
}
private fun createDisabledButtons(): PersistentList<WalletManageButton> {
private fun createDimmedButtons(): PersistentList<WalletManageButton> {
return persistentListOf(
WalletManageButton.Buy(enabled = false, onClick = {}),
WalletManageButton.Send(enabled = false, onClick = {}),
WalletManageButton.Receive(enabled = false, onClick = {}),
WalletManageButton.Sell(enabled = false, onClick = {}),
WalletManageButton.Receive(enabled = true, dimContent = true, onClick = {}),
WalletManageButton.Send(enabled = true, dimContent = true, onClick = {}),
WalletManageButton.Buy(enabled = true, dimContent = true, onClick = {}),
WalletManageButton.Sell(enabled = true, dimContent = true, onClick = {}),
)
}
}

View file

@ -1,9 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import arrow.core.Either
import arrow.core.getOrElse
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
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.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.NetworkGroup
@ -25,8 +27,6 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
internal typealias MaybeTokenListFlow = Flow<Either<TokenListError, TokenList>>
@Suppress("LongParameterList")
internal abstract class BasicTokenListSubscriber(
private val userWallet: UserWallet,
@ -41,7 +41,7 @@ internal abstract class BasicTokenListSubscriber(
private val sendAnalyticsJobHolder = JobHolder()
private val onTokenListReceivedJobHolder = JobHolder()
protected abstract fun tokenListFlow(): MaybeTokenListFlow
protected abstract fun tokenListFlow(): LceFlow<TokenListError, TokenList>
override fun create(coroutineScope: CoroutineScope): Flow<*> {
return combine(
@ -61,11 +61,17 @@ internal abstract class BasicTokenListSubscriber(
},
flow2 = getSelectedAppCurrencyUseCase().distinctUntilChanged(),
transform = { maybeTokenList, maybeAppCurrency ->
val tokenList = maybeTokenList.getOrElse { e ->
Timber.e("Failed to load token list: $e")
SetTokenListErrorTransformer(userWallet.walletId, e)
return@combine
}
val tokenList = maybeTokenList.getOrElse(
ifLoading = { maybeContent ->
maybeContent ?: return@combine
},
ifError = { e ->
Timber.e("Failed to load token list: $e")
SetTokenListErrorTransformer(userWallet.walletId, e)
return@combine
},
)
val appCurrency = maybeAppCurrency.getOrElse { e ->
Timber.e("Failed to load app currency: $e")
AppCurrency.Default
@ -77,7 +83,7 @@ internal abstract class BasicTokenListSubscriber(
)
}
private suspend fun startCheck(maybeTokenList: Either<TokenListError, TokenList>) {
private suspend fun startCheck(maybeTokenList: Lce<TokenListError, TokenList>) {
// Run Polkadot account health check
maybeTokenList.getOrNull()?.let { tokenList ->
val cryptoCurrencies = when (tokenList) {
@ -92,17 +98,17 @@ internal abstract class BasicTokenListSubscriber(
}
}
protected open suspend fun onTokenListReceived(maybeTokenList: Either<TokenListError, TokenList>) {
protected open suspend fun onTokenListReceived(maybeTokenList: Lce<TokenListError, TokenList>) {
/* no-op */
}
private suspend fun sendTokenListAnalytics(maybeTokenList: Either<TokenListError, TokenList>) {
private suspend fun sendTokenListAnalytics(maybeTokenList: Lce<TokenListError, TokenList>) {
val displayedState = stateHolder.getWalletStateIfSelected(userWallet.walletId)
tokenListAnalyticsSender.send(
displayedUiState = displayedState,
userWallet = userWallet,
tokenList = maybeTokenList.getOrElse { return },
tokenList = maybeTokenList.getOrNull() ?: return,
)
}

View file

@ -1,8 +1,9 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import arrow.core.Either
import arrow.core.getOrElse
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.core.lce.Lce
import com.tangem.domain.core.lce.LceFlow
import com.tangem.domain.core.utils.toLce
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
@ -10,16 +11,19 @@ import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.featuretoggle.WalletFeatureToggles
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import kotlinx.coroutines.flow.map
@Suppress("LongParameterList")
internal class MultiWalletTokenListSubscriber(
private val userWallet: UserWallet,
private val getTokenListUseCase: GetTokenListUseCase,
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
private val walletFeatureToggles: WalletFeatureToggles,
stateHolder: WalletStateController,
clickIntents: WalletClickIntents,
tokenListAnalyticsSender: TokenListAnalyticsSender,
@ -36,16 +40,20 @@ internal class MultiWalletTokenListSubscriber(
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
) {
override fun tokenListFlow(): MaybeTokenListFlow = getTokenListUseCase(userWallet.walletId)
override suspend fun onTokenListReceived(maybeTokenList: Either<TokenListError, TokenList>) {
// TODO disabled for 5.7.2 because of potential critical
// updateSortingIfNeeded(maybeTokenList)
override fun tokenListFlow(): LceFlow<TokenListError, TokenList> {
return if (walletFeatureToggles.isTokenListLceFlowEnabled) {
getTokenListUseCase.launchLce(userWallet.walletId)
} else {
getTokenListUseCase.launch(userWallet.walletId).map { it.toLce() }
}
}
@Suppress("UnusedPrivateMember")
private suspend fun updateSortingIfNeeded(maybeTokenList: Either<TokenListError, TokenList>) {
val tokenList = maybeTokenList.getOrElse { return }
override suspend fun onTokenListReceived(maybeTokenList: Lce<TokenListError, TokenList>) {
updateSortingIfNeeded(maybeTokenList)
}
private suspend fun updateSortingIfNeeded(maybeTokenList: Lce<TokenListError, TokenList>) {
val tokenList = maybeTokenList.getOrNull() ?: return
if (!checkNeedSorting(tokenList)) return
applyTokenListSortingUseCase(

View file

@ -1,13 +1,18 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.core.lce.LceFlow
import com.tangem.domain.core.utils.toLce
import com.tangem.domain.tokens.GetCardTokensListUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import kotlinx.coroutines.flow.map
@Suppress("LongParameterList")
internal class SingleWalletWithTokenListSubscriber(
@ -29,5 +34,6 @@ internal class SingleWalletWithTokenListSubscriber(
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
) {
override fun tokenListFlow(): MaybeTokenListFlow = getCardTokensListUseCase(userWallet.walletId)
override fun tokenListFlow(): LceFlow<TokenListError, TokenList> = getCardTokensListUseCase(userWallet.walletId)
.map { it.toLce() }
}

View file

@ -1,80 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import arrow.core.Either
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.NetworkGroup
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.walletconnect.WalletConnectActions
import com.tangem.domain.wallets.models.UserWallet
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import timber.log.Timber
/**
* WalletConnect networks subscriber. Update WalletConnect networks for a specified [userWallet].
*
* @property userWallet user wallet
* @property getTokenListUseCase use case for subscribing on token list changes
* @property reduxStateHolder redux state holder
*
[REDACTED_AUTHOR]
*/
internal class WalletConnectNetworksSubscriber(
private val userWallet: UserWallet,
private val getTokenListUseCase: GetTokenListUseCase,
private val reduxStateHolder: ReduxStateHolder,
) : WalletSubscriber() {
private val mutex = Mutex()
override fun create(coroutineScope: CoroutineScope): Flow<Either<TokenListError, TokenList>> {
return getTokenListUseCase(userWalletId = userWallet.walletId)
.conflate()
.distinctUntilCurrenciesChanged()
.filterLoadedTokens()
.onEach {
mutex.withLock {
Timber.d("WalletConnect: ${userWallet.walletId} networks is updated")
reduxStateHolder.dispatch(
action = WalletConnectActions.New.SetupUserChains(userWallet = userWallet),
)
}
}
}
private fun MaybeTokenListFlow.distinctUntilCurrenciesChanged(): MaybeTokenListFlow {
return distinctUntilChanged { old, new ->
val oldCurrencies = old.fold(ifLeft = { null }, ifRight = { it.getCryptoCurrencies() })
val newCurrencies = new.fold(ifLeft = { null }, ifRight = { it.getCryptoCurrencies() })
oldCurrencies == newCurrencies
}
}
private fun MaybeTokenListFlow.filterLoadedTokens(): MaybeTokenListFlow {
return filter { either ->
either.fold(
ifLeft = { false },
ifRight = { it.getCryptoCurrencies().isAllCurrenciesLoaded() },
)
}
}
private fun TokenList.getCryptoCurrencies(): List<CryptoCurrencyStatus> {
return when (this) {
is TokenList.Ungrouped -> currencies
is TokenList.GroupedByNetwork -> groups.flatMap(NetworkGroup::currencies)
else -> emptyList()
}
}
private fun List<CryptoCurrencyStatus>.isAllCurrenciesLoaded(): Boolean {
return none { it.value is CryptoCurrencyStatus.Loading }
}
}

View file

@ -22,6 +22,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.unit.Dp
@ -39,6 +40,7 @@ import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBotto
import com.tangem.core.ui.components.keyboardAsState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.TestTags
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder
@ -133,7 +135,7 @@ private fun WalletContent(
.padding(horizontal = horizontalPadding)
LazyColumn(
modifier = Modifier.fillMaxSize(),
modifier = Modifier.fillMaxSize().testTag(TestTags.WALLET_SCREEN),
contentPadding = PaddingValues(
top = TangemTheme.dimens.spacing8,
bottom = TangemTheme.dimens.spacing92,

View file

@ -7,11 +7,9 @@ import androidx.lifecycle.viewModelScope
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.navigation.AppScreen
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.settings.CanUseBiometryUseCase
import com.tangem.domain.settings.IsWalletsScrollPreviewEnabled
import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase
import com.tangem.domain.walletconnect.WalletConnectActions
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.feature.wallet.presentation.deeplink.WalletDeepLinksHandler
@ -37,7 +35,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
@Suppress("LongParameterList")
@ -56,7 +53,6 @@ internal class WalletViewModel @Inject constructor(
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
analyticsEventsHandler: AnalyticsEventHandler,
private val dispatchers: CoroutineDispatcherProvider,
private val reduxStateHolder: ReduxStateHolder,
private val screenLifecycleProvider: ScreenLifecycleProvider,
private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender,
private val walletDeepLinksHandler: WalletDeepLinksHandler,
@ -141,16 +137,6 @@ internal class WalletViewModel @Inject constructor(
.distinctUntilChanged()
.onEach { selectedWallet ->
if (selectedWallet.isMultiCurrency) {
Timber.d("WalletConnect: initialize and setup networks for ${selectedWallet.walletId}")
reduxStateHolder.dispatch(
action = WalletConnectActions.New.Initialize(userWallet = selectedWallet),
)
reduxStateHolder.dispatch(
action = WalletConnectActions.New.SetupUserChains(userWallet = selectedWallet),
)
selectedWalletAnalyticsSender.send(selectedWallet)
}

View file

@ -7,8 +7,7 @@ import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBot
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels
import com.tangem.core.ui.extensions.WrappedList
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.*
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.extenstions.unwrap
import com.tangem.domain.common.util.cardTypesResolver
@ -19,6 +18,7 @@ import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.NetworkAddress
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.models.analytics.TokenReceiveAnalyticsEvent
import com.tangem.domain.tokens.models.analytics.TokenScreenAnalyticsEvent
import com.tangem.domain.walletmanager.WalletManagersFacade
@ -39,11 +39,18 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
import java.lang.IllegalArgumentException
import javax.inject.Inject
interface WalletCurrencyActionsClickIntents {
fun onSendClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onSendClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
@ -53,12 +60,6 @@ interface WalletCurrencyActionsClickIntents {
fun onPerformHideToken(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onExploreClick()
}
@ -82,13 +83,18 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private val reduxStateHolder: ReduxStateHolder,
) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents {
override fun onSendClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
override fun onSendClick(
cryptoCurrencyStatus: CryptoCurrencyStatus,
unavailabilityReason: ScenarioUnavailabilityReason,
) {
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
analyticsEventHandler.send(
event = TokenScreenAnalyticsEvent.ButtonSend(cryptoCurrencyStatus.currency.symbol),
)
if (handleUnavailabilityReason(unavailabilityReason)) return
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWallet.walletId))
viewModelScope.launch(dispatchers.main) {
val maybeFeeCurrencyStatus =
@ -121,7 +127,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private fun sendToken(
cryptoCurrency: CryptoCurrency.Token,
cryptoCurrencyStatus: CryptoCurrencyStatus.Status,
cryptoCurrencyStatus: CryptoCurrencyStatus.Value,
feeCurrencyStatus: CryptoCurrencyStatus?,
userWallet: UserWallet,
) {
@ -274,11 +280,16 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
}
}
override fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
override fun onSellClick(
cryptoCurrencyStatus: CryptoCurrencyStatus,
unavailabilityReason: ScenarioUnavailabilityReason,
) {
analyticsEventHandler.send(
event = TokenScreenAnalyticsEvent.ButtonSell(cryptoCurrencyStatus.currency.symbol),
)
if (handleUnavailabilityReason(unavailabilityReason)) return
showErrorIfDemoModeOrElse {
viewModelScope.launch(dispatchers.main) {
reduxStateHolder.dispatch(
@ -291,13 +302,18 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
}
}
override fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
override fun onBuyClick(
cryptoCurrencyStatus: CryptoCurrencyStatus,
unavailabilityReason: ScenarioUnavailabilityReason,
) {
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
analyticsEventHandler.send(
event = TokenScreenAnalyticsEvent.ButtonBuy(cryptoCurrencyStatus.currency.symbol),
)
if (handleUnavailabilityReason(unavailabilityReason)) return
showErrorIfDemoModeOrElse {
viewModelScope.launch(dispatchers.main) {
reduxStateHolder.dispatch(
@ -311,11 +327,16 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
}
}
override fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
override fun onSwapClick(
cryptoCurrencyStatus: CryptoCurrencyStatus,
unavailabilityReason: ScenarioUnavailabilityReason,
) {
analyticsEventHandler.send(
event = TokenScreenAnalyticsEvent.ButtonExchange(cryptoCurrencyStatus.currency.symbol),
)
if (handleUnavailabilityReason(unavailabilityReason)) return
reduxStateHolder.dispatch(TradeCryptoAction.Swap(cryptoCurrencyStatus.currency))
}
@ -402,4 +423,79 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
action()
}
}
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,
onConfirmClick = null,
),
),
)
}
return true
}
private fun getUnavailabilityReasonText(unavailabilityReason: ScenarioUnavailabilityReason): TextReference {
return when (unavailabilityReason) {
// send
is ScenarioUnavailabilityReason.PendingTransaction -> {
resourceReference(
id = R.string.warning_send_blocked_pending_transactions_message,
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencySymbol),
)
}
ScenarioUnavailabilityReason.EmptyBalance -> {
resourceReference(
id = R.string.token_button_unavailability_reason_empty_balance,
)
}
is ScenarioUnavailabilityReason.InsufficientFundsForFee -> {
resourceReference(
id = R.string.warning_send_blocked_funds_for_fee_message,
formatArgs = wrappedList(
unavailabilityReason.currencyName,
unavailabilityReason.networkName,
unavailabilityReason.currencyName,
unavailabilityReason.feeCurrencyName,
unavailabilityReason.feeCurrencySymbol,
),
)
}
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.SellUnavailable -> {
resourceReference(
id = R.string.token_button_unavailability_reason_sell_unavailable,
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
)
}
ScenarioUnavailabilityReason.NoQuotes -> {
resourceReference(
id = R.string.token_button_unavailability_reason_no_quotes,
)
}
ScenarioUnavailabilityReason.None -> {
throw IllegalArgumentException("The unavailability reason must be other than None")
}
}
}
}