Updated on 2026-08-14
This commit is contained in:
commit
6437018fc8
1160 changed files with 35466 additions and 6540 deletions
|
|
@ -7,6 +7,14 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.account.status.usecase.ApplyTokenListSortingUseCaseV2
|
||||
import com.tangem.domain.account.status.usecase.ToggleTokenListGroupingUseCaseV2
|
||||
import com.tangem.domain.account.status.usecase.ToggleTokenListSortingUseCaseV2
|
||||
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
|
|
@ -27,6 +35,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeToken
|
|||
import com.tangem.feature.wallet.presentation.organizetokens.utils.CryptoCurrenciesIdsResolver
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.disableSortingByBalance
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.dnd.DragAndDropAdapter
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.dnd.DragAndDropAdapterV2
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
|
@ -46,6 +55,12 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val toggleTokenListGroupingUseCaseV2: ToggleTokenListGroupingUseCaseV2,
|
||||
private val toggleTokenListSortingUseCaseV2: ToggleTokenListSortingUseCaseV2,
|
||||
private val applyTokenListSortingUseCaseV2: ApplyTokenListSortingUseCaseV2,
|
||||
) : Model(), OrganizeTokensIntents {
|
||||
|
||||
private val selectedAppCurrencyFlow = createSelectedAppCurrencyFlow()
|
||||
|
|
@ -56,15 +71,24 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
listStateProvider = Provider { uiState.value.itemsState },
|
||||
)
|
||||
|
||||
private val dragAndDropAdapterV2 = DragAndDropAdapterV2(
|
||||
tokenListUMProvider = Provider { uiState.value.tokenListUM },
|
||||
)
|
||||
|
||||
private val stateHolder = OrganizeTokensStateHolder(
|
||||
intents = this,
|
||||
dragAndDropIntents = dragAndDropAdapter,
|
||||
dragAndDropAdapterV2 = dragAndDropAdapterV2,
|
||||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||
accountsFeatureToggles = accountsFeatureToggles,
|
||||
)
|
||||
|
||||
private val userWalletId = paramsContainer.require<OrganizeTokensComponent.Params>().userWalletId
|
||||
|
||||
private var cachedTokenList: TokenList? = null
|
||||
private var cachedAccountStatusList: AccountStatusList? = null
|
||||
|
||||
private var isAccountsModeEnabled: Boolean = false
|
||||
|
||||
val uiState: StateFlow<OrganizeTokensState> = stateHolder.stateFlow
|
||||
|
||||
|
|
@ -89,59 +113,109 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onSortClick() {
|
||||
val list = cachedTokenList ?: return
|
||||
if (list.sortedBy == TokensSortType.BALANCE) return
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
val list = cachedAccountStatusList ?: return
|
||||
if (list.sortType == TokensSortType.BALANCE) return
|
||||
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance)
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance)
|
||||
|
||||
modelScope.launch {
|
||||
toggleTokenListSortingUseCase(list).fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateAfterTokenListSorting(it)
|
||||
cachedTokenList = it
|
||||
},
|
||||
)
|
||||
modelScope.launch {
|
||||
toggleTokenListSortingUseCaseV2(list).fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateAfterTokenListSortingV2(it, isAccountsModeEnabled)
|
||||
cachedAccountStatusList = it
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val list = cachedTokenList ?: return
|
||||
if (list.sortedBy == TokensSortType.BALANCE) return
|
||||
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance)
|
||||
|
||||
modelScope.launch {
|
||||
toggleTokenListSortingUseCase(list).fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateAfterTokenListSorting(it)
|
||||
cachedTokenList = it
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGroupClick() {
|
||||
val list = cachedTokenList ?: return
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
val list = cachedAccountStatusList ?: return
|
||||
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group)
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group)
|
||||
|
||||
modelScope.launch {
|
||||
toggleTokenListGroupingUseCase(list).fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateAfterTokenListSorting(it)
|
||||
cachedTokenList = it
|
||||
},
|
||||
)
|
||||
modelScope.launch {
|
||||
toggleTokenListGroupingUseCaseV2(list).fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateAfterTokenListSortingV2(it, isAccountsModeEnabled)
|
||||
cachedAccountStatusList = it
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val list = cachedTokenList ?: return
|
||||
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group)
|
||||
|
||||
modelScope.launch {
|
||||
toggleTokenListGroupingUseCase(list).fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateAfterTokenListSorting(it)
|
||||
cachedTokenList = it
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onApplyClick() {
|
||||
modelScope.launch {
|
||||
stateHolder.updateStateToDisplayProgress()
|
||||
|
||||
val listState = uiState.value.itemsState
|
||||
val resolver = CryptoCurrenciesIdsResolver()
|
||||
|
||||
val isGroupedByNetwork = listState is OrganizeTokensListState.GroupedByNetwork
|
||||
val isSortedByBalance = uiState.value.header.isSortedByBalance
|
||||
|
||||
sendAnalyticsEvent(
|
||||
isGroupedByNetwork = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
val result = if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
val tokensListUM = uiState.value.tokenListUM
|
||||
|
||||
val result = applyTokenListSortingUseCase(
|
||||
userWalletId = userWalletId,
|
||||
sortedTokensIds = resolver.resolve(listState, cachedTokenList),
|
||||
isGroupedByNetwork = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
val isGroupedByNetwork = tokensListUM.isGrouped
|
||||
|
||||
sendAnalyticsEvent(
|
||||
isGroupedByNetwork = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
|
||||
applyTokenListSortingUseCaseV2(
|
||||
sortedTokensIdsByAccount = resolver.resolveV2(tokensListUM, cachedAccountStatusList),
|
||||
isGroupedByNetwork = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
} else {
|
||||
val listState = uiState.value.itemsState
|
||||
|
||||
val isGroupedByNetwork = listState is OrganizeTokensListState.GroupedByNetwork
|
||||
|
||||
sendAnalyticsEvent(
|
||||
isGroupedByNetwork = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
|
||||
applyTokenListSortingUseCase(
|
||||
userWalletId = userWalletId,
|
||||
sortedTokensIds = resolver.resolve(listState, cachedTokenList),
|
||||
isGroupedByNetwork = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
}
|
||||
|
||||
result.fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
|
|
@ -161,10 +235,24 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
|
||||
private fun bootstrapTokenList() {
|
||||
modelScope.launch {
|
||||
val tokenList = getTokenList() ?: return@launch
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
val accountList = singleAccountStatusListSupplier.getSyncOrNull(
|
||||
SingleAccountStatusListProducer.Params(userWalletId),
|
||||
) ?: return@launch
|
||||
|
||||
stateHolder.updateStateWithTokenList(tokenList)
|
||||
cachedTokenList = tokenList
|
||||
isAccountsModeEnabled = isAccountsModeEnabledUseCase.invokeSync()
|
||||
|
||||
stateHolder.updateStateWithAccountList(
|
||||
accountStatusList = accountList,
|
||||
isAccountsModeEnabled = isAccountsModeEnabled,
|
||||
)
|
||||
|
||||
cachedAccountStatusList = accountList
|
||||
} else {
|
||||
val tokenList = getTokenList() ?: return@launch
|
||||
stateHolder.updateStateWithTokenList(tokenList)
|
||||
cachedTokenList = tokenList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -180,14 +268,25 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun bootstrapDragAndDropUpdates() {
|
||||
dragAndDropAdapter.dragAndDropUpdates
|
||||
.distinctUntilChanged()
|
||||
.onEach { (type, updatedListState) ->
|
||||
disableSortingByBalanceIfListChanged(type)
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
dragAndDropAdapterV2.dragAndDropUpdates
|
||||
.distinctUntilChanged()
|
||||
.onEach { (type, updatedListState) ->
|
||||
disableSortingByBalanceIfListChangedV2(type)
|
||||
|
||||
stateHolder.updateStateWithManualSorting(updatedListState)
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
stateHolder.updateStateWithManualSortingV2(updatedListState)
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
} else {
|
||||
dragAndDropAdapter.dragAndDropUpdates
|
||||
.distinctUntilChanged()
|
||||
.onEach { (type, updatedListState) ->
|
||||
disableSortingByBalanceIfListChanged(type)
|
||||
|
||||
stateHolder.updateStateWithManualSorting(updatedListState)
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
}
|
||||
|
||||
private fun disableSortingByBalanceIfListChanged(dragOperationType: DragAndDropAdapter.DragOperation.Type) {
|
||||
|
|
@ -199,6 +298,15 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun disableSortingByBalanceIfListChangedV2(dragOperationType: DragAndDropAdapterV2.DragOperation.Type) {
|
||||
if (dragOperationType !is DragAndDropAdapterV2.DragOperation.Type.End) return
|
||||
|
||||
if (uiState.value.header.isSortedByBalance && dragOperationType.isItemsOrderChanged) {
|
||||
cachedAccountStatusList = cachedAccountStatusList?.copy(sortType = TokensSortType.NONE)
|
||||
stateHolder.disableSortingByBalance()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSelectedAppCurrencyFlow(): StateFlow<AppCurrency> {
|
||||
return getSelectedAppCurrencyUseCase()
|
||||
.map { maybeAppCurrency ->
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -18,6 +19,7 @@ import com.tangem.domain.models.wallet.isMultiCurrency
|
|||
import com.tangem.domain.nft.ObserveAndClearNFTCacheIfNeedUseCase
|
||||
import com.tangem.domain.notifications.GetIsHuaweiDeviceWithoutGoogleServicesUseCase
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||
import com.tangem.domain.pay.usecase.TangemPayIssueOrderUseCase
|
||||
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
||||
import com.tangem.domain.settings.*
|
||||
|
|
@ -93,7 +95,9 @@ internal class WalletModel @Inject constructor(
|
|||
private val tangemPayIssueOrderUseCase: TangemPayIssueOrderUseCase,
|
||||
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
|
||||
private val yieldSupplyApyUpdateUseCase: YieldSupplyApyUpdateUseCase,
|
||||
private val tangemPayOnboardingRepository: OnboardingRepository,
|
||||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
val innerWalletRouter: InnerWalletRouter,
|
||||
) : Model() {
|
||||
|
|
@ -113,7 +117,13 @@ internal class WalletModel @Inject constructor(
|
|||
init {
|
||||
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.ScreenOpened)
|
||||
|
||||
suggestToEnableBiometrics()
|
||||
screenLifecycleProvider.isBackgroundState
|
||||
.onEach { isBackground ->
|
||||
if (isBackground.not()) {
|
||||
suggestToEnableBiometrics()
|
||||
}
|
||||
}.launchIn(modelScope)
|
||||
|
||||
suggestToOpenMarkets()
|
||||
|
||||
maybeMigrateNames()
|
||||
|
|
@ -158,15 +168,13 @@ internal class WalletModel @Inject constructor(
|
|||
walletScreenContentLoader.cancelAll()
|
||||
}
|
||||
|
||||
private fun suggestToEnableBiometrics() {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
withContext(dispatchers.io) { delay(timeMillis = 1_800) }
|
||||
private suspend fun suggestToEnableBiometrics() {
|
||||
if (shouldShowAskBiometryBottomSheet()) {
|
||||
delay(timeMillis = 1_800)
|
||||
|
||||
if (shouldShowAskBiometryBottomSheet()) {
|
||||
innerWalletRouter.dialogNavigation.activate(
|
||||
configuration = WalletDialogConfig.AskForBiometry,
|
||||
)
|
||||
}
|
||||
innerWalletRouter.dialogNavigation.activate(
|
||||
configuration = WalletDialogConfig.AskForBiometry,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -344,16 +352,24 @@ internal class WalletModel @Inject constructor(
|
|||
* and every minute while user stays on the main screen
|
||||
*/
|
||||
screenLifecycleProvider.isBackgroundState.onEach { inBackground ->
|
||||
// fast exit
|
||||
if (!tangemPayFeatureToggles.isTangemPayEnabled) return@onEach
|
||||
|
||||
updateTangemPayJobHolder.cancel()
|
||||
if (!inBackground && tangemPayFeatureToggles.isTangemPayEnabled) {
|
||||
modelScope.launch {
|
||||
|
||||
modelScope.launch {
|
||||
// fast exit
|
||||
val initialDataProduced = tangemPayOnboardingRepository.isTangemPayInitialDataProduced()
|
||||
if (!initialDataProduced) return@launch
|
||||
|
||||
if (!inBackground) {
|
||||
refreshTangemPayInfo()
|
||||
while (isActive) {
|
||||
delay(TANGEM_PAY_UPDATE_INTERVAL)
|
||||
refreshTangemPayInfo()
|
||||
}
|
||||
}.saveIn(updateTangemPayJobHolder)
|
||||
}
|
||||
}
|
||||
}.saveIn(updateTangemPayJobHolder)
|
||||
}.launchIn(modelScope)
|
||||
}
|
||||
|
||||
|
|
@ -365,7 +381,12 @@ internal class WalletModel @Inject constructor(
|
|||
value = info,
|
||||
onClickIssue = ::issueOrder,
|
||||
onClickKyc = innerWalletRouter::openTangemPayOnboarding,
|
||||
openDetails = innerWalletRouter::openTangemPayDetails,
|
||||
openDetails = { config ->
|
||||
innerWalletRouter.openTangemPayDetails(
|
||||
userWalletId = stateHolder.getSelectedWalletId(),
|
||||
config = config,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -516,21 +537,39 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun addWallet(action: WalletsUpdateActionResolver.Action.AddWallet) {
|
||||
walletScreenContentLoader.load(
|
||||
userWallet = action.selectedWallet,
|
||||
clickIntents = clickIntents,
|
||||
coroutineScope = modelScope,
|
||||
)
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
fetchWalletContent(userWallet = action.selectedWallet)
|
||||
|
||||
fetchWalletContent(userWallet = action.selectedWallet)
|
||||
stateHolder.update(
|
||||
AddWalletTransformer(
|
||||
userWallet = action.selectedWallet,
|
||||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
),
|
||||
)
|
||||
|
||||
stateHolder.update(
|
||||
AddWalletTransformer(
|
||||
walletScreenContentLoader.load(
|
||||
userWallet = action.selectedWallet,
|
||||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
),
|
||||
)
|
||||
coroutineScope = modelScope,
|
||||
)
|
||||
} else {
|
||||
walletScreenContentLoader.load(
|
||||
userWallet = action.selectedWallet,
|
||||
clickIntents = clickIntents,
|
||||
coroutineScope = modelScope,
|
||||
)
|
||||
|
||||
fetchWalletContent(userWallet = action.selectedWallet)
|
||||
|
||||
stateHolder.update(
|
||||
AddWalletTransformer(
|
||||
userWallet = action.selectedWallet,
|
||||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
scrollToWallet(prevIndex = action.prevWalletIndex, newIndex = action.selectedWalletIndex) {
|
||||
stateHolder.update {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import com.tangem.core.ui.haptic.TangemHapticEffect
|
|||
import com.tangem.core.ui.haptic.VibratorHapticManager
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
import com.tangem.domain.account.status.usecase.SaveCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.extenstions.unwrap
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
|
|
@ -157,7 +157,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
private val removeCurrencyUseCase: RemoveCurrencyUseCase,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
|
||||
private val saveCryptoCurrenciesUseCase: SaveCryptoCurrenciesUseCase,
|
||||
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
|
||||
) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents {
|
||||
|
||||
override fun onSendClick(
|
||||
|
|
@ -360,7 +360,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
return@launch
|
||||
}
|
||||
|
||||
saveCryptoCurrenciesUseCase(accountId = accountId, remove = cryptoCurrencyStatus.currency)
|
||||
manageCryptoCurrenciesUseCase(accountId = accountId, remove = cryptoCurrencyStatus.currency)
|
||||
} else {
|
||||
removeCurrencyUseCase(userWalletId, cryptoCurrencyStatus.currency)
|
||||
}
|
||||
|
|
@ -507,7 +507,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
appRouter.push(
|
||||
AppRoute.Staking(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = cryptoCurrency.id,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
yieldId = yield?.id ?: return@launch,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,10 +11,15 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.components.bottomsheets.message.*
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.DialogMessage.Companion.invoke
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.domain.card.SetCardWasScannedUseCase
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.error.UnlockWalletError
|
||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
|
|
@ -105,7 +110,7 @@ internal interface WalletWarningsClickIntents {
|
|||
|
||||
fun onDenyPermissions()
|
||||
|
||||
fun onFinishWalletActivationClick(type: WalletActivationBannerType)
|
||||
fun onFinishWalletActivationClick(bannerType: WalletActivationBannerType, isBackupExists: Boolean)
|
||||
}
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
|
||||
|
|
@ -139,34 +144,9 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
private val messageSender: UiMessageSender,
|
||||
private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase,
|
||||
private val getWalletsListForEnablingUseCase: GetWalletsForAutomaticallyPushEnablingUseCase,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
|
||||
|
||||
private val finalizeWalletSetupAlertBS
|
||||
get() = bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.img_knight_shield_32) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Warning
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
||||
}
|
||||
title = resourceReference(R.string.hw_activation_need_title)
|
||||
body = resourceReference(R.string.hw_activation_need_description)
|
||||
}
|
||||
secondaryButton {
|
||||
text = resourceReference(R.string.common_later)
|
||||
onClick {
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.hw_activation_need_backup)
|
||||
onClick {
|
||||
val userWallet = getSelectedUserWallet() ?: return@onClick
|
||||
appRouter.push(WalletActivation(userWallet.walletId))
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAddBackupCardClick() {
|
||||
analyticsEventHandler.send(MainScreen.NoticeBackupYourWalletTapped)
|
||||
|
||||
|
|
@ -221,11 +201,40 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
userWalletsListRepository.unlockAllWallets()
|
||||
.onLeft {
|
||||
val selectedUserWallet = getSelectedUserWallet() ?: return@onLeft
|
||||
val selectedUserWalletId = selectedUserWallet.walletId
|
||||
val method = when (selectedUserWallet) {
|
||||
is UserWallet.Cold -> UserWalletsListRepository.UnlockMethod.Scan()
|
||||
is UserWallet.Hot -> UserWalletsListRepository.UnlockMethod.AccessCode
|
||||
}
|
||||
userWalletsListRepository.unlock(stateHolder.getSelectedWalletId(), method)
|
||||
userWalletsListRepository
|
||||
.unlock(stateHolder.getSelectedWalletId(), method)
|
||||
.onLeft {
|
||||
when (it) {
|
||||
UnlockWalletError.AlreadyUnlocked -> Unit
|
||||
UnlockWalletError.ScannedCardWalletNotMatched -> {
|
||||
uiMessageSender.send(
|
||||
message = DialogMessage(
|
||||
title = resourceReference(R.string.common_warning),
|
||||
message = resourceReference(R.string.error_wrong_wallet_tapped),
|
||||
),
|
||||
)
|
||||
}
|
||||
UnlockWalletError.UnableToUnlock -> {
|
||||
Timber.e("Unable to unlock wallet with id: $selectedUserWalletId")
|
||||
uiMessageSender.send(
|
||||
SnackbarMessage(TextReference.Res(R.string.generic_error)),
|
||||
)
|
||||
}
|
||||
UnlockWalletError.UserCancelled -> Unit
|
||||
UnlockWalletError.UserWalletNotFound -> {
|
||||
// This should never happen in this flow
|
||||
Timber.e("User wallet not found for unlock: $selectedUserWalletId")
|
||||
uiMessageSender.send(
|
||||
SnackbarMessage(TextReference.Res(R.string.generic_error)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
|
|
@ -466,14 +475,38 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onFinishWalletActivationClick(type: WalletActivationBannerType) {
|
||||
when (type) {
|
||||
override fun onFinishWalletActivationClick(bannerType: WalletActivationBannerType, isBackupExists: Boolean) {
|
||||
when (bannerType) {
|
||||
WalletActivationBannerType.Attention -> {
|
||||
val userWallet = getSelectedUserWallet() ?: return
|
||||
appRouter.push(WalletActivation(userWallet.walletId))
|
||||
appRouter.push(WalletActivation(userWallet.walletId, isBackupExists))
|
||||
}
|
||||
WalletActivationBannerType.Warning -> {
|
||||
messageSender.send(finalizeWalletSetupAlertBS)
|
||||
val message = bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.img_knight_shield_32) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Warning
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
||||
}
|
||||
title = resourceReference(R.string.hw_activation_need_title)
|
||||
body = resourceReference(R.string.hw_activation_need_description)
|
||||
}
|
||||
secondaryButton {
|
||||
text = resourceReference(R.string.common_later)
|
||||
onClick {
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.hw_activation_need_backup)
|
||||
onClick {
|
||||
val userWallet = getSelectedUserWallet() ?: return@onClick
|
||||
appRouter.push(WalletActivation(userWallet.walletId, isBackupExists))
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
messageSender.send(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.account
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusSupplier
|
||||
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -12,4 +13,5 @@ internal class AccountDependencies @Inject constructor(
|
|||
val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
val expandedAccountsHolder: ExpandedAccountsHolder,
|
||||
val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
val singleAccountStatusSupplier: SingleAccountStatusSupplier,
|
||||
)
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.producer.SingleAccountListProducer
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -13,25 +14,30 @@ import javax.inject.Inject
|
|||
@ModelScoped
|
||||
internal class ExpandedAccountsHolder @Inject constructor(
|
||||
private val singleAccountListSupplier: SingleAccountListSupplier,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
) {
|
||||
|
||||
private val expandedAccounts = MutableStateFlow<Map<UserWalletId, Set<AccountId>>>(mapOf())
|
||||
|
||||
fun expandedAccounts(userWallet: UserWallet): Flow<Set<AccountId>> = channelFlow {
|
||||
walletAccounts(userWallet)
|
||||
.onEach { accountList ->
|
||||
val isSingleAccount = accountList.totalAccounts == 1
|
||||
combine(
|
||||
flow = walletAccounts(userWallet),
|
||||
flow2 = isAccountsModeEnabledUseCase.invoke(),
|
||||
transform = { accountList, isAccountsMode ->
|
||||
val isSingleAccount = accountList.accounts.size == 1
|
||||
val defaultExpanded = when {
|
||||
!isAccountsMode -> setOf()
|
||||
isSingleAccount -> setOf(accountList.mainAccount.accountId)
|
||||
else -> setOf()
|
||||
}
|
||||
expandedAccounts.update { map ->
|
||||
var expandedSet = map[userWallet.walletId] ?: defaultExpanded
|
||||
// force expand for single account
|
||||
if (isSingleAccount) expandedSet = defaultExpanded
|
||||
if (isSingleAccount || !isAccountsMode) expandedSet = defaultExpanded
|
||||
map.plus(userWallet.walletId to expandedSet)
|
||||
}
|
||||
}.launchIn(this)
|
||||
},
|
||||
).launchIn(this)
|
||||
|
||||
expandedAccounts
|
||||
.mapNotNull { map -> map[userWallet.walletId] }
|
||||
|
|
|
|||
|
|
@ -8,15 +8,13 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBottomSheetConfig
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListUM
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
|
@ -88,7 +86,7 @@ internal object WalletPreviewData {
|
|||
|
||||
private const val networksSize = 10
|
||||
private const val tokensSize = 3
|
||||
private val draggableItems by lazy {
|
||||
private val draggableItems: PersistentList<DraggableItem> by lazy {
|
||||
List(networksSize) { it }
|
||||
.flatMap { index ->
|
||||
val lastNetworkIndex = networksSize - 1
|
||||
|
|
@ -98,11 +96,13 @@ internal object WalletPreviewData {
|
|||
val group = DraggableItem.GroupHeader(
|
||||
id = networkNumber,
|
||||
networkName = "$networkNumber",
|
||||
|
||||
roundingMode = when (index) {
|
||||
0 -> DraggableItem.RoundingMode.Top()
|
||||
lastNetworkIndex -> DraggableItem.RoundingMode.Bottom()
|
||||
else -> DraggableItem.RoundingMode.None
|
||||
},
|
||||
accountId = "account_$networkNumber",
|
||||
)
|
||||
|
||||
val tokens: MutableList<DraggableItem.Token> = mutableListOf()
|
||||
|
|
@ -117,6 +117,7 @@ internal object WalletPreviewData {
|
|||
),
|
||||
),
|
||||
groupId = group.id,
|
||||
accountId = "account_$networkNumber",
|
||||
roundingMode = when {
|
||||
i == lastTokenIndex && index == lastNetworkIndex -> DraggableItem.RoundingMode.Bottom()
|
||||
else -> DraggableItem.RoundingMode.None
|
||||
|
|
@ -125,7 +126,10 @@ internal object WalletPreviewData {
|
|||
)
|
||||
}
|
||||
|
||||
val divider = DraggableItem.Placeholder(id = "divider_$networkNumber")
|
||||
val divider = DraggableItem.Placeholder(
|
||||
id = "divider_$networkNumber",
|
||||
accountId = "account_$networkNumber",
|
||||
)
|
||||
|
||||
buildList {
|
||||
add(group)
|
||||
|
|
@ -154,6 +158,7 @@ internal object WalletPreviewData {
|
|||
itemsState = OrganizeTokensListState.GroupedByNetwork(
|
||||
items = draggableItems,
|
||||
),
|
||||
tokenListUM = OrganizeTokensListUM.EmptyList,
|
||||
header = OrganizeTokensState.HeaderConfig(
|
||||
onSortClick = {},
|
||||
onGroupClick = {},
|
||||
|
|
|
|||
|
|
@ -106,6 +106,28 @@ internal object WalletScreenPreviewData {
|
|||
),
|
||||
)
|
||||
|
||||
private val emptyPortfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
|
||||
items = persistentListOf(
|
||||
TokensListItemUM.Portfolio(
|
||||
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
|
||||
isExpanded = false,
|
||||
isCollapsable = true,
|
||||
tokenItemUM = AccountItemPreviewData.accountItem
|
||||
.copy(iconState = AccountItemPreviewData.accountLetterIcon),
|
||||
),
|
||||
TokensListItemUM.Portfolio(
|
||||
tokens = persistentListOf(),
|
||||
isExpanded = true,
|
||||
isCollapsable = true,
|
||||
tokenItemUM = AccountItemPreviewData.accountItem,
|
||||
),
|
||||
),
|
||||
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
private val noteLockedCard by lazy {
|
||||
WalletCardState.LockedContent(
|
||||
id = UserWalletId(stringValue = "1"),
|
||||
|
|
@ -208,4 +230,12 @@ internal object WalletScreenPreviewData {
|
|||
multiWalletState.copy(tokensListState = portfolioContentState),
|
||||
),
|
||||
)
|
||||
|
||||
internal val accountScreenWithEmptyTokensState =
|
||||
walletScreenState.copy(
|
||||
wallets = persistentListOf(
|
||||
singleWalletLockedState,
|
||||
multiWalletState.copy(tokensListState = emptyPortfolioContentState),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ import com.tangem.core.ui.components.SecondaryButton
|
|||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
import com.tangem.core.ui.components.buttons.actions.RoundedActionButton
|
||||
import com.tangem.core.ui.components.token.TokenItem
|
||||
import com.tangem.core.ui.components.tokenlist.ExpandedPortfolioHeader
|
||||
import com.tangem.core.ui.components.tokenlist.internal.DraggableGroupTitleItem
|
||||
import com.tangem.core.ui.event.EventEffect
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -47,6 +48,7 @@ import com.tangem.feature.wallet.impl.R
|
|||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListUM
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.rememberReorderableLazyListState
|
||||
|
|
@ -73,6 +75,7 @@ internal fun OrganizeTokensScreen(state: OrganizeTokensState, modifier: Modifier
|
|||
.padding(paddingValues)
|
||||
.fillMaxSize(),
|
||||
listState = tokensListState,
|
||||
tokensListUM = state.tokenListUM,
|
||||
state = state.itemsState,
|
||||
dndConfig = state.dndConfig,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
|
|
@ -96,11 +99,17 @@ internal fun OrganizeTokensScreen(state: OrganizeTokensState, modifier: Modifier
|
|||
private fun TokenList(
|
||||
listState: LazyListState,
|
||||
state: OrganizeTokensListState,
|
||||
tokensListUM: OrganizeTokensListUM,
|
||||
dndConfig: OrganizeTokensState.DragAndDropConfig,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val hapticFeedback = LocalHapticFeedback.current
|
||||
val tokenList = if (tokensListUM !is OrganizeTokensListUM.EmptyList) {
|
||||
tokensListUM.items
|
||||
} else {
|
||||
state.items
|
||||
}
|
||||
Box(modifier = modifier) {
|
||||
val onDragEnd: (Int, Int) -> Unit = remember {
|
||||
{ _, _ ->
|
||||
|
|
@ -127,7 +136,7 @@ private fun TokenList(
|
|||
// because sometimes items disappear after reordering in 1.7.4+ compose-runtime version
|
||||
// check removing after update to compose-runtime 1.8.0+
|
||||
val forceRecompose = remember { mutableIntStateOf(0) }
|
||||
LaunchedEffect(state.items) {
|
||||
LaunchedEffect(tokenList) {
|
||||
forceRecompose.intValue++
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +149,7 @@ private fun TokenList(
|
|||
contentPadding = listContentPadding,
|
||||
) {
|
||||
itemsIndexed(
|
||||
items = state.items,
|
||||
items = tokenList,
|
||||
key = { _, item -> item.id },
|
||||
) { index, item ->
|
||||
|
||||
|
|
@ -206,6 +215,13 @@ private fun LazyItemScope.DraggableItem(
|
|||
)
|
||||
// Should be presented in the list but remain invisible
|
||||
is DraggableItem.Placeholder -> Box(modifier = Modifier.fillMaxWidth())
|
||||
is DraggableItem.Portfolio -> ExpandedPortfolioHeader(
|
||||
state = item.tokenItemState,
|
||||
isCollapsable = false,
|
||||
modifier = modifierWithBackground
|
||||
.padding(top = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,19 +2,24 @@ package com.tangem.feature.wallet.presentation.organizetokens
|
|||
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.event.triggeredEvent
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.error.TokenListSortingError
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListUM
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.InProgressStateConverter
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.TokenListToStateConverter
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.TokenListToStateConverterV2
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.error.TokenListErrorConverter
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.error.TokenListSortingErrorConverter
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items.CryptoCurrencyToDraggableItemConverter
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items.NetworkGroupToDraggableItemsConverter
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items.TokenListToListStateConverter
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.dnd.DragAndDropAdapterV2
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -23,7 +28,9 @@ import kotlinx.coroutines.flow.update
|
|||
internal class OrganizeTokensStateHolder(
|
||||
private val intents: OrganizeTokensIntents,
|
||||
private val dragAndDropIntents: DragAndDropIntents,
|
||||
private val dragAndDropAdapterV2: DragAndDropAdapterV2,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
) {
|
||||
|
||||
private val stateFlowInternal: MutableStateFlow<OrganizeTokensState> = MutableStateFlow(getInitialState())
|
||||
|
|
@ -54,6 +61,16 @@ internal class OrganizeTokensStateHolder(
|
|||
updateState { tokenListConverter.convert(tokenList) }
|
||||
}
|
||||
|
||||
fun updateStateWithAccountList(accountStatusList: AccountStatusList, isAccountsModeEnabled: Boolean) {
|
||||
updateState {
|
||||
TokenListToStateConverterV2(
|
||||
accountStatusList = accountStatusList,
|
||||
isAccountsMode = isAccountsModeEnabled,
|
||||
appCurrency = appCurrencyProvider(),
|
||||
).transform(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateStateAfterTokenListSorting(tokenList: TokenList) {
|
||||
updateState {
|
||||
tokenListConverter.convert(tokenList).copy(
|
||||
|
|
@ -62,6 +79,18 @@ internal class OrganizeTokensStateHolder(
|
|||
}
|
||||
}
|
||||
|
||||
fun updateStateAfterTokenListSortingV2(accountStatusList: AccountStatusList, isAccountsModeEnabled: Boolean) {
|
||||
updateState {
|
||||
TokenListToStateConverterV2(
|
||||
accountStatusList = accountStatusList,
|
||||
isAccountsMode = isAccountsModeEnabled,
|
||||
appCurrency = appCurrencyProvider(),
|
||||
).transform(this).copy(
|
||||
scrollListToTop = triggeredEvent(Unit, ::consumeScrollListToTopEvent),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateStateToDisplayProgress() {
|
||||
updateState { inProgressStateConverter.convert(value = this) }
|
||||
}
|
||||
|
|
@ -70,6 +99,10 @@ internal class OrganizeTokensStateHolder(
|
|||
updateState { inProgressStateConverter.convertBack(value = this) }
|
||||
}
|
||||
|
||||
fun updateStateWithManualSortingV2(tokenListUM: OrganizeTokensListUM) {
|
||||
updateState { copy(tokenListUM = tokenListUM) }
|
||||
}
|
||||
|
||||
fun updateStateWithManualSorting(itemsState: OrganizeTokensListState) {
|
||||
updateState { copy(itemsState = itemsState) }
|
||||
}
|
||||
|
|
@ -94,6 +127,7 @@ internal class OrganizeTokensStateHolder(
|
|||
return OrganizeTokensState(
|
||||
onBackClick = intents::onBackClick,
|
||||
itemsState = OrganizeTokensListState.Empty,
|
||||
tokenListUM = OrganizeTokensListUM.EmptyList,
|
||||
header = OrganizeTokensState.HeaderConfig(
|
||||
onSortClick = intents::onSortClick,
|
||||
onGroupClick = intents::onGroupClick,
|
||||
|
|
@ -102,12 +136,21 @@ internal class OrganizeTokensStateHolder(
|
|||
onApplyClick = intents::onApplyClick,
|
||||
onCancelClick = intents::onCancelClick,
|
||||
),
|
||||
dndConfig = OrganizeTokensState.DragAndDropConfig(
|
||||
onItemDragged = dragAndDropIntents::onItemDragged,
|
||||
onItemDragStart = dragAndDropIntents::onItemDraggingStart,
|
||||
onItemDragEnd = dragAndDropIntents::onItemDraggingEnd,
|
||||
canDragItemOver = dragAndDropIntents::canDragItemOver,
|
||||
),
|
||||
dndConfig = if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
OrganizeTokensState.DragAndDropConfig(
|
||||
onItemDragged = dragAndDropAdapterV2::onItemDragged,
|
||||
onItemDragStart = dragAndDropAdapterV2::onItemDraggingStart,
|
||||
onItemDragEnd = dragAndDropAdapterV2::onItemDraggingEnd,
|
||||
canDragItemOver = dragAndDropAdapterV2::canDragItemOver,
|
||||
)
|
||||
} else {
|
||||
OrganizeTokensState.DragAndDropConfig(
|
||||
onItemDragged = dragAndDropIntents::onItemDragged,
|
||||
onItemDragStart = dragAndDropIntents::onItemDraggingStart,
|
||||
onItemDragEnd = dragAndDropIntents::onItemDraggingEnd,
|
||||
canDragItemOver = dragAndDropIntents::canDragItemOver,
|
||||
)
|
||||
},
|
||||
scrollListToTop = consumedEvent(),
|
||||
isBalanceHidden = true,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem.RoundingMode
|
||||
|
||||
/**
|
||||
* Helper class for the DND list items
|
||||
|
|
@ -26,12 +25,14 @@ internal sealed class DraggableItem {
|
|||
*
|
||||
* @property id ID of the network group
|
||||
* @property networkName network group name
|
||||
* @property accountId account id
|
||||
* @property roundingMode item [RoundingMode]
|
||||
* @property showShadow if true then item should be elevated
|
||||
* */
|
||||
data class GroupHeader(
|
||||
override val id: Int,
|
||||
val networkName: String,
|
||||
val accountId: String = "",
|
||||
override val roundingMode: RoundingMode = RoundingMode.None,
|
||||
override val showShadow: Boolean = false,
|
||||
) : DraggableItem() {
|
||||
|
|
@ -50,6 +51,7 @@ internal sealed class DraggableItem {
|
|||
*
|
||||
* @property tokenItemState state of the token item
|
||||
* @property groupId ID of the network group which contains this token
|
||||
* @property accountId account id
|
||||
* @property id ID of the token
|
||||
* @property roundingMode item [RoundingMode]
|
||||
* @property showShadow if true then item should be elevated
|
||||
|
|
@ -57,6 +59,7 @@ internal sealed class DraggableItem {
|
|||
data class Token(
|
||||
val tokenItemState: TokenItemState.Draggable,
|
||||
val groupId: Int,
|
||||
val accountId: String = "",
|
||||
override val showShadow: Boolean = false,
|
||||
override val roundingMode: RoundingMode = RoundingMode.None,
|
||||
) : DraggableItem() {
|
||||
|
|
@ -67,14 +70,32 @@ internal sealed class DraggableItem {
|
|||
* Helper item used to detect possible positions where a draggable item can be placed.
|
||||
*
|
||||
* @property id ID of the placeholder
|
||||
* @property accountId account id
|
||||
* */
|
||||
data class Placeholder(
|
||||
override val id: String,
|
||||
val accountId: String = "",
|
||||
) : DraggableItem() {
|
||||
override val showShadow: Boolean = false
|
||||
override val roundingMode: RoundingMode = RoundingMode.None
|
||||
}
|
||||
|
||||
/**
|
||||
* Item for portfolio.
|
||||
*
|
||||
* @property tokenItemState state of the portfolio item
|
||||
* @property id ID of the portfolio
|
||||
* @property roundingMode item [RoundingMode]
|
||||
* @property showShadow if true then item should be elevated
|
||||
* */
|
||||
data class Portfolio(
|
||||
override val roundingMode: RoundingMode = RoundingMode.None,
|
||||
val tokenItemState: TokenItemState,
|
||||
) : DraggableItem() {
|
||||
override val id: String = tokenItemState.id
|
||||
override val showShadow: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounding mode of the [DraggableItem]
|
||||
*
|
||||
|
|
@ -122,6 +143,7 @@ internal sealed class DraggableItem {
|
|||
* */
|
||||
fun updateRoundingMode(mode: RoundingMode): DraggableItem = when (this) {
|
||||
is Placeholder -> this
|
||||
is Portfolio -> this.copy(roundingMode = mode)
|
||||
is GroupHeader -> this.copy(roundingMode = mode)
|
||||
is Token -> this.copy(roundingMode = mode)
|
||||
}
|
||||
|
|
@ -134,7 +156,9 @@ internal sealed class DraggableItem {
|
|||
* @return updated [DraggableItem]
|
||||
* */
|
||||
fun updateShadowVisibility(show: Boolean): DraggableItem = when (this) {
|
||||
is Placeholder -> this
|
||||
is Portfolio,
|
||||
is Placeholder,
|
||||
-> this
|
||||
is GroupHeader -> this.copy(showShadow = show)
|
||||
is Token -> this.copy(showShadow = show)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable
|
|||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Deprecated("Use OrganizeTokensListUM instead, will be removed in future releases")
|
||||
@Immutable
|
||||
internal sealed class OrganizeTokensListState {
|
||||
abstract val items: PersistentList<DraggableItem>
|
||||
|
|
@ -19,4 +20,26 @@ internal sealed class OrganizeTokensListState {
|
|||
data object Empty : OrganizeTokensListState() {
|
||||
override val items: PersistentList<DraggableItem> = persistentListOf()
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
internal sealed interface OrganizeTokensListUM {
|
||||
|
||||
val items: PersistentList<DraggableItem>
|
||||
val isGrouped: Boolean
|
||||
|
||||
data class AccountList(
|
||||
override val items: PersistentList<DraggableItem>,
|
||||
override val isGrouped: Boolean,
|
||||
) : OrganizeTokensListUM
|
||||
|
||||
data class TokensList(
|
||||
override val items: PersistentList<DraggableItem>,
|
||||
override val isGrouped: Boolean,
|
||||
) : OrganizeTokensListUM
|
||||
|
||||
data object EmptyList : OrganizeTokensListUM {
|
||||
override val items: PersistentList<DraggableItem> = persistentListOf()
|
||||
override val isGrouped: Boolean = false
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import org.burnoutcrew.reorderable.ItemPosition
|
|||
internal data class OrganizeTokensState(
|
||||
val onBackClick: () -> Unit,
|
||||
val itemsState: OrganizeTokensListState,
|
||||
val tokenListUM: OrganizeTokensListUM,
|
||||
val header: HeaderConfig,
|
||||
val actions: ActionsConfig,
|
||||
val dndConfig: DragAndDropConfig,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.utils
|
||||
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrencies
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListUM
|
||||
|
||||
internal class CryptoCurrenciesIdsResolver {
|
||||
|
||||
|
|
@ -29,4 +33,26 @@ internal class CryptoCurrenciesIdsResolver {
|
|||
currencyStatus?.currency?.id
|
||||
}
|
||||
}
|
||||
|
||||
fun resolveV2(tokensListUM: OrganizeTokensListUM, accountStatusList: AccountStatusList?): AccountCryptoCurrencies {
|
||||
val draggableTokens = when (tokensListUM) {
|
||||
OrganizeTokensListUM.EmptyList -> return emptyMap()
|
||||
is OrganizeTokensListUM.AccountList,
|
||||
is OrganizeTokensListUM.TokensList,
|
||||
-> tokensListUM.items.filterIsInstance<DraggableItem.Token>()
|
||||
}
|
||||
|
||||
return accountStatusList?.accountStatuses
|
||||
?.filter { it.getCryptoTokenList() != TokenList.Empty }
|
||||
?.associate { accountStatus ->
|
||||
val currencies = accountStatus.flattenCurrencies()
|
||||
accountStatus.account as Account.CryptoPortfolio to draggableTokens
|
||||
.asSequence()
|
||||
.filter { it.accountId == accountStatus.account.accountId.value }
|
||||
.mapNotNull { sortedToken ->
|
||||
currencies.firstOrNull { it.currency.id.value == sortedToken.id }?.currency
|
||||
}
|
||||
.toList()
|
||||
} ?: emptyMap()
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,9 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.common
|
|||
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
|
||||
internal fun getGroupPlaceholder(index: Int): DraggableItem.Placeholder {
|
||||
return DraggableItem.Placeholder(id = "placeholder_${index.inc()}")
|
||||
internal fun getGroupPlaceholder(index: Int, accountId: String = ""): DraggableItem.Placeholder {
|
||||
return DraggableItem.Placeholder(
|
||||
id = "placeholder_${accountId}_${index.inc()}",
|
||||
accountId = accountId,
|
||||
)
|
||||
}
|
||||
|
|
@ -12,7 +12,9 @@ internal fun List<DraggableItem>.uniteItems(): List<DraggableItem> {
|
|||
1 -> DraggableItem.RoundingMode.Top()
|
||||
lastItemIndex -> DraggableItem.RoundingMode.Bottom()
|
||||
else -> when (item) {
|
||||
is DraggableItem.Placeholder -> DraggableItem.RoundingMode.None
|
||||
is DraggableItem.Portfolio,
|
||||
is DraggableItem.Placeholder,
|
||||
-> DraggableItem.RoundingMode.None
|
||||
is DraggableItem.GroupHeader -> DraggableItem.RoundingMode.Top(showGap = true)
|
||||
is DraggableItem.Token -> if (items[index + 1] is DraggableItem.Placeholder) {
|
||||
DraggableItem.RoundingMode.Bottom(showGap = true)
|
||||
|
|
@ -28,6 +30,48 @@ internal fun List<DraggableItem>.uniteItems(): List<DraggableItem> {
|
|||
}
|
||||
}
|
||||
|
||||
internal fun List<DraggableItem>.uniteItemsV2(isAccountsMode: Boolean): List<DraggableItem> {
|
||||
val items = this
|
||||
val lastItemIndex = items.lastIndex
|
||||
|
||||
return items
|
||||
.asSequence()
|
||||
.mapIndexed { index, item ->
|
||||
val mode = when (index) {
|
||||
0 -> if (item is DraggableItem.Placeholder) {
|
||||
DraggableItem.RoundingMode.None
|
||||
} else {
|
||||
DraggableItem.RoundingMode.Top()
|
||||
}
|
||||
lastItemIndex -> DraggableItem.RoundingMode.Bottom()
|
||||
1 -> if (items.first() is DraggableItem.Placeholder) {
|
||||
DraggableItem.RoundingMode.Top()
|
||||
} else {
|
||||
DraggableItem.RoundingMode.None
|
||||
}
|
||||
else -> when (item) {
|
||||
is DraggableItem.Placeholder -> DraggableItem.RoundingMode.None
|
||||
is DraggableItem.GroupHeader -> if (isAccountsMode) {
|
||||
DraggableItem.RoundingMode.None
|
||||
} else {
|
||||
DraggableItem.RoundingMode.Top(showGap = true)
|
||||
}
|
||||
is DraggableItem.Token -> applyRoundingModeToToken(
|
||||
isAccountsMode = isAccountsMode,
|
||||
items = items,
|
||||
index = index,
|
||||
lastItemIndex = lastItemIndex,
|
||||
)
|
||||
is DraggableItem.Portfolio -> DraggableItem.RoundingMode.Top(showGap = true)
|
||||
}
|
||||
}
|
||||
|
||||
item
|
||||
.updateRoundingMode(mode)
|
||||
.updateShadowVisibility(show = false)
|
||||
}.toList()
|
||||
}
|
||||
|
||||
internal fun List<DraggableItem>.divideMovingItem(movingItem: DraggableItem): List<DraggableItem> {
|
||||
val mutableList = this.toMutableList()
|
||||
val listIterator = mutableList.listIterator()
|
||||
|
|
@ -63,12 +107,55 @@ private fun List<DraggableItem>.prepareItems(): List<DraggableItem> {
|
|||
return mutableListOf<DraggableItem>().apply {
|
||||
add(DraggableItem.Placeholder(firstPlaceholderId))
|
||||
|
||||
val itemsWithoutFirstPlaceholder = if (items.firstOrNull()?.id == firstPlaceholderId) {
|
||||
items.drop(n = 1)
|
||||
} else {
|
||||
items
|
||||
}
|
||||
val itemsWithoutFirstPlaceholder = items.filterNot { it.id == firstPlaceholderId }
|
||||
|
||||
addAll(itemsWithoutFirstPlaceholder)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applying rounding to tokens
|
||||
*
|
||||
* If is in accounts mode without grouping
|
||||
* * PORTFOLIO
|
||||
* * TOKEN
|
||||
* * TOKEN <- add rounding
|
||||
* * PORTFOLIO index + 1 is PORTFOLIO
|
||||
*
|
||||
* If is in accounts mode with grouping
|
||||
* * PORTFOLIO
|
||||
* * PLACEHOLDER
|
||||
* * GROUPING
|
||||
* * TOKEN
|
||||
* * TOKEN <- add rounding
|
||||
* * PLACEHOLDER index + 1 is PLACEHOLDER
|
||||
* * PORTFOLIO index + 2 is PORTFOLIO
|
||||
* * PLACEHOLDER
|
||||
*
|
||||
* If is not accounts mode without grouping
|
||||
* * TOKEN
|
||||
* * TOKEN <- add rounding
|
||||
*
|
||||
* If is not accounts mode with grouping
|
||||
* * PLACEHOLDER
|
||||
* * GROUPING
|
||||
* * TOKEN
|
||||
* * TOKEN <- add rounding
|
||||
* * PLACEHOLDER index + 1 is PLACEHOLDER
|
||||
*/
|
||||
private fun applyRoundingModeToToken(
|
||||
isAccountsMode: Boolean,
|
||||
items: List<DraggableItem>,
|
||||
index: Int,
|
||||
lastItemIndex: Int,
|
||||
) = when {
|
||||
isAccountsMode && index + 1 < lastItemIndex &&
|
||||
(items[index + 1] is DraggableItem.Portfolio ||
|
||||
items[index + 1] is DraggableItem.Placeholder && items[index + 2] is DraggableItem.Portfolio) -> {
|
||||
DraggableItem.RoundingMode.Bottom(showGap = true)
|
||||
}
|
||||
(!isAccountsMode || index + 1 == lastItemIndex) && items[index + 1] is DraggableItem.Placeholder -> {
|
||||
DraggableItem.RoundingMode.Bottom(showGap = true)
|
||||
}
|
||||
else -> DraggableItem.RoundingMode.None
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.common
|
|||
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListUM
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
|
|
@ -15,4 +16,16 @@ internal inline fun OrganizeTokensListState.updateItems(
|
|||
is OrganizeTokensListState.Ungrouped -> copy(items = updatedItems)
|
||||
is OrganizeTokensListState.Empty -> this
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun OrganizeTokensListUM.updateItems(
|
||||
update: (PersistentList<DraggableItem>) -> List<DraggableItem>,
|
||||
): OrganizeTokensListUM {
|
||||
val updatedItems = update(items).toPersistentList()
|
||||
|
||||
return when (this) {
|
||||
is OrganizeTokensListUM.AccountList -> copy(items = updatedItems)
|
||||
is OrganizeTokensListUM.TokensList -> copy(items = updatedItems)
|
||||
OrganizeTokensListUM.EmptyList -> this
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.utils.converter
|
||||
|
||||
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListUM
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupPlaceholder
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.uniteItemsV2
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items.OrganizedTokenListConverter
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal class TokenListToStateConverterV2(
|
||||
private val accountStatusList: AccountStatusList,
|
||||
private val isAccountsMode: Boolean,
|
||||
private val appCurrency: AppCurrency,
|
||||
) : Transformer<OrganizeTokensState> {
|
||||
|
||||
private val accountListItemConverter by lazy {
|
||||
AccountTokenItemConverter(appCurrency = appCurrency, isAccountsMode)
|
||||
}
|
||||
|
||||
override fun transform(prevState: OrganizeTokensState): OrganizeTokensState {
|
||||
val tokenListUM = accountListItemConverter.convert(accountStatusList)
|
||||
|
||||
return prevState.copy(
|
||||
tokenListUM = tokenListUM,
|
||||
header = prevState.header.copy(
|
||||
isEnabled = tokenListUM !is OrganizeTokensListUM.EmptyList,
|
||||
isSortedByBalance = accountStatusList.sortType == TokensSortType.BALANCE,
|
||||
isGrouped = accountStatusList.groupType == TokensGroupType.NETWORK,
|
||||
),
|
||||
actions = prevState.actions.copy(
|
||||
canApply = tokenListUM !is OrganizeTokensListUM.EmptyList,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal class AccountTokenItemConverter(
|
||||
private val appCurrency: AppCurrency,
|
||||
private val isAccountsMode: Boolean,
|
||||
) : Converter<AccountStatusList, OrganizeTokensListUM> {
|
||||
|
||||
private val organizedTokenListConverter by lazy {
|
||||
OrganizedTokenListConverter(appCurrency)
|
||||
}
|
||||
|
||||
override fun convert(value: AccountStatusList): OrganizeTokensListUM {
|
||||
val isGrouping = value.groupType == TokensGroupType.NETWORK
|
||||
return if (isAccountsMode) {
|
||||
OrganizeTokensListUM.AccountList(
|
||||
isGrouped = isGrouping,
|
||||
items = value.accountStatuses
|
||||
.asSequence()
|
||||
.filterIsInstance<AccountStatus.CryptoPortfolio>()
|
||||
.flatMap { accountStatus ->
|
||||
if (accountStatus.tokenList != TokenList.Empty) {
|
||||
buildList {
|
||||
add(
|
||||
DraggableItem.Portfolio(
|
||||
tokenItemState = AccountCryptoPortfolioItemStateConverter(
|
||||
appCurrency = appCurrency,
|
||||
account = accountStatus.account,
|
||||
).convert(TotalFiatBalance.Loading),
|
||||
),
|
||||
)
|
||||
if (isGrouping) {
|
||||
add(getGroupPlaceholder(accountId = accountStatus.accountId.value, index = -1))
|
||||
}
|
||||
addAll(organizedTokenListConverter.convert(accountStatus))
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}.toList()
|
||||
.uniteItemsV2(true).toPersistentList(),
|
||||
)
|
||||
} else {
|
||||
OrganizeTokensListUM.TokensList(
|
||||
isGrouped = isGrouping,
|
||||
items = buildList {
|
||||
if (isGrouping) {
|
||||
add(getGroupPlaceholder(accountId = value.mainAccount.accountId.value, index = -1))
|
||||
}
|
||||
addAll(organizedTokenListConverter.convert(value.mainAccount))
|
||||
}.uniteItemsV2(false)
|
||||
.toPersistentList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items
|
||||
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getTokenItemId
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class CryptoCurrencyToDraggableItemConverterV2(
|
||||
private val appCurrency: AppCurrency,
|
||||
) : Converter<AccountCryptoCurrencyStatus, DraggableItem.Token> {
|
||||
|
||||
private val iconStateConverter = CryptoCurrencyToIconStateConverter()
|
||||
|
||||
override fun convert(value: AccountCryptoCurrencyStatus): DraggableItem.Token {
|
||||
return createDraggableToken(value)
|
||||
}
|
||||
|
||||
private fun createDraggableToken(accountCryptoCurrencyStatus: AccountCryptoCurrencyStatus): DraggableItem.Token {
|
||||
val (account, currencyStatus) = accountCryptoCurrencyStatus
|
||||
return DraggableItem.Token(
|
||||
tokenItemState = createTokenItemState(currencyStatus, appCurrency),
|
||||
groupId = getGroupHeaderId(currencyStatus.currency.network),
|
||||
accountId = account.accountId.value,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createTokenItemState(
|
||||
currencyStatus: CryptoCurrencyStatus,
|
||||
appCurrency: AppCurrency,
|
||||
): TokenItemState.Draggable {
|
||||
val currency = currencyStatus.currency
|
||||
|
||||
return TokenItemState.Draggable(
|
||||
id = getTokenItemId(currency.id),
|
||||
iconState = iconStateConverter.convert(currencyStatus),
|
||||
titleState = TokenItemState.TitleState.Content(text = stringReference(currency.name)),
|
||||
subtitle2State = if (currencyStatus.value.isError) {
|
||||
TokenItemState.Subtitle2State.Unreachable
|
||||
} else {
|
||||
TokenItemState.Subtitle2State.TextContent(text = getFormattedFiatAmount(currencyStatus, appCurrency))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun getFormattedFiatAmount(currency: CryptoCurrencyStatus, appCurrency: AppCurrency): String {
|
||||
val yieldBalance = currency.value.yieldBalance as? YieldBalance.Data
|
||||
val fiatRate = currency.value.fiatRate ?: BigDecimal.ZERO
|
||||
val fiatYieldBalance = yieldBalance?.getTotalWithRewardsStakingBalance(currency.currency.network.rawId)
|
||||
?.multiply(fiatRate).orZero()
|
||||
|
||||
val fiatAmount = currency.value.fiatAmount ?: return BigDecimalFormatConstants.EMPTY_BALANCE_SIGN
|
||||
return (fiatAmount + fiatYieldBalance).format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items
|
||||
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.tokenlist.TokenList.GroupedByNetwork.NetworkGroup
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupPlaceholder
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class NetworkGroupToDraggableItemsConverterV2(
|
||||
private val itemConverter: CryptoCurrencyToDraggableItemConverterV2,
|
||||
) : Converter<Pair<Account.CryptoPortfolio, NetworkGroup>, List<DraggableItem>> {
|
||||
|
||||
override fun convert(value: Pair<Account.CryptoPortfolio, NetworkGroup>): List<DraggableItem> {
|
||||
val (account, networkGroup) = value
|
||||
return buildList {
|
||||
add(createGroupHeader(account.accountId, networkGroup))
|
||||
addAll(createTokens(account, networkGroup))
|
||||
}
|
||||
}
|
||||
|
||||
override fun convertList(
|
||||
input: Collection<Pair<Account.CryptoPortfolio, NetworkGroup>>,
|
||||
): List<List<DraggableItem>> {
|
||||
return input.mapIndexed { index, pair ->
|
||||
convert(pair).toMutableList()
|
||||
.also { mutableGroup ->
|
||||
mutableGroup.add(
|
||||
getGroupPlaceholder(accountId = pair.first.accountId.value, index = index),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createGroupHeader(accountId: AccountId, group: NetworkGroup) = DraggableItem.GroupHeader(
|
||||
id = getGroupHeaderId(group.network),
|
||||
accountId = accountId.value,
|
||||
networkName = group.network.name,
|
||||
)
|
||||
|
||||
private fun createTokens(account: Account.CryptoPortfolio, group: NetworkGroup): List<DraggableItem.Token> {
|
||||
return itemConverter.convertList(
|
||||
group.currencies.map {
|
||||
AccountCryptoCurrencyStatus(
|
||||
account = account,
|
||||
status = it,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items
|
||||
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal class OrganizedTokenListConverter(
|
||||
private val appCurrency: AppCurrency,
|
||||
) : Converter<AccountStatus, PersistentList<DraggableItem>> {
|
||||
|
||||
private val tokensConverter by lazy { CryptoCurrencyToDraggableItemConverterV2(appCurrency) }
|
||||
private val groupsConverter by lazy {
|
||||
NetworkGroupToDraggableItemsConverterV2(tokensConverter)
|
||||
}
|
||||
|
||||
override fun convert(value: AccountStatus): PersistentList<DraggableItem> {
|
||||
val cryptoAccount = value.account as? Account.CryptoPortfolio ?: return persistentListOf()
|
||||
return when (val tokenList = value.getCryptoTokenList()) {
|
||||
is TokenList.GroupedByNetwork -> groupsConverter.convertList(
|
||||
tokenList.groups.map { cryptoAccount to it },
|
||||
)
|
||||
.flatten()
|
||||
.toPersistentList()
|
||||
|
||||
is TokenList.Ungrouped -> tokensConverter.convertList(
|
||||
value.flattenCurrencies().map {
|
||||
AccountCryptoCurrencyStatus(
|
||||
account = cryptoAccount,
|
||||
status = it,
|
||||
)
|
||||
},
|
||||
).toPersistentList()
|
||||
|
||||
is TokenList.Empty -> persistentListOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,9 @@ internal class DragAndDropAdapter(
|
|||
return when (draggingItem) {
|
||||
is DraggableItem.GroupHeader -> checkCanMoveHeaderOver(dragOver, dragOverItem, items.lastIndex)
|
||||
is DraggableItem.Token -> checkCanMoveTokenOver(draggingItem, dragOverItem)
|
||||
is DraggableItem.Placeholder -> false
|
||||
is DraggableItem.Placeholder,
|
||||
is DraggableItem.Portfolio,
|
||||
-> false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +63,9 @@ internal class DragAndDropAdapter(
|
|||
|
||||
updateListState(DragOperation.Type.Start) {
|
||||
when (item) {
|
||||
is DraggableItem.Placeholder -> items
|
||||
is DraggableItem.Placeholder,
|
||||
is DraggableItem.Portfolio,
|
||||
-> items
|
||||
is DraggableItem.GroupHeader -> draggableGroupsOperations.collapseGroup(items, item)
|
||||
is DraggableItem.Token -> when (this) {
|
||||
is OrganizeTokensListState.GroupedByNetwork -> items.divideMovingItem(item)
|
||||
|
|
@ -81,7 +85,9 @@ internal class DragAndDropAdapter(
|
|||
when (draggingItem) {
|
||||
is DraggableItem.GroupHeader -> draggableGroupsOperations.expandGroups(items)
|
||||
is DraggableItem.Token -> items.uniteItems()
|
||||
is DraggableItem.Placeholder -> items
|
||||
is DraggableItem.Placeholder,
|
||||
is DraggableItem.Portfolio,
|
||||
-> items
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +150,9 @@ internal class DragAndDropAdapter(
|
|||
return when (moveOverItem) {
|
||||
is DraggableItem.GroupHeader -> false // Token item can not be moved to group item
|
||||
is DraggableItem.Token -> item.groupId == moveOverItem.groupId // Token item can not be moved over its group
|
||||
is DraggableItem.Placeholder -> false
|
||||
is DraggableItem.Portfolio,
|
||||
is DraggableItem.Placeholder,
|
||||
-> false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,209 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.utils.dnd
|
||||
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.DragAndDropIntents
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListUM
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.divideMovingItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.uniteItemsV2
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.updateItems
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.mutate
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import org.burnoutcrew.reorderable.ItemPosition
|
||||
|
||||
internal class DragAndDropAdapterV2(
|
||||
private val tokenListUMProvider: Provider<OrganizeTokensListUM>,
|
||||
) : DragAndDropIntents {
|
||||
|
||||
private val tokenListUM: OrganizeTokensListUM
|
||||
get() = tokenListUMProvider.invoke()
|
||||
|
||||
private val draggableGroupsOperations = DraggableGroupsOperations()
|
||||
|
||||
private val dragAndDropUpdatesInternal: MutableStateFlow<DragOperation?> = MutableStateFlow(value = null)
|
||||
|
||||
private var draggingItem: DraggableItem? = null
|
||||
private var draggingListState: OrganizeTokensListUM? = null
|
||||
|
||||
val dragAndDropUpdates: Flow<DragOperation>
|
||||
get() = dragAndDropUpdatesInternal.filterNotNull()
|
||||
|
||||
override fun canDragItemOver(dragOver: ItemPosition, dragging: ItemPosition): Boolean {
|
||||
val items = when (val listState = tokenListUM) {
|
||||
is OrganizeTokensListUM.AccountList -> listState.items
|
||||
is OrganizeTokensListUM.TokensList -> {
|
||||
if (tokenListUM.isGrouped) {
|
||||
listState.items
|
||||
} else {
|
||||
return true // If ungrouped then item can be moved anywhere
|
||||
}
|
||||
}
|
||||
OrganizeTokensListUM.EmptyList -> return true
|
||||
}
|
||||
|
||||
val (dragOverItem, draggingItem) = findItemsToMove(
|
||||
items = items,
|
||||
moveOverItemKey = dragOver.key,
|
||||
movedItemKey = dragging.key,
|
||||
)
|
||||
|
||||
if (dragOverItem == null || draggingItem == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
val canDrag = when (draggingItem) {
|
||||
is DraggableItem.GroupHeader -> checkCanMoveHeaderOver(
|
||||
item = draggingItem,
|
||||
moveOverItem = dragOverItem,
|
||||
)
|
||||
is DraggableItem.Token -> checkCanMoveTokenOver(
|
||||
item = draggingItem,
|
||||
moveOverItem = dragOverItem,
|
||||
isAccountsMode = tokenListUM is OrganizeTokensListUM.AccountList,
|
||||
isGrouped = tokenListUM.isGrouped,
|
||||
)
|
||||
is DraggableItem.Placeholder,
|
||||
is DraggableItem.Portfolio,
|
||||
-> false
|
||||
}
|
||||
|
||||
return canDrag
|
||||
}
|
||||
|
||||
override fun onItemDraggingStart(item: DraggableItem) {
|
||||
if (draggingItem != null) return
|
||||
draggingItem = item
|
||||
|
||||
updateListState(DragOperation.Type.Start) {
|
||||
when (item) {
|
||||
is DraggableItem.Placeholder,
|
||||
is DraggableItem.Portfolio,
|
||||
-> items
|
||||
is DraggableItem.GroupHeader -> draggableGroupsOperations.collapseGroupV2(items, item)
|
||||
.divideMovingItem(item)
|
||||
is DraggableItem.Token -> items.divideMovingItem(item)
|
||||
}
|
||||
}
|
||||
|
||||
draggingListState = tokenListUM
|
||||
}
|
||||
|
||||
override fun onItemDraggingEnd() {
|
||||
val draggingItem = draggingItem ?: return
|
||||
|
||||
updateListState(DragOperation.Type.End(isItemsOrderChanged = checkIsItemsOrderChanged())) {
|
||||
when (draggingItem) {
|
||||
is DraggableItem.GroupHeader -> {
|
||||
draggableGroupsOperations.expandGroupsV2(items)
|
||||
.uniteItemsV2(tokenListUM is OrganizeTokensListUM.AccountList)
|
||||
}
|
||||
is DraggableItem.Token -> {
|
||||
items.uniteItemsV2(tokenListUM is OrganizeTokensListUM.AccountList)
|
||||
}
|
||||
is DraggableItem.Placeholder,
|
||||
is DraggableItem.Portfolio,
|
||||
-> items
|
||||
}
|
||||
}
|
||||
|
||||
this.draggingItem = null
|
||||
}
|
||||
|
||||
override fun onItemDragged(from: ItemPosition, to: ItemPosition) {
|
||||
updateListState(DragOperation.Type.Dragged) {
|
||||
items.mutate {
|
||||
it.add(to.index, it.removeAt(from.index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateListState(type: DragOperation.Type, block: OrganizeTokensListUM.() -> List<DraggableItem>) {
|
||||
val updatedState = tokenListUM.updateItems { block(tokenListUM) }
|
||||
|
||||
dragAndDropUpdatesInternal.value = DragOperation(type, updatedState)
|
||||
}
|
||||
|
||||
private fun findItemsToMove(
|
||||
items: List<DraggableItem>,
|
||||
moveOverItemKey: Any?,
|
||||
movedItemKey: Any?,
|
||||
): Pair<DraggableItem?, DraggableItem?> {
|
||||
var moveOverItem: DraggableItem? = null
|
||||
var movedItem: DraggableItem? = null
|
||||
|
||||
for (item in items) {
|
||||
if (item.id == moveOverItemKey) {
|
||||
moveOverItem = item
|
||||
}
|
||||
if (item.id == movedItemKey) {
|
||||
movedItem = item
|
||||
}
|
||||
if (moveOverItem != null && movedItem != null) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return Pair(moveOverItem, movedItem)
|
||||
}
|
||||
|
||||
private fun checkCanMoveHeaderOver(item: DraggableItem.GroupHeader, moveOverItem: DraggableItem): Boolean {
|
||||
return when (moveOverItem) {
|
||||
// Header can be moved only in its account
|
||||
is DraggableItem.Placeholder -> item.accountId == moveOverItem.accountId
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkCanMoveTokenOver(
|
||||
item: DraggableItem.Token,
|
||||
moveOverItem: DraggableItem,
|
||||
isGrouped: Boolean,
|
||||
isAccountsMode: Boolean,
|
||||
): Boolean {
|
||||
return when (moveOverItem) {
|
||||
is DraggableItem.GroupHeader -> false // Token item can not be moved to group item
|
||||
is DraggableItem.Token -> when {
|
||||
// Token item can be moved only in its group
|
||||
isGrouped -> item.groupId == moveOverItem.groupId
|
||||
|
||||
// Token item can be moved only in its account
|
||||
isAccountsMode -> item.accountId == moveOverItem.accountId
|
||||
|
||||
// If ungrouped and not accounts mode then item can be moved anywhere
|
||||
else -> true
|
||||
}
|
||||
is DraggableItem.Portfolio,
|
||||
is DraggableItem.Placeholder,
|
||||
-> false // Token item can not be moved to portfolio or placeholder
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkIsItemsOrderChanged(): Boolean {
|
||||
fun OrganizeTokensListUM?.getItemsIds(): List<Any>? = this?.items?.mapNotNull { item ->
|
||||
if (item is DraggableItem.Placeholder) {
|
||||
null
|
||||
} else {
|
||||
item.id
|
||||
}
|
||||
}
|
||||
|
||||
return tokenListUM.getItemsIds() != draggingListState.getItemsIds()
|
||||
}
|
||||
|
||||
data class DragOperation(
|
||||
val type: Type,
|
||||
val listState: OrganizeTokensListUM,
|
||||
) {
|
||||
|
||||
sealed class Type {
|
||||
|
||||
data object Start : Type()
|
||||
|
||||
data object Dragged : Type()
|
||||
|
||||
data class End(val isItemsOrderChanged: Boolean) : Type()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,21 @@ internal class DraggableGroupsOperations {
|
|||
return itemsWithoutGroupTokens.divideMovingItem(movingGroup)
|
||||
}
|
||||
|
||||
fun collapseGroupV2(items: List<DraggableItem>, movingGroup: DraggableItem.GroupHeader): List<DraggableItem> {
|
||||
if (!groupIdToTokens.isNullOrEmpty()) return items
|
||||
|
||||
groupIdToTokens = items
|
||||
.asSequence()
|
||||
.filterIsInstance<DraggableItem.Token>()
|
||||
.groupBy { it.groupId }
|
||||
|
||||
val itemsWithoutGroupTokens = items.filterNot {
|
||||
it is DraggableItem.Token && it.groupId == movingGroup.id
|
||||
}
|
||||
|
||||
return itemsWithoutGroupTokens.divideMovingItem(movingGroup)
|
||||
}
|
||||
|
||||
fun expandGroups(items: List<DraggableItem>): List<DraggableItem> {
|
||||
if (groupIdToTokens.isNullOrEmpty()) return items
|
||||
|
||||
|
|
@ -46,4 +61,49 @@ internal class DraggableGroupsOperations {
|
|||
|
||||
return expandedGroups
|
||||
}
|
||||
|
||||
fun expandGroupsV2(items: List<DraggableItem>): List<DraggableItem> {
|
||||
if (groupIdToTokens.isNullOrEmpty()) return items
|
||||
|
||||
val accountList = items.filterIsInstance<DraggableItem.Portfolio>()
|
||||
val currentGroups = items.filterIsInstance<DraggableItem.GroupHeader>()
|
||||
|
||||
val expandedGroups = if (items.any { it is DraggableItem.Portfolio }) {
|
||||
accountList
|
||||
.asSequence()
|
||||
.flatMap { account ->
|
||||
buildList {
|
||||
add(account)
|
||||
currentGroups
|
||||
.asSequence()
|
||||
.filter { it.accountId == account.id }
|
||||
.forEachIndexed { index, group ->
|
||||
if (index == 0) {
|
||||
add(getGroupPlaceholder(accountId = group.accountId, index = -1))
|
||||
}
|
||||
add(group)
|
||||
addAll(groupIdToTokens?.get(group.id).orEmpty())
|
||||
add(getGroupPlaceholder(accountId = group.accountId, index = index))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
currentGroups
|
||||
.asSequence()
|
||||
.flatMapIndexed { index, group ->
|
||||
buildList {
|
||||
if (index == 0) {
|
||||
add(getGroupPlaceholder(accountId = group.accountId, index = -1))
|
||||
}
|
||||
add(group)
|
||||
addAll(groupIdToTokens?.get(group.id).orEmpty())
|
||||
add(getGroupPlaceholder(accountId = group.accountId, index = index))
|
||||
}
|
||||
}
|
||||
}.toList()
|
||||
|
||||
groupIdToTokens = null
|
||||
|
||||
return expandedGroups
|
||||
}
|
||||
}
|
||||
|
|
@ -115,8 +115,8 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
router.push(AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding))
|
||||
}
|
||||
|
||||
override fun openTangemPayDetails(config: TangemPayDetailsConfig) {
|
||||
router.push(AppRoute.TangemPayDetails(config))
|
||||
override fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {
|
||||
router.push(AppRoute.TangemPayDetails(userWalletId = userWalletId, config = config))
|
||||
}
|
||||
|
||||
override fun openYieldSupplyBottomSheet(
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ internal interface InnerWalletRouter {
|
|||
|
||||
fun openTangemPayOnboarding()
|
||||
|
||||
fun openTangemPayDetails(config: TangemPayDetailsConfig)
|
||||
fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)
|
||||
|
||||
/** Open BS abput yield supply active and all money deposited in AAVE */
|
||||
fun openYieldSupplyBottomSheet(
|
||||
|
|
|
|||
|
|
@ -31,11 +31,13 @@ import com.tangem.domain.tokens.error.TokenListError
|
|||
import com.tangem.domain.wallets.models.SeedPhraseNotificationsStatus
|
||||
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
|
||||
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
|
||||
import com.tangem.domain.hotwallet.GetAccessCodeSkippedUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isBitcoin
|
||||
import com.tangem.utils.extensions.addIf
|
||||
import com.tangem.utils.extensions.isPositive
|
||||
|
|
@ -62,6 +64,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
private val getOnrampCountryUseCase: GetOnrampCountryUseCase,
|
||||
private val notificationsRepository: NotificationsRepository,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val getAccessCodeSkippedUseCase: GetAccessCodeSkippedUseCase,
|
||||
) {
|
||||
|
||||
@Suppress("UNCHECKED_CAST", "MagicNumber")
|
||||
|
|
@ -98,6 +101,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.VisaPresale),
|
||||
shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Sepa),
|
||||
notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key),
|
||||
getAccessCodeSkippedUseCase(userWallet.walletId),
|
||||
) { array -> array }
|
||||
.combine(tokenListFlow()) { array, any: Any -> arrayOf(any).plus(elements = array) }
|
||||
.map { array ->
|
||||
|
|
@ -110,13 +114,14 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
val shouldShowVisaPromo = array[4] as Boolean
|
||||
val shouldShowSepaBanner = array[5] as Boolean
|
||||
val shouldShowEnablePushesReminderNotification = array[6] as Boolean
|
||||
val accessCodeSkipped = array[7] as Boolean
|
||||
|
||||
buildList {
|
||||
addUsedOutdatedDataNotification(totalFiatBalance)
|
||||
|
||||
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
|
||||
|
||||
addFinishWalletActivationNotification(userWallet, totalFiatBalance, clickIntents)
|
||||
addFinishWalletActivationNotification(userWallet, totalFiatBalance, clickIntents, accessCodeSkipped)
|
||||
|
||||
addVisaPresalePromoNotification(clickIntents, shouldShowVisaPromo)
|
||||
|
||||
|
|
@ -405,10 +410,14 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
userWallet: UserWallet,
|
||||
totalFiatBalance: Lce<TokenListError, TotalFiatBalance>,
|
||||
clickIntents: WalletClickIntents,
|
||||
accessCodeSkipped: Boolean,
|
||||
) {
|
||||
if (userWallet !is UserWallet.Hot) return
|
||||
|
||||
val shouldShowFinishActivation = !userWallet.backedUp
|
||||
val isBackupExists = userWallet.backedUp
|
||||
val isAccessCodeRequired = userWallet.hotWalletId.authType == HotWalletId.AuthType.NoPassword &&
|
||||
!accessCodeSkipped
|
||||
val shouldShowFinishActivation = !isBackupExists || isAccessCodeRequired
|
||||
|
||||
val type = totalFiatBalance.fold(
|
||||
ifLoading = { it.getFinishWalletActivationType() },
|
||||
|
|
@ -427,11 +436,11 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
buttonsState = when (type) {
|
||||
WalletActivationBannerType.Warning -> ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(R.string.hw_activation_need_finish),
|
||||
onClick = { clickIntents.onFinishWalletActivationClick(type) },
|
||||
onClick = { clickIntents.onFinishWalletActivationClick(type, isBackupExists) },
|
||||
)
|
||||
else -> ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.hw_activation_need_finish),
|
||||
onClick = { clickIntents.onFinishWalletActivationClick(type) },
|
||||
onClick = { clickIntents.onFinishWalletActivationClick(type, isBackupExists) },
|
||||
)
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.right
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusProducer
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusSupplier
|
||||
import com.tangem.domain.card.CardTypesResolver
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
|
|
@ -22,8 +28,11 @@ import kotlinx.coroutines.flow.*
|
|||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
internal class GetSingleWalletWarningsFactory @Inject constructor(
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
|
||||
private val singleAccountStatusSupplier: SingleAccountStatusSupplier,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||
|
|
@ -40,7 +49,7 @@ internal class GetSingleWalletWarningsFactory @Inject constructor(
|
|||
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
|
||||
|
||||
return combine(
|
||||
flow = getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWallet.walletId),
|
||||
flow = getPrimaryCurrencyStatusFlow(userWallet),
|
||||
flow2 = isReadyToShowRateAppUseCase().conflate(),
|
||||
flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(),
|
||||
flow4 = getWalletsUseCase().conflate(),
|
||||
|
|
@ -217,6 +226,29 @@ internal class GetSingleWalletWarningsFactory @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getPrimaryCurrencyStatusFlow(
|
||||
userWallet: UserWallet,
|
||||
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
|
||||
return if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
getAccountStatusFlow(userWallet).mapNotNull { accountStatus ->
|
||||
accountStatus.flattenCurrencies().firstOrNull()
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.conflate()
|
||||
.map { it.right() }
|
||||
} else {
|
||||
getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWallet.walletId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAccountStatusFlow(userWallet: UserWallet): Flow<AccountStatus> {
|
||||
val accountId = AccountId.forMainCryptoPortfolio(userWalletId = userWallet.walletId)
|
||||
|
||||
return singleAccountStatusSupplier(SingleAccountStatusProducer.Params(accountId))
|
||||
.distinctUntilChanged()
|
||||
.conflate()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val NOTE_MIGRATION_URL = "https://tangem.com/en/?promocode=Note10"
|
||||
const val MAX_REMAINING_SIGNATURES_COUNT = 10
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class HasSingleWalletSignedHashesUseCase @Inject constructor(
|
|||
|
||||
private fun UserWallet.Cold.isCorrectCardType(): Boolean {
|
||||
return with(scanResponse.cardTypesResolver) {
|
||||
!DemoConfig().isDemoCardId(cardId) && isReleaseFirmwareType() && !isMultiwalletAllowed() && !isTangemTwins()
|
||||
!DemoConfig.isDemoCardId(cardId) && isReleaseFirmwareType() && !isMultiwalletAllowed() && !isTangemTwins()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ internal class WalletImageResolver @Inject constructor(
|
|||
cardTypesResolver.isTangemTwins() -> R.drawable.ill_twins_120_106
|
||||
cardTypesResolver.isStart2Coin() -> R.drawable.ill_start2coin_120_106
|
||||
cardTypesResolver.isTangemNote() -> noteImage?.imageResId
|
||||
DemoConfig.isDemoNoteAsMultiwallet(userWallet.cardId) -> R.drawable.ill_wallet2_cards2_120_106
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
@ -84,7 +85,7 @@ internal class WalletImageResolver @Inject constructor(
|
|||
@DrawableRes twoBackupResId: Int = R.drawable.ill_wallet2_cards3_120_106,
|
||||
): Int? {
|
||||
return resolveWalletWithBackups { count ->
|
||||
if (DemoConfig().isDemoCardId(cardId)) return@resolveWalletWithBackups oneBackupResId
|
||||
if (DemoConfig.isDemoCardId(cardId)) return@resolveWalletWithBackups oneBackupResId
|
||||
|
||||
when (count) {
|
||||
WALLET_WITH_ONE_BACKUP_COUNT -> oneBackupResId
|
||||
|
|
|
|||
|
|
@ -1,21 +1,37 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class WalletWithFundsChecker @Inject constructor(
|
||||
private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase,
|
||||
) {
|
||||
|
||||
private val statusByWalletId = ConcurrentHashMap<UserWalletId, Boolean>()
|
||||
|
||||
suspend fun check(tokenList: TokenList) {
|
||||
val hasNonZeroWallets = tokenList.flattenCurrencies().hasNonZeroWallets()
|
||||
|
||||
if (hasNonZeroWallets) setWalletWithFundsFoundUseCase()
|
||||
}
|
||||
|
||||
suspend fun check(userWalletId: UserWalletId, currencies: List<CryptoCurrencyStatus>) {
|
||||
val hasNonZeroWallets = currencies.hasNonZeroWallets()
|
||||
val prevStatus = statusByWalletId.get(userWalletId)
|
||||
|
||||
if (hasNonZeroWallets && prevStatus != true) {
|
||||
statusByWalletId[userWalletId] = true
|
||||
setWalletWithFundsFoundUseCase()
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>.hasNonZeroWallets(): Boolean {
|
||||
return any {
|
||||
val amount = it.value.amount ?: return@any false
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders
|
||||
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
|
|
@ -8,11 +9,16 @@ import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
|||
import com.tangem.feature.wallet.presentation.wallet.loaders.implementors.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ModelScoped
|
||||
internal class WalletContentLoaderFactory @Inject constructor(
|
||||
private val multiWalletContentLoaderFactory: MultiWalletContentLoaderFactory,
|
||||
private val multiWalletContentLoaderV2Factory: MultiWalletContentLoaderV2.Factory,
|
||||
private val singleWalletWithTokenContentLoaderFactory: SingleWalletWithTokenContentLoaderFactory,
|
||||
private val singleWalletWithTokenContentLoaderV2Factory: SingleWalletWithTokenContentLoaderV2.Factory,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val singleWalletContentLoaderFactory: SingleWalletContentLoaderFactory,
|
||||
private val singleWalletContentLoaderV2Factory: SingleWalletContentLoaderV2.Factory,
|
||||
private val visaWalletContentLoaderFactory: VisaWalletContentLoaderFactory,
|
||||
) {
|
||||
|
||||
|
|
@ -23,16 +29,28 @@ internal class WalletContentLoaderFactory @Inject constructor(
|
|||
): WalletContentLoader? {
|
||||
return when {
|
||||
userWallet.isMultiCurrency -> {
|
||||
multiWalletContentLoaderFactory.create(userWallet, clickIntents)
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
multiWalletContentLoaderV2Factory.create(userWallet)
|
||||
} else {
|
||||
multiWalletContentLoaderFactory.create(userWallet, clickIntents)
|
||||
}
|
||||
}
|
||||
userWallet is UserWallet.Cold && userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() -> {
|
||||
singleWalletWithTokenContentLoaderFactory.create(userWallet, clickIntents)
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
singleWalletWithTokenContentLoaderV2Factory.create(userWallet)
|
||||
} else {
|
||||
singleWalletWithTokenContentLoaderFactory.create(userWallet, clickIntents)
|
||||
}
|
||||
}
|
||||
userWallet is UserWallet.Cold && userWallet.scanResponse.cardTypesResolver.isVisaWallet() -> {
|
||||
visaWalletContentLoaderFactory.create(userWallet, clickIntents, isRefresh)
|
||||
}
|
||||
userWallet is UserWallet.Cold && !userWallet.isMultiCurrency -> {
|
||||
singleWalletContentLoaderFactory.create(userWallet, clickIntents, isRefresh)
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
singleWalletContentLoaderV2Factory.create(userWallet, isRefresh)
|
||||
} else {
|
||||
singleWalletContentLoaderFactory.create(userWallet, clickIntents, isRefresh)
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.tangem.domain.wallets.repository.WalletsRepository
|
|||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
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.analytics.utils.WalletWarningsSingleEventSender
|
||||
|
|
@ -23,6 +22,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
|||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Deprecated("Use MultiWalletContentLoaderV2 instead")
|
||||
@ModelScoped
|
||||
internal class MultiWalletContentLoader(
|
||||
private val userWallet: UserWallet,
|
||||
|
|
@ -41,7 +41,6 @@ internal class MultiWalletContentLoader(
|
|||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val walletsRepository: WalletsRepository,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
private val stakingApyFlowUseCase: StakingApyFlowUseCase,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
|
@ -57,7 +56,6 @@ internal class MultiWalletContentLoader(
|
|||
tokenListStore = tokenListStore,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
|
||||
accountDependencies = accountDependencies,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
stakingApyFlowUseCase = stakingApyFlowUseCase,
|
||||
).let(::add)
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
|||
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.nft.GetNFTCollectionsUseCase
|
||||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
|
|
@ -19,10 +19,10 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Deprecated("Use MultiWalletContentLoaderV2.Factory instead")
|
||||
@ModelScoped
|
||||
internal class MultiWalletContentLoaderFactory @Inject constructor(
|
||||
private val stateHolder: WalletStateController,
|
||||
|
|
@ -39,7 +39,6 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
private val walletsRepository: WalletsRepository,
|
||||
private val getNFTCollectionsUseCase: GetNFTCollectionsUseCase,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
private val stakingApyFlowUseCase: StakingApyFlowUseCase,
|
||||
) {
|
||||
|
|
@ -62,7 +61,6 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
walletsRepository = walletsRepository,
|
||||
getNFTCollectionsUseCase = getNFTCollectionsUseCase,
|
||||
currenciesRepository = currenciesRepository,
|
||||
accountDependencies = accountDependencies,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
stakingApyFlowUseCase = stakingApyFlowUseCase,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class MultiWalletContentLoaderV2 @AssistedInject constructor(
|
||||
@Assisted private val userWallet: UserWallet,
|
||||
private val accountListSubscriberFactory: AccountListSubscriber.Factory,
|
||||
private val walletNFTListSubscriberV2Factory: WalletNFTListSubscriberV2.Factory,
|
||||
private val stateController: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
|
||||
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val checkWalletWithFundsSubscriberFactory: CheckWalletWithFundsSubscriber.Factory,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> = listOf(
|
||||
accountListSubscriberFactory.create(userWallet = userWallet),
|
||||
walletNFTListSubscriberV2Factory.create(userWallet = userWallet),
|
||||
checkWalletWithFundsSubscriberFactory.create(userWallet = userWallet),
|
||||
MultiWalletWarningsSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateHolder = stateController,
|
||||
clickIntents = clickIntents,
|
||||
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
|
||||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
|
||||
),
|
||||
MultiWalletActionButtonsSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateHolder = stateController,
|
||||
getStoryContentUseCase = getStoryContentUseCase,
|
||||
),
|
||||
WalletDropDownItemsSubscriber(
|
||||
stateHolder = stateController,
|
||||
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet): MultiWalletContentLoaderV2
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
|||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
|
||||
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
|
||||
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
|
||||
|
|
@ -9,10 +10,8 @@ import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
|||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
|
|
@ -36,7 +35,6 @@ internal class SingleWalletContentLoader(
|
|||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> {
|
||||
|
|
@ -55,7 +53,6 @@ internal class SingleWalletContentLoader(
|
|||
clickIntents = clickIntents,
|
||||
getSingleCryptoCurrencyStatusUseCase = getSingleCryptoCurrencyStatusUseCase,
|
||||
getCryptoCurrencyActionsUseCase = getCryptoCurrencyActionsUseCase,
|
||||
accountDependencies = accountDependencies,
|
||||
),
|
||||
SingleWalletNotificationsSubscriber(
|
||||
userWallet = userWallet,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
|||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
|
||||
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
|
||||
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
|
||||
|
|
@ -10,10 +11,8 @@ import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
|||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
|
|
@ -21,6 +20,7 @@ import javax.inject.Inject
|
|||
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
@Deprecated("Use SingleWalletContentLoaderV2.Factory instead")
|
||||
internal class SingleWalletContentLoaderFactory @Inject constructor(
|
||||
private val stateHolder: WalletStateController,
|
||||
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
|
||||
|
|
@ -35,14 +35,12 @@ internal class SingleWalletContentLoaderFactory @Inject constructor(
|
|||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
) {
|
||||
|
||||
fun create(userWallet: UserWallet.Cold, clickIntents: WalletClickIntents, isRefresh: Boolean): WalletContentLoader {
|
||||
return SingleWalletContentLoader(
|
||||
userWallet = userWallet,
|
||||
clickIntents = clickIntents,
|
||||
accountDependencies = accountDependencies,
|
||||
isRefresh = isRefresh,
|
||||
stateHolder = stateHolder,
|
||||
getSingleCryptoCurrencyStatusUseCase = getSingleCryptoCurrencyStatusUseCase,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
|
||||
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class SingleWalletContentLoaderV2 @AssistedInject constructor(
|
||||
@Assisted private val userWallet: UserWallet.Cold,
|
||||
@Assisted private val isRefresh: Boolean,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val stateHolder: WalletStateController,
|
||||
private val getCryptoCurrencyActionsUseCaseV2: GetCryptoCurrencyActionsUseCaseV2,
|
||||
private val getSingleWalletWarningsFactory: GetSingleWalletWarningsFactory,
|
||||
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
|
||||
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val walletWithFundsChecker: WalletWithFundsChecker,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> = listOf(
|
||||
PrimaryCurrencySubscriberV2(
|
||||
userWallet = userWallet,
|
||||
singleAccountStatusListSupplier = accountDependencies.singleAccountStatusListSupplier,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
stateController = stateHolder,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
),
|
||||
SingleWalletButtonsSubscriberV2(
|
||||
userWallet = userWallet,
|
||||
singleAccountStatusListSupplier = accountDependencies.singleAccountStatusListSupplier,
|
||||
stateController = stateHolder,
|
||||
clickIntents = clickIntents,
|
||||
getCryptoCurrencyActionsUseCaseV2 = getCryptoCurrencyActionsUseCaseV2,
|
||||
),
|
||||
SingleWalletNotificationsSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateHolder = stateHolder,
|
||||
clickIntents = clickIntents,
|
||||
getSingleWalletWarningsFactory = getSingleWalletWarningsFactory,
|
||||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
),
|
||||
WalletDropDownItemsSubscriber(
|
||||
stateHolder = stateHolder,
|
||||
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
SingleWalletExpressStatusesSubscriberV2(
|
||||
userWallet = userWallet,
|
||||
singleAccountStatusListSupplier = accountDependencies.singleAccountStatusListSupplier,
|
||||
getOnrampTransactionsUseCase = getOnrampTransactionsUseCase,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
onrampRemoveTransactionUseCase = onrampRemoveTransactionUseCase,
|
||||
stateController = stateHolder,
|
||||
clickIntents = clickIntents,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
),
|
||||
TxHistorySubscriberV2(
|
||||
userWallet = userWallet,
|
||||
singleAccountStatusListSupplier = accountDependencies.singleAccountStatusListSupplier,
|
||||
txHistoryItemsCountUseCase = txHistoryItemsCountUseCase,
|
||||
txHistoryItemsUseCase = txHistoryItemsUseCase,
|
||||
isRefresh = isRefresh,
|
||||
stateController = stateHolder,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
CheckWalletWithFundsSubscriber(
|
||||
userWallet = userWallet,
|
||||
singleAccountStatusListSupplier = accountDependencies.singleAccountStatusListSupplier,
|
||||
walletWithFundsChecker = walletWithFundsChecker,
|
||||
dispatchers = dispatchers,
|
||||
),
|
||||
)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet.Cold, isRefresh: Boolean): SingleWalletContentLoaderV2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
||||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
|
|
@ -15,8 +15,8 @@ import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenList
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
|
||||
@Deprecated("Use SingleWalletWithTokenContentLoaderV2 instead")
|
||||
@Suppress("LongParameterList")
|
||||
internal class SingleWalletWithTokenContentLoader(
|
||||
private val userWallet: UserWallet.Cold,
|
||||
|
|
@ -31,7 +31,6 @@ internal class SingleWalletWithTokenContentLoader(
|
|||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
private val stakingApyFlowUseCase: StakingApyFlowUseCase,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
|
@ -46,7 +45,6 @@ internal class SingleWalletWithTokenContentLoader(
|
|||
walletWithFundsChecker = walletWithFundsChecker,
|
||||
tokenListStore = tokenListStore,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
accountDependencies = accountDependencies,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
stakingApyFlowUseCase = stakingApyFlowUseCase,
|
||||
).let(::add)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
|||
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
|
|
@ -15,11 +15,11 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO: Refactor
|
||||
@Suppress("LongParameterList")
|
||||
@Deprecated("Use SingleWalletWithTokenContentLoaderV2.Factory instead")
|
||||
@ModelScoped
|
||||
internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
|
||||
private val stateHolder: WalletStateController,
|
||||
|
|
@ -32,7 +32,6 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
|
|||
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
private val stakingApyFlowUseCase: StakingApyFlowUseCase,
|
||||
) {
|
||||
|
|
@ -51,7 +50,6 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
|
|||
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
|
||||
getStoryContentUseCase = getStoryContentUseCase,
|
||||
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
|
||||
accountDependencies = accountDependencies,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
stakingApyFlowUseCase = stakingApyFlowUseCase,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class SingleWalletWithTokenContentLoaderV2 @AssistedInject constructor(
|
||||
@Assisted private val userWallet: UserWallet.Cold,
|
||||
private val singleWalletWithTokenSubscriberFactory: SingleWalletWithTokenSubscriber.Factory,
|
||||
private val checkWalletWithFundsSubscriberFactory: CheckWalletWithFundsSubscriber.Factory,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val stateController: WalletStateController,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
|
||||
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> = listOf(
|
||||
singleWalletWithTokenSubscriberFactory.create(userWallet),
|
||||
MultiWalletWarningsSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateHolder = stateController,
|
||||
clickIntents = clickIntents,
|
||||
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
|
||||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
|
||||
),
|
||||
WalletDropDownItemsSubscriber(
|
||||
stateHolder = stateController,
|
||||
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
checkWalletWithFundsSubscriberFactory.create(userWallet),
|
||||
)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet.Cold): SingleWalletWithTokenContentLoaderV2
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat
|
|||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
|
||||
import com.tangem.domain.pay.model.CustomerInfo.ProductInstance
|
||||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderStatus.CANCELED
|
||||
import com.tangem.domain.pay.model.OrderStatus.UNKNOWN
|
||||
|
|
@ -35,29 +36,36 @@ internal class TangemPayInitialStateTransformer(
|
|||
|
||||
private fun createInitialState(): TangemPayState {
|
||||
val cardInfo = value?.info?.cardInfo
|
||||
val productInstance = value?.info?.productInstance
|
||||
return when {
|
||||
value == null -> TangemPayState.Empty
|
||||
!value.info.isKycApproved -> createKycInProgressState(onClickKyc)
|
||||
cardInfo != null -> getCardInfoState(cardInfo)
|
||||
cardInfo != null && productInstance != null -> getCardInfoState(cardInfo, productInstance)
|
||||
value.orderStatus == UNKNOWN || value.orderStatus == CANCELED -> createIssueAvailableState(onClickIssue)
|
||||
else -> createIssueProgressState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCardInfoState(cardInfo: CardInfo): TangemPayState = TangemPayState.Card(
|
||||
lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"),
|
||||
balanceText = TextReference.Str(getBalanceText(cardInfo)),
|
||||
onClick = {
|
||||
openDetails(
|
||||
TangemPayDetailsConfig(
|
||||
customerWalletAddress = cardInfo.customerWalletAddress,
|
||||
cardNumberEnd = cardInfo.lastFourDigits,
|
||||
chainId = POLYGON_CHAIN_ID,
|
||||
depositAddress = cardInfo.depositAddress,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
private fun getCardInfoState(cardInfo: CardInfo, productInstance: ProductInstance): TangemPayState =
|
||||
TangemPayState.Card(
|
||||
lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"),
|
||||
balanceText = TextReference.Str(getBalanceText(cardInfo)),
|
||||
onClick = {
|
||||
openDetails(
|
||||
TangemPayDetailsConfig(
|
||||
cardId = productInstance.cardId,
|
||||
isCardFrozen = when (productInstance.status) {
|
||||
ProductInstance.Status.ACTIVE -> false
|
||||
ProductInstance.Status.INACTIVE -> true
|
||||
},
|
||||
customerWalletAddress = cardInfo.customerWalletAddress,
|
||||
cardNumberEnd = cardInfo.lastFourDigits,
|
||||
chainId = POLYGON_CHAIN_ID,
|
||||
depositAddress = cardInfo.depositAddress,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
private fun getBalanceText(cardInfo: CardInfo): String {
|
||||
val currency = Currency.getInstance(cardInfo.currencyCode)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
|
|||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
|
|
@ -21,7 +22,6 @@ import com.tangem.domain.staking.model.stakekit.Yield
|
|||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.OrganizeTokensButtonConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TokenConverterParams
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
|
|
@ -87,8 +87,6 @@ internal class TokenListStateConverter(
|
|||
|
||||
private fun convertAccountList(params: TokenConverterParams.Account): WalletTokensListState {
|
||||
val accountList = params.accountList
|
||||
// todo account null for firs iteration?
|
||||
val organizeTokensButtonConfig: OrganizeTokensButtonConfig? = null
|
||||
|
||||
fun AccountStatus.CryptoPortfolio.map(): TokensListItemUM.Portfolio {
|
||||
val tokenList: TokenList = this.tokenList
|
||||
|
|
@ -105,6 +103,7 @@ internal class TokenListStateConverter(
|
|||
appCurrency = appCurrency,
|
||||
account = account,
|
||||
onItemClick = onItemClick,
|
||||
priceChangeLce = this.priceChangeLce,
|
||||
)
|
||||
val accountItem = converter.convert(tokenList.totalFiatBalance)
|
||||
val tokenConverter = tokenStatusConverter(account.accountId)
|
||||
|
|
@ -132,7 +131,7 @@ internal class TokenListStateConverter(
|
|||
}
|
||||
return WalletTokensListState.ContentState.PortfolioContent(
|
||||
items = accountItems.toPersistentList(),
|
||||
organizeTokensButtonConfig = organizeTokensButtonConfig,
|
||||
organizeTokensButtonConfig = getOrganizeTokensButtonStateV2(accountList = accountList),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -197,6 +196,17 @@ internal class TokenListStateConverter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getOrganizeTokensButtonStateV2(accountList: AccountStatusList): WalletOrganizeTokensButtonConfig? {
|
||||
return if (accountList.flattenCurrencies().size > 1 && !isSingleCurrencyWalletWithToken()) {
|
||||
WalletOrganizeTokensButtonConfig(
|
||||
isEnabled = accountList.totalFiatBalance !is TotalFiatBalance.Loading,
|
||||
onClick = clickIntents::onOrganizeTokensClick,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSingleCurrencyWalletWithToken(): Boolean {
|
||||
return selectedWallet is UserWallet.Cold &&
|
||||
selectedWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.utils.coroutines.combine6
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
||||
/**
|
||||
* Subscriber that monitors account list related data and updates the wallet state accordingly.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
internal class AccountListSubscriber @AssistedInject constructor(
|
||||
@Assisted override val userWallet: UserWallet,
|
||||
override val accountDependencies: AccountDependencies,
|
||||
override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
override val stateController: WalletStateController,
|
||||
override val clickIntents: WalletClickIntents,
|
||||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
private val stakingApyFlowUseCase: StakingApyFlowUseCase,
|
||||
) : BasicAccountListSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> = combine6(
|
||||
flow1 = getAccountStatusListFlow(),
|
||||
flow2 = getAppCurrencyFlow(),
|
||||
flow3 = accountDependencies.expandedAccountsHolder.expandedAccounts(userWallet),
|
||||
flow4 = accountDependencies.isAccountsModeEnabledUseCase(),
|
||||
flow5 = yieldSupplyApyFlow(),
|
||||
flow6 = stakingApyFlow(),
|
||||
transform = ::updateState,
|
||||
)
|
||||
|
||||
private fun yieldSupplyApyFlow(): Flow<Map<String, String>> {
|
||||
return yieldSupplyApyFlowUseCase().distinctUntilChanged()
|
||||
}
|
||||
|
||||
private fun stakingApyFlow(): Flow<Map<String, List<Yield.Validator>>> {
|
||||
return stakingApyFlowUseCase().distinctUntilChanged()
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet): AccountListSubscriber
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
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.utils.getOrElse
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListErrorTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TokenConverterParams
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Basic implementation of [WalletSubscriber] for wallet with accounts.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
||||
|
||||
abstract val accountDependencies: AccountDependencies
|
||||
abstract val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase
|
||||
abstract val stateController: WalletStateController
|
||||
abstract val clickIntents: WalletClickIntents
|
||||
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier
|
||||
get() = accountDependencies.singleAccountStatusListSupplier
|
||||
|
||||
protected fun getAppCurrencyFlow(): Flow<AppCurrency> {
|
||||
return getSelectedAppCurrencyUseCase.invokeOrDefault()
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
protected fun updateState(
|
||||
accountList: AccountStatusList,
|
||||
appCurrency: AppCurrency,
|
||||
expandedAccounts: Set<AccountId>,
|
||||
isAccountMode: Boolean,
|
||||
yieldSupplyApyMap: Map<String, String> = emptyMap(),
|
||||
stakingApyMap: Map<String, List<Yield.Validator>> = emptyMap(),
|
||||
) {
|
||||
val accountFlattenCurrencies = accountList.flattenCurrencies()
|
||||
val mainAccount = accountList.mainAccount
|
||||
|
||||
when {
|
||||
!isAccountMode -> {
|
||||
val isMainAccountEmpty = mainAccount.tokenList.flattenCurrencies().isEmpty()
|
||||
val maybeTokenList = if (isMainAccountEmpty) {
|
||||
Lce.Error(TokenListError.EmptyTokens)
|
||||
} else {
|
||||
Lce.Content(mainAccount.tokenList)
|
||||
}
|
||||
|
||||
singleAccountTransform(
|
||||
maybeTokenList = maybeTokenList,
|
||||
appCurrency = appCurrency,
|
||||
portfolioId = PortfolioId(mainAccount.accountId),
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingApyMap = stakingApyMap,
|
||||
)
|
||||
}
|
||||
isAccountMode -> {
|
||||
val isAllAccountsEmpty = accountFlattenCurrencies.isEmpty()
|
||||
if (isAllAccountsEmpty) {
|
||||
stateController.update(
|
||||
SetTokenListErrorTransformer(
|
||||
selectedWallet = userWallet,
|
||||
error = TokenListError.EmptyTokens,
|
||||
appCurrency = appCurrency,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
val convertParams = TokenConverterParams.Account(accountList, expandedAccounts)
|
||||
|
||||
updateContent(convertParams, appCurrency, yieldSupplyApyMap, stakingApyMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun singleAccountTransform(
|
||||
maybeTokenList: Lce<TokenListError, TokenList>,
|
||||
appCurrency: AppCurrency,
|
||||
portfolioId: PortfolioId,
|
||||
yieldSupplyApyMap: Map<String, String> = emptyMap(),
|
||||
stakingApyMap: Map<String, List<Yield.Validator>> = emptyMap(),
|
||||
) {
|
||||
val tokenList = maybeTokenList.getOrElse(
|
||||
ifLoading = { maybeContent ->
|
||||
val isRefreshing = stateController.getWalletState(userWallet.walletId)
|
||||
?.pullToRefreshConfig
|
||||
?.isRefreshing == true
|
||||
|
||||
maybeContent
|
||||
?.takeIf { !isRefreshing }
|
||||
?: return
|
||||
},
|
||||
ifError = { e ->
|
||||
Timber.e("Failed to load token list: $e")
|
||||
stateController.update(
|
||||
SetTokenListErrorTransformer(
|
||||
selectedWallet = userWallet,
|
||||
error = e,
|
||||
appCurrency = appCurrency,
|
||||
),
|
||||
)
|
||||
return
|
||||
},
|
||||
)
|
||||
|
||||
updateContent(
|
||||
params = TokenConverterParams.Wallet(portfolioId, tokenList),
|
||||
appCurrency = appCurrency,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingApyMap = stakingApyMap,
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateContent(
|
||||
params: TokenConverterParams,
|
||||
appCurrency: AppCurrency,
|
||||
yieldSupplyApyMap: Map<String, String> = emptyMap(),
|
||||
stakingApyMap: Map<String, List<Yield.Validator>> = emptyMap(),
|
||||
) {
|
||||
stateController.update(
|
||||
SetTokenListTransformer(
|
||||
params = params,
|
||||
userWallet = userWallet,
|
||||
appCurrency = appCurrency,
|
||||
clickIntents = clickIntents,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingApyMap = stakingApyMap,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
|
||||
/**
|
||||
* Basic implementation of [WalletSubscriber] for single wallet.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal abstract class BasicSingleWalletSubscriber : BasicWalletSubscriber() {
|
||||
|
||||
/** Account ID for the main crypto portfolio of the user wallet */
|
||||
val accountId: AccountId
|
||||
get() = AccountId.forMainCryptoPortfolio(userWalletId = userWallet.walletId)
|
||||
|
||||
/**
|
||||
* Provides a flow of the primary [CryptoCurrencyStatus] for the associated user wallet.
|
||||
*
|
||||
* @return A [Flow] emitting the primary [CryptoCurrencyStatus] of the wallet, distinct and conflated.
|
||||
*/
|
||||
protected fun getPrimaryCurrencyStatusFlow(): Flow<CryptoCurrencyStatus> {
|
||||
return getMainAccountStatusFlow()
|
||||
.mapNotNull { accountStatus ->
|
||||
accountStatus.flattenCurrencies().firstOrNull()
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.conflate()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
|
|
@ -9,7 +8,6 @@ import com.tangem.domain.core.lce.LceFlow
|
|||
import com.tangem.domain.core.utils.getOrElse
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -18,7 +16,6 @@ import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
|
|||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
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
|
||||
|
|
@ -26,43 +23,33 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetToken
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TokenConverterParams
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.combine6
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.joinAll
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
@Deprecated("Use AccountListSubscriber instead")
|
||||
@Suppress("LongParameterList")
|
||||
internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
||||
protected abstract val userWallet: UserWallet
|
||||
protected abstract val stateHolder: WalletStateController
|
||||
protected abstract val clickIntents: WalletClickIntents
|
||||
protected abstract val tokenListAnalyticsSender: TokenListAnalyticsSender
|
||||
protected abstract val walletWithFundsChecker: WalletWithFundsChecker
|
||||
protected abstract val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase
|
||||
protected abstract val accountDependencies: AccountDependencies
|
||||
protected abstract val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase
|
||||
protected abstract val stakingApyFlowUseCase: StakingApyFlowUseCase
|
||||
internal abstract class BasicTokenListSubscriber(
|
||||
private val userWallet: UserWallet,
|
||||
private val stateHolder: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
private val walletWithFundsChecker: WalletWithFundsChecker,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
private val stakingApyFlowUseCase: StakingApyFlowUseCase,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
private val sendAnalyticsJobHolder = JobHolder()
|
||||
private val onTokenListReceivedJobHolder = JobHolder()
|
||||
protected val isAccountsEnabled get() = accountDependencies.accountsFeatureToggles.isFeatureEnabled
|
||||
|
||||
protected abstract fun tokenListFlow(coroutineScope: CoroutineScope): LceFlow<TokenListError, TokenList>
|
||||
|
||||
protected abstract fun accountListFlow(coroutineScope: CoroutineScope): Flow<AccountStatusList>
|
||||
|
||||
protected abstract suspend fun onTokenListReceived(maybeTokenList: Lce<TokenListError, TokenList>)
|
||||
protected open suspend fun onAccountListReceived() = {
|
||||
// todo account updateSortingIfNeeded? like [onTokenListReceived]
|
||||
}
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> =
|
||||
if (isAccountsEnabled) createAccountListFlow(coroutineScope) else createTokenListFlow(coroutineScope)
|
||||
|
||||
private fun createTokenListFlow(coroutineScope: CoroutineScope): Flow<*> {
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
return combine(
|
||||
flow = tokenListFlow(coroutineScope)
|
||||
.onEach { maybeTokenList ->
|
||||
|
|
@ -83,122 +70,41 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
flow3 = yieldSupplyApyFlow(),
|
||||
flow4 = stakingApyFlow(),
|
||||
transform = { maybeTokenList, appCurrency, yieldSupplyApyMap, stakingApyMap ->
|
||||
singleAccountTransform(
|
||||
maybeTokenList = maybeTokenList,
|
||||
val tokenList = maybeTokenList.getOrElse(
|
||||
ifLoading = { maybeContent ->
|
||||
val isRefreshing = stateHolder.getWalletState(userWallet.walletId)
|
||||
?.pullToRefreshConfig
|
||||
?.isRefreshing == true
|
||||
|
||||
maybeContent
|
||||
?.takeIf { !isRefreshing }
|
||||
?: return@combine
|
||||
},
|
||||
ifError = { e ->
|
||||
Timber.e("Failed to load token list: $e")
|
||||
stateHolder.update(
|
||||
SetTokenListErrorTransformer(
|
||||
selectedWallet = userWallet,
|
||||
error = e,
|
||||
appCurrency = appCurrency,
|
||||
),
|
||||
)
|
||||
return@combine
|
||||
},
|
||||
)
|
||||
|
||||
updateContent(
|
||||
params = TokenConverterParams.Wallet(PortfolioId(userWallet.walletId), tokenList),
|
||||
appCurrency = appCurrency,
|
||||
portfolioId = PortfolioId(userWallet.walletId),
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingApyMap = stakingApyMap,
|
||||
)
|
||||
|
||||
walletWithFundsChecker.check(tokenList)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun singleAccountTransform(
|
||||
maybeTokenList: Lce<TokenListError, TokenList>,
|
||||
appCurrency: AppCurrency,
|
||||
portfolioId: PortfolioId,
|
||||
yieldSupplyApyMap: Map<String, String>,
|
||||
stakingApyMap: Map<String, List<Yield.Validator>>,
|
||||
) {
|
||||
val tokenList = maybeTokenList.getOrElse(
|
||||
ifLoading = { maybeContent ->
|
||||
val isRefreshing = stateHolder.getWalletState(userWallet.walletId)
|
||||
?.pullToRefreshConfig
|
||||
?.isRefreshing == true
|
||||
|
||||
maybeContent
|
||||
?.takeIf { !isRefreshing }
|
||||
?: return
|
||||
},
|
||||
ifError = { e ->
|
||||
Timber.e("Failed to load token list: $e")
|
||||
stateHolder.update(
|
||||
SetTokenListErrorTransformer(
|
||||
selectedWallet = userWallet,
|
||||
error = e,
|
||||
appCurrency = appCurrency,
|
||||
),
|
||||
)
|
||||
return
|
||||
},
|
||||
)
|
||||
updateContent(
|
||||
params = TokenConverterParams.Wallet(portfolioId, tokenList),
|
||||
appCurrency = appCurrency,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingApyMap = stakingApyMap,
|
||||
)
|
||||
|
||||
walletWithFundsChecker.check(tokenList)
|
||||
}
|
||||
|
||||
private fun createAccountListFlow(coroutineScope: CoroutineScope): Flow<*> = combine6(
|
||||
flow1 = accountListFlow(coroutineScope)
|
||||
.onEach { accountStatusList ->
|
||||
coroutineScope.launch {
|
||||
sendTokenListAnalytics(
|
||||
flattenCurrencies = accountStatusList.flattenCurrencies(),
|
||||
totalFiatBalance = accountStatusList.totalFiatBalance,
|
||||
)
|
||||
}.saveIn(sendAnalyticsJobHolder)
|
||||
}
|
||||
.distinctUntilChanged(),
|
||||
flow2 = appCurrencyFlow(),
|
||||
flow3 = accountDependencies.expandedAccountsHolder.expandedAccounts(userWallet),
|
||||
flow4 = accountDependencies.isAccountsModeEnabledUseCase(),
|
||||
flow5 = yieldSupplyApyFlow(),
|
||||
flow6 = stakingApyFlow(),
|
||||
transform = { accountList, appCurrency, expandedAccounts, isAccountMode, yieldSupplyApyMap, stakingApyMap ->
|
||||
val accountFlattenTokensList = accountList.flattenTokens()
|
||||
val accountFlattenCurrencies = accountFlattenTokensList
|
||||
.map { it.flattenCurrencies() }
|
||||
.flatten()
|
||||
val mainAccount: AccountStatus.CryptoPortfolio = when (val mainAccount = accountList.mainAccount) {
|
||||
is AccountStatus.CryptoPortfolio -> mainAccount
|
||||
}
|
||||
|
||||
suspend fun singleAccountTransform(maybeTokenList: Lce<TokenListError, TokenList>) =
|
||||
this.singleAccountTransform(
|
||||
maybeTokenList,
|
||||
appCurrency,
|
||||
PortfolioId(mainAccount.account.accountId),
|
||||
yieldSupplyApyMap,
|
||||
stakingApyMap,
|
||||
)
|
||||
|
||||
when {
|
||||
!isAccountMode -> when (mainAccount.tokenList.flattenCurrencies().isEmpty()) {
|
||||
true -> singleAccountTransform(Lce.Error(TokenListError.EmptyTokens))
|
||||
false -> singleAccountTransform(Lce.Content(mainAccount.tokenList))
|
||||
}
|
||||
|
||||
isAccountMode -> when (accountFlattenCurrencies.isEmpty()) {
|
||||
true -> stateHolder.update(
|
||||
SetTokenListErrorTransformer(
|
||||
selectedWallet = userWallet,
|
||||
error = TokenListError.EmptyTokens,
|
||||
appCurrency = appCurrency,
|
||||
),
|
||||
)
|
||||
false -> {
|
||||
val convertParams = TokenConverterParams.Account(accountList, expandedAccounts)
|
||||
updateContent(convertParams, appCurrency, yieldSupplyApyMap, stakingApyMap)
|
||||
accountFlattenTokensList
|
||||
.map { tokenList -> coroutineScope.launch { walletWithFundsChecker.check(tokenList) } }
|
||||
.joinAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
private fun AccountStatusList.flattenTokens(): List<TokenList> = this.accountStatuses.map {
|
||||
when (it) {
|
||||
is AccountStatus.CryptoPortfolio -> it.tokenList
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun sendTokenListAnalytics(
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>?,
|
||||
totalFiatBalance: TotalFiatBalance?,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
|
||||
/**
|
||||
* Basic implementation of [WalletSubscriber] for wallet.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal abstract class BasicWalletSubscriber : WalletSubscriber() {
|
||||
|
||||
/** User wallet associated with this subscriber */
|
||||
abstract val userWallet: UserWallet
|
||||
|
||||
/** Supplier to get the account status for the wallet */
|
||||
abstract val singleAccountStatusListSupplier: SingleAccountStatusListSupplier
|
||||
|
||||
/**
|
||||
* Provides a flow of [AccountStatusList] for the associated user wallet.
|
||||
*
|
||||
* @return A [Flow] emitting the [AccountStatusList] of the wallet, distinct and conflated.
|
||||
*/
|
||||
protected fun getAccountStatusListFlow(): Flow<AccountStatusList> {
|
||||
return singleAccountStatusListSupplier(
|
||||
params = SingleAccountStatusListProducer.Params(userWallet.walletId),
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.conflate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a flow of [AccountStatus] for the associated user wallet.
|
||||
*
|
||||
* @return A [Flow] emitting the [AccountStatus] of the wallet, distinct and conflated.
|
||||
*/
|
||||
protected fun getMainAccountStatusFlow(): Flow<AccountStatus.CryptoPortfolio> {
|
||||
return getAccountStatusListFlow()
|
||||
.mapNotNull { accountStatusList ->
|
||||
accountStatusList.accountStatuses.find { accountStatus ->
|
||||
when (accountStatus) {
|
||||
is AccountStatus.CryptoPortfolio -> accountStatus.account.isMainAccount
|
||||
}
|
||||
} as? AccountStatus.CryptoPortfolio
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.conflate()
|
||||
}
|
||||
|
||||
protected fun getCryptoCurrencyStatusesFlow(): Flow<List<CryptoCurrencyStatus>> {
|
||||
return getAccountStatusListFlow()
|
||||
.mapNotNull(AccountStatusList::flattenCurrencies)
|
||||
.distinctUntilChanged()
|
||||
.conflate()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
/**
|
||||
* Subscriber that checks if the wallet has funds and notifies the [WalletWithFundsChecker].
|
||||
*
|
||||
* @property userWallet The user wallet.
|
||||
* @property singleAccountStatusListSupplier Supplier for account status list.
|
||||
* @property walletWithFundsChecker The checker to notify when funds are detected.
|
||||
* @property dispatchers Coroutine dispatcher provider.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class CheckWalletWithFundsSubscriber @AssistedInject constructor(
|
||||
@Assisted override val userWallet: UserWallet,
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val walletWithFundsChecker: WalletWithFundsChecker,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : BasicWalletSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
return getAccountStatusListFlow()
|
||||
.mapNotNull { it.flattenCurrencies() }
|
||||
.distinctUntilChanged()
|
||||
.onEach { walletWithFundsChecker.check(userWalletId = userWallet.walletId, currencies = it) }
|
||||
.flowOn(dispatchers.default)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet): CheckWalletWithFundsSubscriber
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
|
|
@ -15,28 +13,35 @@ import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
|||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Deprecated("Use AccountListSubscriber instead")
|
||||
@Suppress("LongParameterList")
|
||||
internal class MultiWalletTokenListSubscriber(
|
||||
override val userWallet: UserWallet,
|
||||
private val userWallet: UserWallet,
|
||||
private val tokenListStore: MultiWalletTokenListStore,
|
||||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
override val stateHolder: WalletStateController,
|
||||
override val clickIntents: WalletClickIntents,
|
||||
override val tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
override val walletWithFundsChecker: WalletWithFundsChecker,
|
||||
override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
override val accountDependencies: AccountDependencies,
|
||||
override val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
override val stakingApyFlowUseCase: StakingApyFlowUseCase,
|
||||
) : BasicTokenListSubscriber() {
|
||||
stateHolder: WalletStateController,
|
||||
clickIntents: WalletClickIntents,
|
||||
tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
walletWithFundsChecker: WalletWithFundsChecker,
|
||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
stakingApyFlowUseCase: StakingApyFlowUseCase,
|
||||
) : BasicTokenListSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateHolder = stateHolder,
|
||||
clickIntents = clickIntents,
|
||||
tokenListAnalyticsSender = tokenListAnalyticsSender,
|
||||
walletWithFundsChecker = walletWithFundsChecker,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
stakingApyFlowUseCase = stakingApyFlowUseCase,
|
||||
) {
|
||||
|
||||
override fun tokenListFlow(coroutineScope: CoroutineScope): LceFlow<TokenListError, TokenList> {
|
||||
tokenListStore.addIfNot(userWallet.walletId, coroutineScope)
|
||||
|
|
@ -44,11 +49,6 @@ internal class MultiWalletTokenListSubscriber(
|
|||
return tokenListStore.getOrThrow(userWallet.walletId)
|
||||
}
|
||||
|
||||
override fun accountListFlow(coroutineScope: CoroutineScope): Flow<AccountStatusList> {
|
||||
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
|
||||
return accountDependencies.singleAccountStatusListSupplier(params)
|
||||
}
|
||||
|
||||
override suspend fun onTokenListReceived(maybeTokenList: Lce<TokenListError, TokenList>) {
|
||||
updateSortingIfNeeded(maybeTokenList)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import kotlinx.coroutines.flow.*
|
|||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Deprecated("Use PrimaryCurrencySubscriberV2 instead")
|
||||
internal class PrimaryCurrencySubscriber(
|
||||
private val userWallet: UserWallet,
|
||||
private val stateHolder: WalletStateController,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetPrimaryCurrencyTransformer
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class PrimaryCurrencySubscriberV2(
|
||||
override val userWallet: UserWallet,
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val stateController: WalletStateController,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : BasicSingleWalletSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
return combine(
|
||||
flow = getPrimaryCurrencyStatusFlow(),
|
||||
flow2 = getSelectedAppCurrencyUseCase.invokeOrDefault(),
|
||||
transform = ::Pair,
|
||||
)
|
||||
.onEach { (status, appCurrency) ->
|
||||
updateContent(status, appCurrency)
|
||||
sendAnalyticsEvent(status)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateContent(status: CryptoCurrencyStatus, appCurrency: AppCurrency) {
|
||||
stateController.update(
|
||||
SetPrimaryCurrencyTransformer(
|
||||
status = status,
|
||||
userWallet = userWallet,
|
||||
appCurrency = appCurrency,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendAnalyticsEvent(status: CryptoCurrencyStatus) {
|
||||
val fiatAmount = status.value.fiatAmount
|
||||
val cardBalanceState = when (status.value) {
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
-> createCardBalanceState(fiatAmount)
|
||||
is CryptoCurrencyStatus.NoQuote -> AnalyticsParam.CardBalanceState.NoRate
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
-> AnalyticsParam.CardBalanceState.BlockchainError
|
||||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.Loading,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
-> null
|
||||
}
|
||||
|
||||
cardBalanceState?.let {
|
||||
// do not send tokens count for single currency wallet
|
||||
analyticsEventHandler.send(
|
||||
event = WalletScreenAnalyticsEvent.Basic.BalanceLoaded(
|
||||
balance = it,
|
||||
tokensCount = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCardBalanceState(fiatAmount: BigDecimal?): AnalyticsParam.CardBalanceState? {
|
||||
return when {
|
||||
fiatAmount == null -> null
|
||||
fiatAmount.isZero() -> AnalyticsParam.CardBalanceState.Empty
|
||||
else -> AnalyticsParam.CardBalanceState.Full
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,20 +7,19 @@ import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
|||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.collectLatest
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetCryptoCurrencyActionsTransformer
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@Deprecated("Use SingleWalletButtonsSubscriberV2 instead")
|
||||
internal class SingleWalletButtonsSubscriber(
|
||||
private val userWallet: UserWallet,
|
||||
private val stateHolder: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
|
||||
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<TokenActionsState> {
|
||||
|
|
@ -31,11 +30,10 @@ internal class SingleWalletButtonsSubscriber(
|
|||
}
|
||||
}
|
||||
.onEach { actions ->
|
||||
val portfolioId = when (accountDependencies.accountsFeatureToggles.isFeatureEnabled) {
|
||||
true -> TODO("account") // get main account id for single wallet
|
||||
false -> PortfolioId(userWallet.walletId)
|
||||
}
|
||||
updateContent(actions, portfolioId)
|
||||
updateContent(
|
||||
tokenActionsState = actions,
|
||||
portfolioId = PortfolioId(userWallet.walletId),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetCryptoCurrencyActionsTransformer
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
internal class SingleWalletButtonsSubscriberV2(
|
||||
override val userWallet: UserWallet,
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val stateController: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val getCryptoCurrencyActionsUseCaseV2: GetCryptoCurrencyActionsUseCaseV2,
|
||||
) : BasicSingleWalletSubscriber() {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<TokenActionsState> {
|
||||
return getPrimaryCurrencyStatusFlow()
|
||||
.flatMapLatest {
|
||||
getCryptoCurrencyActionsUseCaseV2(accountId = accountId, currency = it.currency)
|
||||
}
|
||||
.onEach {
|
||||
updateContent(tokenActionsState = it, portfolioId = PortfolioId(userWallet.walletId))
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateContent(tokenActionsState: TokenActionsState, portfolioId: PortfolioId) {
|
||||
stateController.update(
|
||||
SetCryptoCurrencyActionsTransformer(
|
||||
tokenActionsState = tokenActionsState,
|
||||
userWallet = userWallet,
|
||||
clickIntents = clickIntents,
|
||||
portfolioId = portfolioId,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import kotlinx.coroutines.flow.*
|
|||
import timber.log.Timber
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Deprecated("Use SingleWalletExpressStatusesSubscriberV2 instead")
|
||||
internal class SingleWalletExpressStatusesSubscriber(
|
||||
private val userWallet: UserWallet,
|
||||
private val stateHolder: WalletStateController,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
|
||||
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
|
||||
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetExpressStatusesTransformer
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class SingleWalletExpressStatusesSubscriberV2(
|
||||
override val userWallet: UserWallet,
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
|
||||
private val stateController: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : BasicSingleWalletSubscriber() {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
val getOnrampTransactionsFlow = getPrimaryCurrencyStatusFlow().flatMapLatest { currencyStatus ->
|
||||
getOnrampTransactionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrencyId = currencyStatus.currency.id,
|
||||
)
|
||||
.map { currencyStatus to it }
|
||||
}
|
||||
|
||||
return combine(
|
||||
flow = getOnrampTransactionsFlow,
|
||||
flow2 = getSelectedAppCurrencyUseCase.invokeOrDefault(),
|
||||
transform = ::toTriple,
|
||||
)
|
||||
.onEach { (status, maybeTransaction, appCurrency) ->
|
||||
maybeTransaction.fold(
|
||||
ifRight = { onrampTxs ->
|
||||
onrampTxs.clearHiddenTerminal()
|
||||
stateController.update(
|
||||
SetExpressStatusesTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
onrampTxs = onrampTxs,
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatus = status,
|
||||
appCurrency = appCurrency,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
),
|
||||
)
|
||||
},
|
||||
ifLeft = {
|
||||
stateController.update(
|
||||
SetExpressStatusesTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
onrampTxs = listOf(),
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatus = status,
|
||||
appCurrency = appCurrency,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <A, B, C> toTriple(firstPair: Pair<A, B>, second: C): Triple<A, B, C> {
|
||||
return Triple(firstPair.first, firstPair.second, second)
|
||||
}
|
||||
|
||||
private suspend fun List<OnrampTransaction>.clearHiddenTerminal() {
|
||||
this
|
||||
.filter { it.status.isHidden && it.status.isTerminal }
|
||||
.forEach { onrampRemoveTransactionUseCase(txId = it.txId) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
|
|
@ -11,27 +9,34 @@ import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
|
|||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Deprecated("Use SingleWalletWithTokenSubscriber instead")
|
||||
@Suppress("LongParameterList")
|
||||
internal class SingleWalletWithTokenListSubscriber(
|
||||
override val userWallet: UserWallet.Cold,
|
||||
private val userWallet: UserWallet.Cold,
|
||||
private val tokenListStore: MultiWalletTokenListStore,
|
||||
override val stateHolder: WalletStateController,
|
||||
override val clickIntents: WalletClickIntents,
|
||||
override val tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
override val walletWithFundsChecker: WalletWithFundsChecker,
|
||||
override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
override val accountDependencies: AccountDependencies,
|
||||
override val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
override val stakingApyFlowUseCase: StakingApyFlowUseCase,
|
||||
) : BasicTokenListSubscriber() {
|
||||
stateHolder: WalletStateController,
|
||||
clickIntents: WalletClickIntents,
|
||||
tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
walletWithFundsChecker: WalletWithFundsChecker,
|
||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
stakingApyFlowUseCase: StakingApyFlowUseCase,
|
||||
) : BasicTokenListSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateHolder = stateHolder,
|
||||
clickIntents = clickIntents,
|
||||
tokenListAnalyticsSender = tokenListAnalyticsSender,
|
||||
walletWithFundsChecker = walletWithFundsChecker,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
stakingApyFlowUseCase = stakingApyFlowUseCase,
|
||||
) {
|
||||
|
||||
override fun tokenListFlow(coroutineScope: CoroutineScope): LceFlow<TokenListError, TokenList> {
|
||||
tokenListStore.addIfNot(userWallet.walletId, coroutineScope)
|
||||
|
|
@ -39,10 +44,5 @@ internal class SingleWalletWithTokenListSubscriber(
|
|||
return tokenListStore.getOrThrow(userWallet.walletId)
|
||||
}
|
||||
|
||||
override fun accountListFlow(coroutineScope: CoroutineScope): Flow<AccountStatusList> {
|
||||
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
|
||||
return accountDependencies.singleAccountStatusListSupplier(params)
|
||||
}
|
||||
|
||||
override suspend fun onTokenListReceived(maybeTokenList: Lce<TokenListError, TokenList>) = Unit
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
|
||||
internal class SingleWalletWithTokenSubscriber @AssistedInject constructor(
|
||||
@Assisted override val userWallet: UserWallet.Cold,
|
||||
override val accountDependencies: AccountDependencies,
|
||||
override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
override val stateController: WalletStateController,
|
||||
override val clickIntents: WalletClickIntents,
|
||||
) : BasicAccountListSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<Unit> = combine(
|
||||
flow = getAccountStatusListFlow(),
|
||||
flow2 = getAppCurrencyFlow(),
|
||||
flow3 = accountDependencies.expandedAccountsHolder.expandedAccounts(userWallet),
|
||||
flow4 = accountDependencies.isAccountsModeEnabledUseCase(),
|
||||
transform = ::updateState,
|
||||
)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet.Cold): SingleWalletWithTokenSubscriber
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ typealias MaybeTxHistoryCount = Either<TxHistoryStateError, Int>
|
|||
typealias MaybeTxHistoryItems = Either<TxHistoryListError, Flow<PagingData<TxInfo>>>
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Deprecated("Use TxHistorySubscriberV2 instead")
|
||||
internal class TxHistorySubscriber(
|
||||
private val userWallet: UserWallet.Cold,
|
||||
private val isRefresh: Boolean,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,111 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import androidx.paging.map
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryCountErrorTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryCountTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsErrorTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemStateConverter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class TxHistorySubscriberV2(
|
||||
override val userWallet: UserWallet.Cold,
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
private val isRefresh: Boolean,
|
||||
private val stateController: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : BasicSingleWalletSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<PagingData<TxInfo>> {
|
||||
return flow {
|
||||
getPrimaryCurrencyStatusFlow().collectLatest { status ->
|
||||
val maybeTxHistoryItemCount = txHistoryItemsCountUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = status.currency,
|
||||
)
|
||||
|
||||
setLoadingTxHistoryState(maybeTxHistoryItemCount, status)
|
||||
|
||||
maybeTxHistoryItemCount.onRight {
|
||||
val maybeTxHistoryItems = txHistoryItemsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = status.currency,
|
||||
refresh = isRefresh,
|
||||
).map { it.cachedIn(coroutineScope) }
|
||||
|
||||
setLoadedTxHistoryState(maybeTxHistoryItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLoadingTxHistoryState(maybeTxHistoryItemCount: MaybeTxHistoryCount, status: CryptoCurrencyStatus) {
|
||||
stateController.update(
|
||||
maybeTxHistoryItemCount.fold(
|
||||
ifLeft = {
|
||||
SetTxHistoryCountErrorTransformer(
|
||||
userWallet = userWallet,
|
||||
error = it,
|
||||
pendingTransactions = status.value.pendingTransactions,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
},
|
||||
ifRight = {
|
||||
SetTxHistoryCountTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
transactionsCount = it,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun setLoadedTxHistoryState(maybeTxHistoryItems: MaybeTxHistoryItems) {
|
||||
stateController.update(
|
||||
maybeTxHistoryItems.fold(
|
||||
ifLeft = {
|
||||
SetTxHistoryItemsErrorTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
error = it,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
},
|
||||
ifRight = { itemsFlow ->
|
||||
val blockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain()
|
||||
val itemConverter = TxHistoryItemStateConverter(
|
||||
symbol = blockchain.currency,
|
||||
decimals = blockchain.decimals(),
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
SetTxHistoryItemsTransformer(
|
||||
userWallet = userWallet,
|
||||
flow = itemsFlow.map { items ->
|
||||
items.map(itemConverter::convert)
|
||||
},
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,9 @@ import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWalletCardDropDownItemsTransformer
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
internal class WalletDropDownItemsSubscriber(
|
||||
private val stateHolder: WalletStateController,
|
||||
|
|
@ -13,18 +15,15 @@ internal class WalletDropDownItemsSubscriber(
|
|||
private val clickIntents: WalletClickIntents,
|
||||
) : WalletSubscriber() {
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
return flow<Any> {
|
||||
shouldSaveUserWalletsUseCase.invoke()
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
stateHolder.update(
|
||||
SetWalletCardDropDownItemsTransformer(
|
||||
dropdownEnabled = it,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
}
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
return shouldSaveUserWalletsUseCase.invoke()
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
stateHolder.update(
|
||||
SetWalletCardDropDownItemsTransformer(
|
||||
dropdownEnabled = it,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.nft.GetNFTCollectionsUseCase
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.RemoveNFTCollectionsTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetNFTCollectionsTransformer
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
internal class WalletNFTListSubscriberV2 @AssistedInject constructor(
|
||||
@Assisted override val userWallet: UserWallet,
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val walletsRepository: WalletsRepository,
|
||||
private val getNFTCollectionsUseCase: GetNFTCollectionsUseCase,
|
||||
private val stateController: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : BasicWalletSubscriber() {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> = combine(
|
||||
flow = walletsRepository.nftEnabledStatus(userWallet.walletId),
|
||||
flow2 = getCryptoCurrencyStatusesFlow(),
|
||||
transform = ::Pair,
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.flatMapLatest { (nftEnabled, currencies) ->
|
||||
// if NFT is enabled for this wallet and there are currencies,
|
||||
// then start observing changes from store and apply transformer if need
|
||||
if (nftEnabled && currencies.isNotEmpty()) {
|
||||
getNFTCollectionsUseCase(userWallet.walletId)
|
||||
.shareIn(
|
||||
scope = coroutineScope,
|
||||
started = SharingStarted.WhileSubscribed(),
|
||||
replay = 1,
|
||||
)
|
||||
.onEach {
|
||||
stateController.update(
|
||||
SetNFTCollectionsTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
nftCollections = it,
|
||||
onItemClick = { clickIntents.onNFTClick(userWallet) },
|
||||
),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// otherwise, hide NFT from wallet
|
||||
stateController.update(
|
||||
RemoveNFTCollectionsTransformer(userWallet.walletId),
|
||||
)
|
||||
emptyFlow()
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet): WalletNFTListSubscriberV2
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import androidx.compose.foundation.lazy.LazyListState
|
|||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
|
|
@ -55,6 +56,7 @@ import com.tangem.core.ui.components.rememberIsKeyboardVisible
|
|||
import com.tangem.core.ui.components.sheetscaffold.*
|
||||
import com.tangem.core.ui.components.snackbar.CopiedTextSnackbar
|
||||
import com.tangem.core.ui.components.snackbar.TangemSnackbar
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
|
|
@ -69,12 +71,14 @@ import com.tangem.core.ui.utils.moveTo
|
|||
import com.tangem.core.ui.utils.toPx
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenState
|
||||
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenWithEmptyTokensState
|
||||
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.walletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.fastForEach
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.nftCollections
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeTokensButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.marketPriceBlock
|
||||
|
|
@ -142,6 +146,7 @@ private fun WalletContent(
|
|||
*/
|
||||
val selectedWalletIndex by remember(state.selectedWalletIndex) { mutableIntStateOf(state.selectedWalletIndex) }
|
||||
val selectedWallet = state.wallets.getOrElse(selectedWalletIndex) { state.wallets[state.selectedWalletIndex] }
|
||||
val (expandedState, collapsedState) = getExpandPortfolioStates(selectedWallet)
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
|
|
@ -216,7 +221,7 @@ private fun WalletContent(
|
|||
item(
|
||||
key = "TangemPayMainScreenBlock",
|
||||
contentType = state.tangemPayState::class.java,
|
||||
) { TangemPayMainScreenBlock(state.tangemPayState, itemModifier) }
|
||||
) { TangemPayMainScreenBlock(state.tangemPayState, isBalanceHidden = state.isHidingMode, itemModifier) }
|
||||
|
||||
(selectedWallet as? WalletState.SingleCurrency)?.let { walletState ->
|
||||
walletState.marketPriceBlockState?.let { marketPriceBlockState ->
|
||||
|
|
@ -242,6 +247,13 @@ private fun WalletContent(
|
|||
txHistoryItems = txHistoryItems,
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
modifier = movableItemModifier,
|
||||
portfolioVisibleState = {
|
||||
findPortfolioVisibleState(
|
||||
portfolio = it,
|
||||
expandedState = expandedState,
|
||||
collapsedState = collapsedState,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
nftCollections(state = selectedWallet, itemModifier = itemModifier)
|
||||
|
|
@ -289,6 +301,61 @@ private fun WalletContent(
|
|||
)
|
||||
}
|
||||
|
||||
private fun findPortfolioVisibleState(
|
||||
portfolio: TokensListItemUM.Portfolio,
|
||||
expandedState: SnapshotStateMap<String, MutableTransitionState<Boolean>>,
|
||||
collapsedState: SnapshotStateMap<String, MutableTransitionState<Boolean>>,
|
||||
): MutableTransitionState<Boolean> {
|
||||
val portfolioKey = portfolio.id
|
||||
|
||||
fun forceVisible() = MutableTransitionState(true).apply { targetState = true }
|
||||
val shouldExpand = portfolio.isExpanded
|
||||
return if (shouldExpand) {
|
||||
collapsedState[portfolioKey] ?: forceVisible()
|
||||
} else {
|
||||
expandedState[portfolioKey] ?: forceVisible()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getExpandPortfolioStates(
|
||||
state: WalletState,
|
||||
): Pair<
|
||||
SnapshotStateMap<String, MutableTransitionState<Boolean>>,
|
||||
SnapshotStateMap<String, MutableTransitionState<Boolean>>,
|
||||
> {
|
||||
val expandedTransitionState =
|
||||
remember { mutableStateMapOf<String, MutableTransitionState<Boolean>>() }
|
||||
val collapsedTransitionState =
|
||||
remember { mutableStateMapOf<String, MutableTransitionState<Boolean>>() }
|
||||
|
||||
val portfolioContent = state is WalletState.MultiCurrency.Content &&
|
||||
state.tokensListState is WalletTokensListState.ContentState.PortfolioContent
|
||||
if (!portfolioContent) {
|
||||
return expandedTransitionState to collapsedTransitionState
|
||||
}
|
||||
|
||||
state.tokensListState.items.fastForEach { portfolio ->
|
||||
val portfolioKey = portfolio.id
|
||||
val shouldExpand = portfolio.isExpanded
|
||||
|
||||
fun toggleVisible() = MutableTransitionState(false).apply { targetState = true }
|
||||
fun forceVisible() = MutableTransitionState(true).apply { targetState = true }
|
||||
|
||||
val isFirstCall = collapsedTransitionState[portfolioKey] == null ||
|
||||
expandedTransitionState[portfolioKey] == null
|
||||
when {
|
||||
isFirstCall -> {
|
||||
collapsedTransitionState[portfolioKey] = forceVisible()
|
||||
expandedTransitionState[portfolioKey] = forceVisible()
|
||||
}
|
||||
shouldExpand -> expandedTransitionState[portfolioKey] = toggleVisible()
|
||||
else -> collapsedTransitionState[portfolioKey] = toggleVisible()
|
||||
}
|
||||
}
|
||||
return expandedTransitionState to collapsedTransitionState
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList", "LongMethod", "CyclomaticComplexMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -743,6 +810,7 @@ private class WalletScreenPreviewProvider : PreviewParameterProvider<WalletScree
|
|||
walletScreenState,
|
||||
walletScreenState.copy(selectedWalletIndex = 1),
|
||||
accountScreenState.copy(selectedWalletIndex = 1),
|
||||
accountScreenWithEmptyTokensState.copy(selectedWalletIndex = 1),
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
|
||||
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.core.ui.components.transactions.txHistoryItems
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
|
|
@ -22,10 +24,11 @@ internal fun LazyListScope.contentItems(
|
|||
txHistoryItems: LazyPagingItems<TxHistoryState.TxHistoryItemState>?,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
when (state) {
|
||||
is WalletState.MultiCurrency -> {
|
||||
tokensListItems(state.tokensListState, modifier, isBalanceHidden)
|
||||
tokensListItems(state.tokensListState, modifier, isBalanceHidden, portfolioVisibleState)
|
||||
}
|
||||
is WalletState.SingleCurrency -> {
|
||||
txHistoryItems(state.txHistoryState, txHistoryItems, isBalanceHidden, modifier)
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -27,6 +25,7 @@ internal fun LazyListScope.portfolioContentItems(
|
|||
items: ImmutableList<TokensListItemUM.Portfolio>,
|
||||
modifier: Modifier = Modifier,
|
||||
isBalanceHidden: Boolean,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
items.forEachIndexed { index, item ->
|
||||
portfolioTokensList(
|
||||
|
|
@ -34,6 +33,7 @@ internal fun LazyListScope.portfolioContentItems(
|
|||
modifier = modifier,
|
||||
portfolioIndex = index,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
portfolioVisibleState = portfolioVisibleState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -43,6 +43,7 @@ internal fun LazyListScope.portfolioTokensList(
|
|||
modifier: Modifier,
|
||||
portfolioIndex: Int,
|
||||
isBalanceHidden: Boolean,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
val tokens = portfolio.tokens
|
||||
val isExpanded = portfolio.isExpanded
|
||||
|
|
@ -52,8 +53,33 @@ internal fun LazyListScope.portfolioTokensList(
|
|||
modifier = modifier,
|
||||
portfolioIndex = portfolioIndex,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
portfolioVisibleState = portfolioVisibleState,
|
||||
)
|
||||
if (!isExpanded) return
|
||||
if (tokens.isEmpty()) {
|
||||
item(
|
||||
key = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}",
|
||||
contentType = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}",
|
||||
) {
|
||||
val appear = portfolioVisibleState(portfolio)
|
||||
SlideInItemVisibility(
|
||||
modifier = modifier
|
||||
.animateItem()
|
||||
.roundedShapeItemDecoration(
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
currentIndex = 1,
|
||||
lastIndex = 1,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
),
|
||||
visibleState = appear,
|
||||
) {
|
||||
NonContentItemContent(
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing28),
|
||||
)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
itemsIndexed(
|
||||
items = tokens,
|
||||
key = { _, item -> item.id },
|
||||
|
|
@ -61,15 +87,13 @@ internal fun LazyListScope.portfolioTokensList(
|
|||
itemContent = { tokenIndex, token ->
|
||||
val indexWithHeader = tokenIndex.inc()
|
||||
val lastIndex = tokens.lastIndex.inc()
|
||||
val isPreview = LocalInspectionMode.current
|
||||
val appear = remember {
|
||||
MutableTransitionState(isPreview).apply { targetState = true }
|
||||
}
|
||||
val appear = portfolioVisibleState(portfolio)
|
||||
SlideInItemVisibility(
|
||||
modifier = modifier
|
||||
.testModifier(indexWithHeader)
|
||||
.animateItem()
|
||||
.roundedShapeItemDecoration(
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
currentIndex = indexWithHeader,
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
|
|
@ -92,10 +116,17 @@ private fun LazyListScope.portfolioItem(
|
|||
modifier: Modifier,
|
||||
portfolioIndex: Int,
|
||||
isBalanceHidden: Boolean,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
val tokens = portfolio.tokens
|
||||
val isExpanded = portfolio.isExpanded
|
||||
|
||||
val lastIndex = when {
|
||||
isExpanded && tokens.isEmpty() -> 1
|
||||
isExpanded -> tokens.lastIndex.inc()
|
||||
else -> 0
|
||||
}
|
||||
|
||||
item(
|
||||
key = "account-${portfolio.id}-isExpanded$isExpanded",
|
||||
contentType = "account-isExpanded$isExpanded",
|
||||
|
|
@ -105,27 +136,20 @@ private fun LazyListScope.portfolioItem(
|
|||
.animateItem()
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = 0,
|
||||
lastIndex = if (isExpanded) tokens.lastIndex.inc() else 0,
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
)
|
||||
val isPreview = LocalInspectionMode.current
|
||||
val appear = remember {
|
||||
MutableTransitionState(isPreview).apply { targetState = true }
|
||||
}
|
||||
val appear = portfolioVisibleState(portfolio)
|
||||
if (isExpanded) {
|
||||
SlideInItemVisibility(
|
||||
modifier = anchorModifier,
|
||||
visibleState = appear,
|
||||
) {
|
||||
val modifier = if (portfolio.tokens.isEmpty()) {
|
||||
Modifier.padding(vertical = 8.dp)
|
||||
} else {
|
||||
Modifier.padding(top = 8.dp)
|
||||
}
|
||||
PortfolioListItem(
|
||||
state = portfolio,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = modifier,
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency
|
||||
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -8,6 +9,7 @@ import androidx.compose.foundation.lazy.LazyListScope
|
|||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
|
|
@ -25,7 +27,7 @@ import com.tangem.feature.wallet.impl.R
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
private const val NON_CONTENT_TOKENS_LIST_KEY = "NON_CONTENT_TOKENS_LIST"
|
||||
internal const val NON_CONTENT_TOKENS_LIST_KEY = "NON_CONTENT_TOKENS_LIST"
|
||||
|
||||
/**
|
||||
* LazyList extension for [WalletTokensListState]
|
||||
|
|
@ -39,12 +41,14 @@ internal fun LazyListScope.tokensListItems(
|
|||
state: WalletTokensListState,
|
||||
modifier: Modifier = Modifier,
|
||||
isBalanceHidden: Boolean,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
when (state) {
|
||||
is WalletTokensListState.ContentState.PortfolioContent -> portfolioContentItems(
|
||||
items = state.items,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = modifier,
|
||||
portfolioVisibleState = portfolioVisibleState,
|
||||
)
|
||||
is WalletTokensListState.ContentState.Content,
|
||||
is WalletTokensListState.ContentState.Loading,
|
||||
|
|
@ -89,27 +93,34 @@ private fun LazyListScope.nonContentItem(modifier: Modifier = Modifier) {
|
|||
key = NON_CONTENT_TOKENS_LIST_KEY,
|
||||
contentType = NON_CONTENT_TOKENS_LIST_KEY,
|
||||
) {
|
||||
Column(
|
||||
NonContentItemContent(
|
||||
modifier = modifier
|
||||
.animateItem(fadeInSpec = null, fadeOutSpec = null)
|
||||
.padding(top = TangemTheme.dimens.spacing96),
|
||||
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing16),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_empty_64),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(size = TangemTheme.dimens.size64),
|
||||
tint = TangemTheme.colors.icon.inactive,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.main_empty_tokens_list_message),
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing48),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
@Composable
|
||||
internal fun NonContentItemContent(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing16),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_empty_64),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(size = TangemTheme.dimens.size64),
|
||||
tint = TangemTheme.colors.icon.inactive,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.main_empty_tokens_list_message),
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing48),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,13 +13,18 @@ import androidx.compose.ui.res.painterResource
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayCardMainBlock(state: TangemPayState.Card, modifier: Modifier = Modifier) {
|
||||
internal fun TangemPayCardMainBlock(
|
||||
state: TangemPayState.Card,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
|
|
@ -72,7 +77,7 @@ internal fun TangemPayCardMainBlock(state: TangemPayState.Card, modifier: Modifi
|
|||
contentAlignment = Alignment.TopEnd,
|
||||
) {
|
||||
Text(
|
||||
text = state.balanceText.resolveReference(),
|
||||
text = state.balanceText.resolveReference().orMaskWithStars(isBalanceHidden),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.End,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.
|
|||
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.TangemPayCardMainBlock
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayMainScreenBlock(state: TangemPayState, modifier: Modifier = Modifier) {
|
||||
internal fun TangemPayMainScreenBlock(state: TangemPayState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is Progress -> {
|
||||
Notification(
|
||||
|
|
@ -38,7 +38,7 @@ internal fun TangemPayMainScreenBlock(state: TangemPayState, modifier: Modifier
|
|||
),
|
||||
)
|
||||
}
|
||||
is TangemPayState.Card -> TangemPayCardMainBlock(state, modifier)
|
||||
is TangemPayState.Card -> TangemPayCardMainBlock(state, isBalanceHidden, modifier)
|
||||
is TangemPayState.Empty -> Unit
|
||||
}
|
||||
}
|
||||
|
|
@ -56,6 +56,7 @@ private fun ResetCardScreenPreview() {
|
|||
iconRes = R.drawable.ic_promo_kyc_36,
|
||||
onButtonClick = {},
|
||||
),
|
||||
isBalanceHidden = false,
|
||||
)
|
||||
|
||||
TangemPayMainScreenBlock(
|
||||
|
|
@ -65,6 +66,7 @@ private fun ResetCardScreenPreview() {
|
|||
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
|
||||
onButtonClick = {},
|
||||
),
|
||||
isBalanceHidden = false,
|
||||
)
|
||||
|
||||
TangemPayMainScreenBlock(
|
||||
|
|
@ -75,6 +77,7 @@ private fun ResetCardScreenPreview() {
|
|||
onButtonClick = {},
|
||||
showProgress = true,
|
||||
),
|
||||
isBalanceHidden = false,
|
||||
)
|
||||
|
||||
TangemPayMainScreenBlock(
|
||||
|
|
@ -83,6 +86,7 @@ private fun ResetCardScreenPreview() {
|
|||
balanceText = TextReference.Str("$ 0.00"),
|
||||
onClick = {},
|
||||
),
|
||||
isBalanceHidden = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.feature.wallet.utils
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.common.ui.userwallet.converter.ArtworkUMConverter
|
||||
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
|
||||
import com.tangem.core.ui.components.artwork.ArtworkUM
|
||||
|
|
@ -47,12 +46,11 @@ class DefaultUserWalletImageFetcher @Inject constructor(
|
|||
|
||||
override fun walletImage(walletId: UserWalletId, size: ArtworkSize): Flow<UserWalletItemUM.ImageState> =
|
||||
getUserWalletUseCase.invokeFlow(walletId)
|
||||
.transform { either ->
|
||||
if (either.isLeft()) {
|
||||
emit(UserWalletItemUM.ImageState.Loading)
|
||||
} else if (either is Either.Right) {
|
||||
emitAll(walletImage(either.value, size))
|
||||
}
|
||||
.transform {
|
||||
it.fold(
|
||||
ifLeft = { emit(UserWalletItemUM.ImageState.Loading) },
|
||||
ifRight = { wallet -> emitAll(walletImage(wallet, size)) },
|
||||
)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import com.tangem.common.ui.userwallet.state.UserWalletItemUM
|
|||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.status.usecase.GetWalletTotalBalanceUseCaseV2
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.error.SelectedAppCurrencyError
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
|
|
@ -37,7 +39,9 @@ import kotlinx.coroutines.flow.*
|
|||
@Suppress("LongParameterList")
|
||||
internal class DefaultUserWalletsFetcher @AssistedInject constructor(
|
||||
getWalletsUseCase: GetWalletsUseCase,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val getWalletTotalBalanceUseCase: GetWalletTotalBalanceUseCase,
|
||||
private val getWalletTotalBalanceUseCaseV2: GetWalletTotalBalanceUseCaseV2,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
@Assisted private val onWalletClick: (UserWalletId) -> Unit,
|
||||
|
|
@ -97,7 +101,11 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
|
|||
// We should not load balances in auth mode
|
||||
flowOf(Lce.Loading(walletIds.associateWith { TotalFiatBalance.Loading }))
|
||||
} else {
|
||||
getWalletTotalBalanceUseCase(walletIds).distinctUntilChanged()
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
getWalletTotalBalanceUseCaseV2(userWalletIds = walletIds)
|
||||
} else {
|
||||
getWalletTotalBalanceUseCase(walletIds).distinctUntilChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue