Updated on 2026-08-14
This commit is contained in:
commit
e8ccd67098
295 changed files with 4414 additions and 2971 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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 = {}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 -> {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = {}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
}
|
||||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue