Updated on 2026-08-14
This commit is contained in:
commit
45156f05b0
1184 changed files with 47887 additions and 11982 deletions
|
|
@ -23,6 +23,7 @@ import com.tangem.feature.wallet.child.wallet.model.WalletModel
|
|||
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejectedComponent
|
||||
import com.tangem.feature.walletsettings.component.RenameWalletComponent
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.feed.entry.components.FeedEntryComponent
|
||||
|
|
@ -54,7 +55,10 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
private val model: WalletModel = getOrCreateModel()
|
||||
|
||||
private val feedEntryComponent by lazy {
|
||||
feedEntryComponentFactory.create(child("feedEntryComponent"))
|
||||
feedEntryComponentFactory.create(
|
||||
context = child("feedEntryComponent"),
|
||||
entryRoute = null,
|
||||
)
|
||||
}
|
||||
private val marketsEntryComponent by lazy {
|
||||
marketsEntryComponentFactory.create(child("marketsEntryComponent"))
|
||||
|
|
@ -123,6 +127,17 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
),
|
||||
)
|
||||
}
|
||||
is WalletDialogConfig.KycRejected -> {
|
||||
KycRejectedComponent(
|
||||
appComponentContext = childByContext(componentContext),
|
||||
params = KycRejectedComponent.Params(
|
||||
walletId = dialogConfig.walletId,
|
||||
customerId = dialogConfig.customerId,
|
||||
callbacks = model.tangemPayKycRejectedCallbacks,
|
||||
onDismiss = model.innerWalletRouter.dialogNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import com.tangem.domain.apptheme.model.AppThemeMode
|
|||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.wallet.*
|
||||
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
|
||||
|
|
@ -40,6 +39,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.Dem
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejectedCallbacks
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.feed.entry.featuretoggle.FeedFeatureToggle
|
||||
|
|
@ -84,7 +84,6 @@ internal class WalletModel @Inject constructor(
|
|||
private val onrampStatusFactory: OnrampStatusFactory,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val walletContentFetcher: WalletContentFetcher,
|
||||
private val observeAndClearNFTCacheIfNeedUseCase: ObserveAndClearNFTCacheIfNeedUseCase,
|
||||
private val walletDeepLinkActionListener: WalletDeepLinkActionListener,
|
||||
private val notificationsRepository: NotificationsRepository,
|
||||
private val getWalletsListForEnablingUseCase: GetWalletsForAutomaticallyPushEnablingUseCase,
|
||||
|
|
@ -110,12 +109,12 @@ internal class WalletModel @Inject constructor(
|
|||
) : Model() {
|
||||
|
||||
val askBiometryModelCallbacks = AskBiometryModelCallbacks()
|
||||
val tangemPayKycRejectedCallbacks = TangemPayKycRejectedCallbacks()
|
||||
val askForPushNotificationsModelCallbacks = AskForPushNotificationsCallbacks()
|
||||
val uiState: StateFlow<WalletScreenState> = stateHolder.uiState
|
||||
|
||||
private val walletsUpdateJobHolder = JobHolder()
|
||||
private val refreshWalletJobHolder = JobHolder()
|
||||
private val clearNFTCacheJobHolder = JobHolder()
|
||||
private val updateTangemPayJobHolder = JobHolder()
|
||||
|
||||
private var needToRefreshWallet = false
|
||||
|
|
@ -340,7 +339,6 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
|
||||
subscribeOnExpressTransactionsUpdates(selectedWallet)
|
||||
observeAndClearNFTCacheIfNeedUseCase(selectedWallet)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
|
|
@ -396,13 +394,6 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun observeAndClearNFTCacheIfNeedUseCase(selectedWallet: UserWallet) {
|
||||
observeAndClearNFTCacheIfNeedUseCase
|
||||
.invoke(selectedWallet.walletId)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(clearNFTCacheJobHolder)
|
||||
}
|
||||
|
||||
private fun subscribeTangemPayOnWalletState() {
|
||||
/**
|
||||
* Update state each time a user opens/returns to wallet screen
|
||||
|
|
@ -786,6 +777,20 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
inner class TangemPayKycRejectedCallbacks : KycRejectedCallbacks {
|
||||
override fun onClickYourProfile(userWalletId: UserWalletId) {
|
||||
clickIntents.onKycRejectedOpenProfileClicked(userWalletId)
|
||||
}
|
||||
|
||||
override fun onClickGoToSupport(customerId: String) {
|
||||
clickIntents.onKycRejectedGoToSupportClicked(customerId)
|
||||
}
|
||||
|
||||
override fun onClickHideKyc(userWalletId: UserWalletId) {
|
||||
clickIntents.onKycRejectedHideKycClicked(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
inner class AskForPushNotificationsCallbacks : PushNotificationsModelCallbacks {
|
||||
|
||||
override fun onAllowSystemPermission() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.child.wallet.model.intents
|
||||
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
|
|
@ -13,6 +14,7 @@ import com.tangem.core.ui.message.bottomSheetMessage
|
|||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.feedback.models.WalletMetaInfo
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
|
|
@ -21,6 +23,7 @@ import com.tangem.domain.pay.repository.OnboardingRepository
|
|||
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
|
||||
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayHideOnboardingStateTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayRefreshNeededStateTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayRefreshShowProgressTransformer
|
||||
|
|
@ -38,6 +41,14 @@ internal interface TangemPayIntents {
|
|||
|
||||
fun onKycProgressClicked(userWalletId: UserWalletId)
|
||||
|
||||
fun onKycRejectedClicked(userWalletId: UserWalletId, customerId: String)
|
||||
|
||||
fun onKycRejectedOpenProfileClicked(userWalletId: UserWalletId)
|
||||
|
||||
fun onKycRejectedGoToSupportClicked(customerId: String)
|
||||
|
||||
fun onKycRejectedHideKycClicked(userWalletId: UserWalletId)
|
||||
|
||||
fun onIssuingCardClicked()
|
||||
|
||||
fun onIssuingFailedClicked()
|
||||
|
|
@ -137,6 +148,42 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
|
|||
uiMessageSender.send(kycInfoBottomSheet)
|
||||
}
|
||||
|
||||
override fun onKycRejectedOpenProfileClicked(userWalletId: UserWalletId) {
|
||||
router.openTangemPayOnboarding(
|
||||
mode = AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding(userWalletId),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onKycRejectedGoToSupportClicked(customerId: String) {
|
||||
goToSupportForRejectKyc(customerId)
|
||||
}
|
||||
|
||||
override fun onKycRejectedHideKycClicked(userWalletId: UserWalletId) {
|
||||
disableTangemPay(userWalletId)
|
||||
}
|
||||
|
||||
override fun onKycRejectedClicked(userWalletId: UserWalletId, customerId: String) {
|
||||
router.dialogNavigation.activate(
|
||||
configuration = WalletDialogConfig.KycRejected(
|
||||
walletId = userWalletId,
|
||||
customerId = customerId,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun goToSupportForRejectKyc(customerId: String) {
|
||||
modelScope.launch {
|
||||
sendFeedbackEmailUseCase(
|
||||
type = FeedbackEmailType.Visa.KycRejected(
|
||||
walletMetaInfo = WalletMetaInfo(
|
||||
userWalletId = stateHolder.getSelectedWalletId(),
|
||||
),
|
||||
customerId = customerId,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onIssuingCardClicked() {
|
||||
val issuingBottomSheet = bottomSheetMessage {
|
||||
infoBlock {
|
||||
|
|
|
|||
|
|
@ -1,116 +0,0 @@
|
|||
package com.tangem.feature.wallet.child.wallet.model.intents
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.models.wallet.requireColdWallet
|
||||
import com.tangem.domain.visa.GetVisaCurrencyUseCase
|
||||
import com.tangem.domain.visa.GetVisaTxDetailsUseCase
|
||||
import com.tangem.domain.visa.model.VisaTxDetails
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.BalancesAndLimitsBottomSheetConverter
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.VisaTxDetailsBottomSheetConverter
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface VisaWalletIntents {
|
||||
|
||||
fun onBalancesAndLimitsClick()
|
||||
|
||||
fun onVisaTransactionClick(id: String)
|
||||
|
||||
fun onExploreClick(exploreUrl: String)
|
||||
|
||||
fun onDisputeClick(txDetails: VisaTxDetails)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ModelScoped
|
||||
internal class VisaWalletIntentsImplementor @Inject constructor(
|
||||
private val stateController: WalletStateController,
|
||||
private val eventSender: WalletEventSender,
|
||||
private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase,
|
||||
private val getVisaTxDetailsUseCase: GetVisaTxDetailsUseCase,
|
||||
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
|
||||
private val getUserWalletsUseCase: GetWalletsUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : BaseWalletClickIntents(), VisaWalletIntents {
|
||||
|
||||
private val balancesAndLimitsBottomSheetConverter by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
BalancesAndLimitsBottomSheetConverter(eventSender, analyticsEventHandler)
|
||||
}
|
||||
|
||||
override fun onBalancesAndLimitsClick() {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.LimitsClicked())
|
||||
modelScope.launch(dispatchers.main) {
|
||||
val userWalletId = stateController.getSelectedWalletId()
|
||||
val balancesAndLimits = getVisaCurrencyUseCase(userWalletId)
|
||||
.getOrElse {
|
||||
Timber.e("Unable to get balances and limits: $it")
|
||||
return@launch
|
||||
}
|
||||
|
||||
val bottomSheetContent = balancesAndLimitsBottomSheetConverter.convert(
|
||||
value = balancesAndLimits,
|
||||
)
|
||||
|
||||
stateController.showBottomSheet(bottomSheetContent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onVisaTransactionClick(id: String) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
val userWalletId = stateController.getSelectedWalletId()
|
||||
val visaCurrency = getVisaCurrencyUseCase(userWalletId)
|
||||
.getOrElse {
|
||||
Timber.e(it, "Failed to get visa currency")
|
||||
return@launch
|
||||
}
|
||||
val transactionDetails = getVisaTxDetailsUseCase(userWalletId, id)
|
||||
.getOrElse {
|
||||
Timber.e(it, "Failed to get transaction details")
|
||||
return@launch
|
||||
}
|
||||
|
||||
val converter = VisaTxDetailsBottomSheetConverter(
|
||||
visaCurrency,
|
||||
clickIntents = this@VisaWalletIntentsImplementor,
|
||||
)
|
||||
|
||||
stateController.showBottomSheet(content = converter.convert(transactionDetails))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onExploreClick(exploreUrl: String) {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.ButtonExplore())
|
||||
router.openUrl(exploreUrl)
|
||||
}
|
||||
|
||||
override fun onDisputeClick(txDetails: VisaTxDetails) {
|
||||
modelScope.launch {
|
||||
val userWalletId = stateController.getSelectedWalletId()
|
||||
val userWallet = getUserWalletsUseCase.invokeSync()
|
||||
.firstOrNull { it.walletId == userWalletId } ?: return@launch
|
||||
val cardInfo = getWalletMetaInfoUseCase.invoke(
|
||||
userWallet.requireColdWallet().scanResponse,
|
||||
).getOrNull() ?: return@launch
|
||||
|
||||
sendFeedbackEmailUseCase(
|
||||
FeedbackEmailType.Visa.Dispute(
|
||||
walletMetaInfo = cardInfo,
|
||||
visaTxDetails = txDetails,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,6 @@ internal class WalletClickIntents @Inject constructor(
|
|||
private val warningsClickIntentsImplementer: WalletWarningsClickIntentsImplementor,
|
||||
private val currencyActionsClickIntentsImplementor: WalletCurrencyActionsClickIntentsImplementor,
|
||||
private val contentClickIntentsImplementor: WalletContentClickIntentsImplementor,
|
||||
private val visaWalletIntentsImplementor: VisaWalletIntentsImplementor,
|
||||
private val pushPermissionClickIntentsImplementor: WalletPushPermissionClickIntentsImplementor,
|
||||
private val stateHolder: WalletStateController,
|
||||
private val walletScreenContentLoader: WalletScreenContentLoader,
|
||||
|
|
@ -47,7 +46,6 @@ internal class WalletClickIntents @Inject constructor(
|
|||
WalletWarningsClickIntents by warningsClickIntentsImplementer,
|
||||
WalletCurrencyActionsClickIntents by currencyActionsClickIntentsImplementor,
|
||||
WalletContentClickIntents by contentClickIntentsImplementor,
|
||||
VisaWalletIntents by visaWalletIntentsImplementor,
|
||||
WalletPushPermissionClickIntents by pushPermissionClickIntentsImplementor,
|
||||
TangemPayIntents by tangemPayIntents {
|
||||
|
||||
|
|
@ -58,7 +56,6 @@ internal class WalletClickIntents @Inject constructor(
|
|||
warningsClickIntentsImplementer.initialize(router, coroutineScope)
|
||||
currencyActionsClickIntentsImplementor.initialize(router, coroutineScope)
|
||||
contentClickIntentsImplementor.initialize(router, coroutineScope)
|
||||
visaWalletIntentsImplementor.initialize(router, coroutineScope)
|
||||
pushPermissionClickIntentsImplementor.initialize(router, coroutineScope)
|
||||
tangemPayIntents.initialize(router, coroutineScope)
|
||||
}
|
||||
|
|
@ -98,14 +95,11 @@ internal class WalletClickIntents @Inject constructor(
|
|||
refreshMultiCurrencyContent(showRefreshState)
|
||||
}
|
||||
is WalletState.SingleCurrency.Content,
|
||||
is WalletState.Visa.Content,
|
||||
-> {
|
||||
refreshSingleCurrencyContent(showRefreshState)
|
||||
}
|
||||
is WalletState.MultiCurrency.Locked,
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
-> Unit
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import com.tangem.domain.tokens.model.TokenActionsState
|
|||
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyEnterStatusUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplySetShouldShowMainPromoUseCase
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
|
|
@ -103,7 +102,6 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val yieldSupplyEnterStatusUseCase: YieldSupplyEnterStatusUseCase,
|
||||
private val yieldSupplySetShouldShowMainPromoUseCase: YieldSupplySetShouldShowMainPromoUseCase,
|
||||
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
) : BaseWalletClickIntents(), WalletContentClickIntents {
|
||||
|
|
@ -199,7 +197,6 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
is NavigationAction.YieldSupply -> openYieldSupply(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyStatus = currencyStatus,
|
||||
navigationAction = navigationAction,
|
||||
apy = apy,
|
||||
)
|
||||
is NavigationAction.CloreMigration -> Unit
|
||||
|
|
@ -234,43 +231,21 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onAccountExpandClick(account: Account) {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.AccountShowTokens())
|
||||
accountDependencies.expandedAccountsHolder.expandAccount(userWalletId, account.accountId)
|
||||
accountDependencies.expandedAccountsHolder.expandAccount(account.accountId)
|
||||
}
|
||||
|
||||
override fun onAccountCollapseClick(account: Account) {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.AccountHideTokens())
|
||||
accountDependencies.expandedAccountsHolder.collapseAccount(userWalletId, account.accountId)
|
||||
accountDependencies.expandedAccountsHolder.collapseAccount(account.accountId)
|
||||
}
|
||||
|
||||
private fun openYieldSupply(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
navigationAction: NavigationAction.YieldSupply,
|
||||
apy: String,
|
||||
) {
|
||||
modelScope.launch {
|
||||
val tokenEnterStatus = yieldSupplyEnterStatusUseCase(userWalletId, cryptoCurrencyStatus).getOrNull()
|
||||
when {
|
||||
tokenEnterStatus != null -> router.openTokenDetails(
|
||||
userWalletId = userWalletId,
|
||||
currencyStatus = cryptoCurrencyStatus,
|
||||
navigationAction = navigationAction,
|
||||
)
|
||||
navigationAction.isActive -> router.openYieldSupplyActiveScreen(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
apy = apy,
|
||||
)
|
||||
else -> router.openYieldSupplyPromoScreen(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
apy = apy,
|
||||
)
|
||||
}
|
||||
}
|
||||
private fun openYieldSupply(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, apy: String) {
|
||||
router.openYieldSupplyEntryScreen(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
apy = apy,
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendApyLabelClickAnalytics(navigationAction: NavigationAction, currencyStatus: CryptoCurrencyStatus) {
|
||||
|
|
@ -326,7 +301,6 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
|
||||
getExplorerTransactionUrlUseCase(
|
||||
txHash = txHash,
|
||||
networkId = currency.network.id,
|
||||
currency = currency,
|
||||
).fold(
|
||||
ifLeft = { Timber.e(it.toString()) },
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.tangem.common.routing.AppRoute
|
|||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetConfig
|
||||
import com.tangem.common.ui.bottomsheet.receive.AddressModel
|
||||
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
|
||||
import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels
|
||||
import com.tangem.common.ui.tokens.getUnavailabilityReasonText
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
|
|
@ -13,9 +12,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
|
|||
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.navigation.share.ShareManager
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.WrappedList
|
||||
|
|
@ -35,7 +32,6 @@ import com.tangem.domain.markets.TokenMarketParams
|
|||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.network.NetworkAddress
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -47,7 +43,6 @@ import com.tangem.domain.staking.model.StakingOption
|
|||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
|
||||
import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource
|
||||
import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
|
||||
|
|
@ -67,7 +62,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -144,10 +138,8 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val vibratorHapticManager: VibratorHapticManager,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
private val shareManager: ShareManager,
|
||||
private val appRouter: AppRouter,
|
||||
private val rampStateManager: RampStateManager,
|
||||
private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle,
|
||||
private val saveViewedTokenReceiveWarningUseCase: SaveViewedTokenReceiveWarningUseCase,
|
||||
private val receiveAddressesFactory: ReceiveAddressesFactory,
|
||||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
|
||||
|
|
@ -249,29 +241,6 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
return resourceReference(R.string.wallet_notification_address_copied)
|
||||
}
|
||||
|
||||
private fun createReceiveBottomSheetContent(
|
||||
currency: CryptoCurrency,
|
||||
addresses: NetworkAddress,
|
||||
): TangemBottomSheetConfigContent {
|
||||
return TokenReceiveBottomSheetConfig(
|
||||
asset = TokenReceiveBottomSheetConfig.Asset.Currency(
|
||||
name = currency.name,
|
||||
symbol = currency.symbol,
|
||||
),
|
||||
network = currency.network,
|
||||
networkAddress = addresses,
|
||||
showMemoDisclaimer = currency.network.transactionExtrasType != Network.TransactionExtrasType.NONE,
|
||||
onCopyClick = {
|
||||
analyticsEventHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(currency.symbol))
|
||||
clipboardManager.setText(text = it, isSensitive = true)
|
||||
},
|
||||
onShareClick = {
|
||||
analyticsEventHandler.send(TokenReceiveAnalyticsEvent.ButtonShareAddress(currency.symbol))
|
||||
shareManager.shareText(text = it)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCopyAddressClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
analyticsEventHandler.send(
|
||||
event = TokenReceiveNewAnalyticsEvent.ButtonCopyAddress(
|
||||
|
|
@ -505,6 +474,8 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
) {
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWalletId))
|
||||
|
||||
val integrationId = option?.integrationId ?: return
|
||||
|
||||
modelScope.launch {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
|
||||
|
|
@ -512,7 +483,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
AppRoute.Staking(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
yieldId = option?.integrationId ?: return@launch,
|
||||
integrationId = integrationId,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -594,11 +565,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
) {
|
||||
stateHolder.showBottomSheet(
|
||||
ChooseAddressBottomSheetConfig(
|
||||
asset = TokenReceiveBottomSheetConfig.Asset.Currency(
|
||||
name = currency.name,
|
||||
symbol = currency.symbol,
|
||||
),
|
||||
network = currency.network,
|
||||
currency = currency,
|
||||
networkAddress = addresses,
|
||||
onClick = {
|
||||
onAddressTypeSelected(
|
||||
|
|
@ -766,25 +733,11 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
private fun navigateToReceive(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
|
||||
stateHolder.hideBottomSheet()
|
||||
modelScope.launch {
|
||||
configureReceiveAddresses(cryptoCurrencyStatus = cryptoCurrencyStatus)?.let {
|
||||
router.openTokenReceiveBottomSheet(it)
|
||||
}
|
||||
stateHolder.hideBottomSheet()
|
||||
modelScope.launch {
|
||||
configureReceiveAddresses(cryptoCurrencyStatus = cryptoCurrencyStatus)?.let {
|
||||
router.openTokenReceiveBottomSheet(it)
|
||||
}
|
||||
} else {
|
||||
analyticsEventHandler.send(
|
||||
event = TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrencyStatus.currency.symbol),
|
||||
)
|
||||
stateHolder.showBottomSheet(
|
||||
createReceiveBottomSheetContent(
|
||||
currency = cryptoCurrencyStatus.currency,
|
||||
addresses = cryptoCurrencyStatus.value.networkAddress ?: return,
|
||||
),
|
||||
userWalletId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.decompose.model.Model
|
|||
import com.tangem.feature.wallet.DefaultWalletEntryComponent
|
||||
import com.tangem.feature.wallet.child.organizetokens.model.OrganizeTokensModel
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletModel
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejectedModel
|
||||
import com.tangem.feature.wallet.utils.DefaultUserWalletImageFetcher
|
||||
import com.tangem.feature.wallet.utils.DefaultUserWalletsFetcher
|
||||
import com.tangem.features.wallet.WalletEntryComponent
|
||||
|
|
@ -36,6 +37,11 @@ internal interface WalletFeatureModule {
|
|||
@ClassKey(WalletModel::class)
|
||||
fun bindWalletModel(model: WalletModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(KycRejectedModel::class)
|
||||
fun bindKycRejectedModel(model: KycRejectedModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OrganizeTokensModel::class)
|
||||
|
|
|
|||
|
|
@ -1,60 +1,103 @@
|
|||
package com.tangem.feature.wallet.presentation.account
|
||||
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.account.models.AccountExpandedState
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.producer.SingleAccountListProducer
|
||||
import com.tangem.domain.account.repository.AccountsExpandedRepository
|
||||
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
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class ExpandedAccountsHolder @Inject constructor(
|
||||
private val singleAccountListSupplier: SingleAccountListSupplier,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
private val accountsExpandedRepository: AccountsExpandedRepository,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
private val expandedAccounts = MutableStateFlow<Map<UserWalletId, Set<AccountId>>>(mapOf())
|
||||
private val actionChannel = MutableSharedFlow<Pair<AccountId, Boolean>>(
|
||||
extraBufferCapacity = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
|
||||
fun expandedAccounts(userWallet: UserWallet): Flow<Set<AccountId>> = channelFlow {
|
||||
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()
|
||||
val walletId = userWallet.walletId
|
||||
|
||||
val storedState = accountsExpandedRepository.expandedAccounts
|
||||
.map { it[walletId].orEmpty() }
|
||||
.stateIn(this)
|
||||
|
||||
val isAccountsMode = isAccountsModeEnabledUseCase.invoke()
|
||||
.stateIn(this)
|
||||
|
||||
val initExpandedState = storedState.value
|
||||
.mapNotNull { it.takeIf { state -> state.isExpanded }?.accountId }
|
||||
.toSet()
|
||||
// main state holder
|
||||
val expandedAccounts = MutableStateFlow(initExpandedState)
|
||||
|
||||
actionChannel
|
||||
.filter { (accountId, _) -> accountId.userWalletId == walletId }
|
||||
.onEach { (accountId, isExpand) ->
|
||||
val newState = AccountExpandedState(accountId, isExpand)
|
||||
launch { accountsExpandedRepository.update(newState) }
|
||||
if (isExpand) {
|
||||
expandedAccounts.update { it.plus(accountId) }
|
||||
} else {
|
||||
expandedAccounts.update { it.minus(accountId) }
|
||||
}
|
||||
expandedAccounts.update { map ->
|
||||
var expandedSet = map[userWallet.walletId] ?: defaultExpanded
|
||||
// force expand for single account
|
||||
if (isSingleAccount || !isAccountsMode) expandedSet = defaultExpanded
|
||||
map.plus(userWallet.walletId to expandedSet)
|
||||
}
|
||||
.launchIn(this)
|
||||
|
||||
walletAccounts(walletId).onEach { accountList ->
|
||||
if (!isAccountsModeEnabledUseCase.invokeSync()) {
|
||||
accountsExpandedRepository.clearStore()
|
||||
expandedAccounts.update { setOf() }
|
||||
return@onEach
|
||||
}
|
||||
val idsSet = accountList.accounts.mapTo(mutableSetOf()) { it.accountId }
|
||||
accountsExpandedRepository.syncStore(walletId, idsSet)
|
||||
|
||||
val isSingleAccount = accountList.accounts.size == 1
|
||||
val storedMainAccountState = storedState.value
|
||||
.find { it.accountId == accountList.mainAccount.accountId }
|
||||
|
||||
if (isSingleAccount && storedMainAccountState == null) {
|
||||
// force expand for single and not stored account
|
||||
expandedAccounts.update { setOf(accountList.mainAccount.accountId) }
|
||||
}
|
||||
}.launchIn(this)
|
||||
|
||||
combine(
|
||||
flow = expandedAccounts,
|
||||
flow2 = isAccountsMode,
|
||||
transform = { expanded, isAccountMode ->
|
||||
if (isAccountMode) {
|
||||
channel.send(expanded)
|
||||
} else {
|
||||
channel.send(setOf())
|
||||
}
|
||||
},
|
||||
).launchIn(this)
|
||||
).collect()
|
||||
}
|
||||
.flowOn(dispatchers.default)
|
||||
.distinctUntilChanged()
|
||||
|
||||
expandedAccounts
|
||||
.mapNotNull { map -> map[userWallet.walletId] }
|
||||
.onEach { expanded -> channel.send(expanded) }
|
||||
.collect()
|
||||
fun expandAccount(accountId: AccountId) {
|
||||
actionChannel.tryEmit(accountId to true)
|
||||
}
|
||||
|
||||
fun expandAccount(userWalletId: UserWalletId, accountId: AccountId) = expandedAccounts.update { map ->
|
||||
val expandedSet = map[userWalletId]?.plus(accountId) ?: return@update map
|
||||
map.plus(userWalletId to expandedSet)
|
||||
fun collapseAccount(accountId: AccountId) {
|
||||
actionChannel.tryEmit(accountId to false)
|
||||
}
|
||||
|
||||
fun collapseAccount(userWalletId: UserWalletId, accountId: AccountId) = expandedAccounts.update { map ->
|
||||
val expandedSet = map[userWalletId]?.minus(accountId) ?: return@update map
|
||||
map.plus(userWalletId to expandedSet)
|
||||
}
|
||||
|
||||
private fun walletAccounts(userWallet: UserWallet): Flow<AccountList> =
|
||||
singleAccountListSupplier(SingleAccountListProducer.Params(userWallet.walletId))
|
||||
private fun walletAccounts(walletId: UserWalletId): Flow<AccountList> = singleAccountListSupplier(walletId)
|
||||
}
|
||||
|
|
@ -218,6 +218,7 @@ private fun LazyItemScope.DraggableItem(
|
|||
is DraggableItem.Portfolio -> ExpandedPortfolioHeader(
|
||||
state = item.tokenItemState,
|
||||
isCollapsable = false,
|
||||
composables = null,
|
||||
modifier = modifierWithBackground
|
||||
.padding(top = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
|
|
|
|||
|
|
@ -133,19 +133,9 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override fun openYieldSupplyActiveScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String) {
|
||||
override fun openYieldSupplyEntryScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String) {
|
||||
router.push(
|
||||
AppRoute.YieldSupplyActive(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
apy = apy,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun openYieldSupplyPromoScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String) {
|
||||
router.push(
|
||||
AppRoute.YieldSupplyPromo(
|
||||
AppRoute.YieldSupplyEntry(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
apy = apy,
|
||||
|
|
|
|||
|
|
@ -75,9 +75,6 @@ internal interface InnerWalletRouter {
|
|||
onWarningAcknowledged: (TokenAction) -> Unit,
|
||||
)
|
||||
|
||||
/** Open yield supply active screen */
|
||||
fun openYieldSupplyActiveScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String)
|
||||
|
||||
/** Open yield supply promo screen */
|
||||
fun openYieldSupplyPromoScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String)
|
||||
/** Open yield supply entry screen */
|
||||
fun openYieldSupplyEntryScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String)
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.analytics.utils
|
|||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.pay.model.CustomerInfo
|
||||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderStatus
|
||||
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
|
||||
|
|
@ -25,8 +26,10 @@ internal class WalletTangemPayAnalyticsEventSender @Inject constructor(
|
|||
// TODO: TangemPay refactor analytics
|
||||
// when statement copied from TangemPayUpdateInfoStateTransformer. Be careful when editing
|
||||
val event = when {
|
||||
customerInfo.orderStatus == OrderStatus.CANCELED -> return // ignore cancelled state on analytics
|
||||
!customerInfo.info.isKycApproved -> return // ignore kyc not approved state on analytics
|
||||
// ignore cancelled state on analytics
|
||||
customerInfo.orderStatus == OrderStatus.CANCELED -> return
|
||||
// ignore kyc not approved state on analytics
|
||||
customerInfo.info.kycStatus != CustomerInfo.KycStatus.APPROVED -> return
|
||||
cardInfo != null && productInstance != null -> return
|
||||
else -> TangemPayAnalyticsEvents.IssuingBannerDisplayed()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ internal class WalletContentLoaderFactory @Inject constructor(
|
|||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val singleWalletContentLoaderFactory: SingleWalletContentLoaderFactory,
|
||||
private val singleWalletContentLoaderV2Factory: SingleWalletContentLoaderV2.Factory,
|
||||
private val visaWalletContentLoaderFactory: VisaWalletContentLoaderFactory,
|
||||
) {
|
||||
|
||||
fun create(
|
||||
|
|
@ -42,9 +41,6 @@ internal class WalletContentLoaderFactory @Inject constructor(
|
|||
singleWalletWithTokenContentLoaderFactory.create(userWallet, clickIntents)
|
||||
}
|
||||
}
|
||||
userWallet is UserWallet.Cold && userWallet.scanResponse.cardTypesResolver.isVisaWallet() -> {
|
||||
visaWalletContentLoaderFactory.create(userWallet, clickIntents, isRefresh)
|
||||
}
|
||||
userWallet is UserWallet.Cold && !userWallet.isMultiCurrency -> {
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
singleWalletContentLoaderV2Factory.create(userWallet, isRefresh)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CloseableCoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.newSingleThreadContext
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -23,9 +24,11 @@ import javax.inject.Inject
|
|||
internal class WalletScreenContentLoader @Inject constructor(
|
||||
private val factory: WalletContentLoaderFactory,
|
||||
private val storage: WalletLoaderStorage,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
private val singleBackgroundDispatcher: CloseableCoroutineDispatcher =
|
||||
newSingleThreadContext(name = "Background Main")
|
||||
|
||||
/**
|
||||
* Load content by [UserWallet]
|
||||
*
|
||||
|
|
@ -64,6 +67,7 @@ internal class WalletScreenContentLoader @Inject constructor(
|
|||
fun cancelAll() {
|
||||
Timber.d("All content loading is canceled")
|
||||
storage.clear()
|
||||
singleBackgroundDispatcher.close()
|
||||
}
|
||||
|
||||
private fun loadInternal(
|
||||
|
|
@ -86,7 +90,7 @@ internal class WalletScreenContentLoader @Inject constructor(
|
|||
Timber.d("${userWallet.walletId} content loading is ${if (isRefresh) "re" else ""}started")
|
||||
|
||||
loader.subscribers
|
||||
.map { it.subscribe(coroutineScope, dispatchers) }
|
||||
.map { it.subscribe(coroutineScope, singleBackgroundDispatcher) }
|
||||
.let { storage.set(userWallet.walletId, it) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
||||
|
||||
import com.tangem.domain.visa.GetVisaCurrencyUseCase
|
||||
import com.tangem.domain.visa.GetVisaTxHistoryUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.VisaWalletSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
|
||||
internal class VisaWalletContentLoader(
|
||||
private val userWallet: UserWallet.Cold,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val isRefresh: Boolean,
|
||||
private val stateController: WalletStateController,
|
||||
private val getVisaTxHistoryUseCase: GetVisaTxHistoryUseCase,
|
||||
private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> {
|
||||
return listOf(
|
||||
VisaWalletSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateController = stateController,
|
||||
isRefresh = isRefresh,
|
||||
getVisaCurrencyUseCase = getVisaCurrencyUseCase,
|
||||
getVisaTxHistoryUseCase = getVisaTxHistoryUseCase,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
||||
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.visa.GetVisaCurrencyUseCase
|
||||
import com.tangem.domain.visa.GetVisaTxHistoryUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class VisaWalletContentLoaderFactory @Inject constructor(
|
||||
private val stateHolder: WalletStateController,
|
||||
private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase,
|
||||
private val getVisaTxHistoryUseCase: GetVisaTxHistoryUseCase,
|
||||
) {
|
||||
|
||||
fun create(userWallet: UserWallet.Cold, clickIntents: WalletClickIntents, isRefresh: Boolean): WalletContentLoader {
|
||||
return VisaWalletContentLoader(
|
||||
userWallet = userWallet,
|
||||
clickIntents = clickIntents,
|
||||
isRefresh = isRefresh,
|
||||
stateController = stateHolder,
|
||||
getVisaCurrencyUseCase = getVisaCurrencyUseCase,
|
||||
getVisaTxHistoryUseCase = getVisaTxHistoryUseCase,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
@Immutable
|
||||
internal sealed class BalancesAndLimitsBlockState {
|
||||
|
||||
object Loading : BalancesAndLimitsBlockState()
|
||||
|
||||
object Error : BalancesAndLimitsBlockState()
|
||||
|
||||
data class Content(
|
||||
val availableBalance: String,
|
||||
val limitDays: Int,
|
||||
val isEnabled: Boolean,
|
||||
val onClick: () -> Unit,
|
||||
) : BalancesAndLimitsBlockState()
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
|
||||
internal data class BalancesAndLimitsBottomSheetConfig(
|
||||
val balance: Balance,
|
||||
val limit: Limit,
|
||||
) : TangemBottomSheetConfigContent {
|
||||
|
||||
data class Balance(
|
||||
val totalBalance: String,
|
||||
val availableBalance: String,
|
||||
val blockedBalance: String,
|
||||
val debit: String,
|
||||
val amlVerified: String,
|
||||
val onInfoClick: () -> Unit,
|
||||
)
|
||||
|
||||
data class Limit(
|
||||
val availableBy: String,
|
||||
val total: String,
|
||||
val other: String,
|
||||
val singleTransaction: String,
|
||||
val onInfoClick: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class VisaTxDetailsBottomSheetConfig(
|
||||
val transaction: Transaction,
|
||||
val requests: ImmutableList<Request>,
|
||||
val onDisputeClick: () -> Unit,
|
||||
) : TangemBottomSheetConfigContent {
|
||||
|
||||
data class Transaction(
|
||||
val id: String,
|
||||
val type: String,
|
||||
val status: String,
|
||||
val blockchainAmount: String,
|
||||
val transactionAmount: String,
|
||||
val transactionCurrencyCode: String,
|
||||
val merchantName: String,
|
||||
val merchantCity: String,
|
||||
val merchantCountryCode: String,
|
||||
val merchantCategoryCode: String,
|
||||
)
|
||||
|
||||
data class Request(
|
||||
val id: String,
|
||||
val type: String,
|
||||
val status: String,
|
||||
val blockchainAmount: String,
|
||||
val transactionAmount: String,
|
||||
val currencyCode: String,
|
||||
val errorCode: Int,
|
||||
val date: String,
|
||||
val txHash: String,
|
||||
val txStatus: String,
|
||||
val onExploreClick: (() -> Unit)?,
|
||||
)
|
||||
}
|
||||
|
|
@ -32,4 +32,7 @@ internal sealed interface WalletDialogConfig {
|
|||
val tokenAction: TokenAction,
|
||||
val onWarningAcknowledged: (TokenAction) -> Unit,
|
||||
) : WalletDialogConfig
|
||||
|
||||
@Serializable
|
||||
data class KycRejected(val walletId: UserWalletId, val customerId: String) : WalletDialogConfig
|
||||
}
|
||||
|
|
@ -12,7 +12,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistor
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.WalletStateHolder
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal const val NOT_INITIALIZED_WALLET_INDEX = -1
|
||||
|
||||
|
|
@ -97,59 +96,4 @@ internal sealed interface WalletState : WalletStateHolder {
|
|||
override val marketPriceBlockState: MarketPriceBlockState? = null
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Visa : WalletState, TxHistoryStateHolder {
|
||||
|
||||
abstract val balancesAndLimitBlockState: BalancesAndLimitsBlockState?
|
||||
|
||||
data class Content(
|
||||
override val pullToRefreshConfig: PullToRefreshConfig,
|
||||
override val walletCardState: WalletCardState,
|
||||
override val buttons: PersistentList<WalletManageButton>,
|
||||
override val warnings: ImmutableList<WalletNotification>,
|
||||
override val bottomSheetConfig: TangemBottomSheetConfig?,
|
||||
override val balancesAndLimitBlockState: BalancesAndLimitsBlockState,
|
||||
override val txHistoryState: TxHistoryState,
|
||||
) : Visa()
|
||||
|
||||
data class Locked(
|
||||
override val walletCardState: WalletCardState,
|
||||
override val buttons: PersistentList<WalletManageButton>,
|
||||
override val bottomSheetConfig: TangemBottomSheetConfig?,
|
||||
val onUnlockNotificationClick: () -> Unit,
|
||||
val onExploreClick: () -> Unit,
|
||||
) : Visa(),
|
||||
TxHistoryStateHolder by LockedTxHistoryStateHolder(onExploreClick),
|
||||
WalletStateHolder by LockedWalletStateHolder(
|
||||
walletCardState = walletCardState,
|
||||
buttons = buttons,
|
||||
bottomSheetConfig = bottomSheetConfig,
|
||||
onUnlockNotificationClick = onUnlockNotificationClick,
|
||||
) {
|
||||
|
||||
override val balancesAndLimitBlockState: BalancesAndLimitsBlockState? = null
|
||||
}
|
||||
|
||||
data class AccessTokenLocked(
|
||||
override val walletCardState: WalletCardState,
|
||||
override val buttons: PersistentList<WalletManageButton>,
|
||||
override val bottomSheetConfig: TangemBottomSheetConfig?,
|
||||
val onExploreClick: () -> Unit,
|
||||
val onUnlockVisaAccessNotificationClick: () -> Unit,
|
||||
) : Visa(),
|
||||
TxHistoryStateHolder by LockedTxHistoryStateHolder(onExploreClick),
|
||||
WalletStateHolder by LockedWalletStateHolder(
|
||||
walletCardState = walletCardState,
|
||||
buttons = buttons,
|
||||
bottomSheetConfig = bottomSheetConfig,
|
||||
onUnlockNotificationClick = {},
|
||||
) {
|
||||
|
||||
override val warnings: ImmutableList<WalletNotification> = persistentListOf(
|
||||
WalletNotification.UnlockVisaAccess(onUnlockClick = onUnlockVisaAccessNotificationClick),
|
||||
)
|
||||
|
||||
override val balancesAndLimitBlockState: BalancesAndLimitsBlockState? = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,15 +19,6 @@ internal class CloseBottomSheetTransformer(userWalletId: UserWalletId) : WalletS
|
|||
is WalletState.SingleCurrency.Locked -> prevState.copy(
|
||||
bottomSheetConfig = updateConfig(prevState),
|
||||
)
|
||||
is WalletState.Visa.Content -> prevState.copy(
|
||||
bottomSheetConfig = updateConfig(prevState),
|
||||
)
|
||||
is WalletState.Visa.Locked -> prevState.copy(
|
||||
bottomSheetConfig = updateConfig(prevState),
|
||||
)
|
||||
is WalletState.Visa.AccessTokenLocked -> prevState.copy(
|
||||
bottomSheetConfig = updateConfig(prevState),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import timber.log.Timber
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Transformer for disabling action of multi-currency wallet
|
||||
*
|
||||
* @param userWalletId user wallet id
|
||||
* @property actionClass action class that must be disabled
|
||||
*/
|
||||
internal class DisableActionTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
private val actionClass: KClass<out WalletManageButton>,
|
||||
) : WalletStateTransformer(userWalletId = userWalletId) {
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return when (prevState) {
|
||||
is WalletState.MultiCurrency.Content -> {
|
||||
prevState.copy(buttons = prevState.buttons.updateButtons())
|
||||
}
|
||||
is WalletState.MultiCurrency.Locked -> {
|
||||
Timber.w("Impossible to disable action for locked wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.SingleCurrency -> {
|
||||
Timber.w("Impossible to disable action for single-currency wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.Visa -> {
|
||||
Timber.w("Impossible to disable action for VISA wallet")
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PersistentList<WalletManageButton>.updateButtons(): PersistentList<WalletManageButton> {
|
||||
return map { action ->
|
||||
if (action::class == actionClass) {
|
||||
when (action) {
|
||||
is WalletManageButton.Buy -> action.copy(enabled = false)
|
||||
is WalletManageButton.Swap -> action.copy(enabled = false)
|
||||
is WalletManageButton.Sell -> action.copy(enabled = false)
|
||||
else -> action
|
||||
}
|
||||
} else {
|
||||
action
|
||||
}
|
||||
}
|
||||
.toPersistentList()
|
||||
}
|
||||
}
|
||||
|
|
@ -3,13 +3,13 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState.MultiCurrency.WalletType
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.createStateByWalletType
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState.MultiCurrency.WalletType
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -77,15 +77,6 @@ internal class InitializeWalletsTransformer(
|
|||
onExploreClick = clickIntents::onExploreClick,
|
||||
)
|
||||
},
|
||||
visaWalletCreator = {
|
||||
WalletState.Visa.Locked(
|
||||
walletCardState = userWallet.toLockedWalletCardState(),
|
||||
buttons = createMultiWalletEnabledButtons(userWallet),
|
||||
bottomSheetConfig = null,
|
||||
onUnlockNotificationClick = clickIntents::onOpenUnlockWalletsBottomSheetClick,
|
||||
onExploreClick = clickIntents::onExploreClick,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,15 +25,6 @@ internal class OpenBottomSheetTransformer(
|
|||
is WalletState.SingleCurrency.Locked -> prevState.copy(
|
||||
bottomSheetConfig = updateConfig(),
|
||||
)
|
||||
is WalletState.Visa.Content -> prevState.copy(
|
||||
bottomSheetConfig = updateConfig(),
|
||||
)
|
||||
is WalletState.Visa.Locked -> prevState.copy(
|
||||
bottomSheetConfig = updateConfig(),
|
||||
)
|
||||
is WalletState.Visa.AccessTokenLocked -> prevState.copy(
|
||||
bottomSheetConfig = updateConfig(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,8 @@ internal class RemoveNFTCollectionsTransformer(
|
|||
nftState = WalletNFTItemUM.Hidden,
|
||||
)
|
||||
is WalletState.SingleCurrency.Content,
|
||||
is WalletState.Visa.Content,
|
||||
is WalletState.MultiCurrency.Locked,
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
-> prevState
|
||||
}
|
||||
}
|
||||
|
|
@ -38,13 +38,8 @@ internal class RenameWalletsTransformer(
|
|||
is WalletState.SingleCurrency.Content -> {
|
||||
prevState.copy(walletCardState = prevState.walletCardState.copySealed(title = newName))
|
||||
}
|
||||
is WalletState.Visa.Content -> {
|
||||
prevState.copy(walletCardState = prevState.walletCardState.copySealed(title = newName))
|
||||
}
|
||||
is WalletState.MultiCurrency.Locked,
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
-> {
|
||||
Timber.e("Impossible to rename wallet in locked state")
|
||||
prevState
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import timber.log.Timber
|
||||
|
|
@ -32,10 +32,6 @@ internal class SetCryptoCurrencyActionsTransformer(
|
|||
Timber.w("Impossible to load crypto currency actions for multi-currency wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.Visa -> {
|
||||
Timber.w("Impossible to load crypto currency actions for VISA wallet")
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,10 +49,6 @@ internal class SetExpressStatusesTransformer(
|
|||
Timber.w("Impossible to load express statuses for locked wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.Visa -> {
|
||||
Timber.w("Impossible to load express statuses for visa wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.MultiCurrency -> {
|
||||
Timber.w("Impossible to load express statuses for multi-currency wallet")
|
||||
prevState
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.nft.models.*
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.nft.models.NFTCollection
|
||||
import com.tangem.domain.nft.models.NFTCollections
|
||||
import com.tangem.domain.nft.models.allLoadedCollectionsEmpty
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNFTItemUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
|
@ -21,11 +23,8 @@ internal class SetNFTCollectionsTransformer(
|
|||
},
|
||||
)
|
||||
is WalletState.SingleCurrency.Content,
|
||||
is WalletState.Visa.Content,
|
||||
is WalletState.MultiCurrency.Locked,
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
-> prevState
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,10 +24,6 @@ internal class SetPrimaryCurrencyTransformer(
|
|||
marketPriceBlockState = prevState.marketPriceBlockState.toLoadedState(),
|
||||
)
|
||||
}
|
||||
is WalletState.Visa -> {
|
||||
Timber.w("Impossible to load primary currency status for VISA wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.SingleCurrency.Locked -> {
|
||||
Timber.w("Impossible to load primary currency status for locked wallet")
|
||||
prevState
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBlockState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.mutate
|
||||
|
||||
|
|
@ -26,17 +27,8 @@ internal class SetRefreshStateTransformer(
|
|||
buttons = prevState.buttons.toUpdatedState(),
|
||||
)
|
||||
}
|
||||
is WalletState.Visa.Content -> {
|
||||
prevState.copy(
|
||||
buttons = prevState.buttons.toUpdatedState(),
|
||||
pullToRefreshConfig = prevState.pullToRefreshConfig.toUpdatedState(isRefreshing),
|
||||
balancesAndLimitBlockState = prevState.balancesAndLimitBlockState.toUpdatedState(isRefreshing),
|
||||
)
|
||||
}
|
||||
is WalletState.MultiCurrency.Locked,
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
-> prevState
|
||||
}
|
||||
}
|
||||
|
|
@ -73,13 +65,4 @@ internal class SetRefreshStateTransformer(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BalancesAndLimitsBlockState.toUpdatedState(isRefreshing: Boolean): BalancesAndLimitsBlockState {
|
||||
return when (this) {
|
||||
is BalancesAndLimitsBlockState.Content -> copy(isEnabled = !isRefreshing)
|
||||
is BalancesAndLimitsBlockState.Error,
|
||||
is BalancesAndLimitsBlockState.Loading,
|
||||
-> this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,8 @@ import com.tangem.core.ui.format.bigdecimal.fiat
|
|||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.card.common.util.getCardsCount
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
|
|
@ -43,10 +43,6 @@ internal class SetTokenListErrorTransformer(
|
|||
Timber.w("Impossible to load tokens list for single-currency wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.Visa -> {
|
||||
Timber.w("Impossible to load tokens list for VISA wallet")
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
is TokenListError.DataError,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ internal class SetTokenListTransformer(
|
|||
Timber.w("Impossible to load tokens list for locked wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.Visa,
|
||||
is WalletState.SingleCurrency,
|
||||
-> {
|
||||
Timber.w("Impossible to load tokens list for single-currency wallet")
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
|||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.txhistory.models.TxHistoryStateError
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.txhistory.models.TxHistoryStateError
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemStateConverter
|
||||
|
|
@ -38,15 +38,9 @@ internal class SetTxHistoryCountErrorTransformer(
|
|||
override fun transform(prevState: WalletState): WalletState {
|
||||
return when (prevState) {
|
||||
is WalletState.SingleCurrency.Content -> prevState.copy(txHistoryState = createErrorState())
|
||||
is WalletState.Visa.Content -> prevState.copy(txHistoryState = createErrorState())
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
is WalletState.MultiCurrency,
|
||||
-> {
|
||||
Timber.w("Impossible to load transactions history for locked wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.MultiCurrency -> {
|
||||
Timber.w("Impossible to load transactions history for multi-currency wallet")
|
||||
prevState
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import androidx.paging.PagingData
|
|||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import timber.log.Timber
|
||||
|
|
@ -21,12 +21,7 @@ internal class SetTxHistoryCountTransformer(
|
|||
is WalletState.SingleCurrency.Content -> prevState.copy(
|
||||
txHistoryState = prevState.txHistoryState.toLoadingState(),
|
||||
)
|
||||
is WalletState.Visa.Content -> prevState.copy(
|
||||
txHistoryState = prevState.txHistoryState.toLoadingState(),
|
||||
)
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
-> {
|
||||
Timber.w("Impossible to load transactions history for locked wallet")
|
||||
prevState
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.domain.visa.exception.RefreshTokenExpiredException
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import timber.log.Timber
|
||||
|
||||
internal class SetTxHistoryItemsErrorTransformer(
|
||||
|
|
@ -17,11 +16,7 @@ internal class SetTxHistoryItemsErrorTransformer(
|
|||
override fun transform(prevState: WalletState): WalletState {
|
||||
return when (prevState) {
|
||||
is WalletState.SingleCurrency.Content -> prevState.copy(txHistoryState = createErrorState())
|
||||
is WalletState.Visa.Content -> transformVisaContent(prevState)
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
-> {
|
||||
is WalletState.SingleCurrency.Locked -> {
|
||||
Timber.w("Impossible to load transactions history for locked wallet")
|
||||
prevState
|
||||
}
|
||||
|
|
@ -32,20 +27,6 @@ internal class SetTxHistoryItemsErrorTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
private fun transformVisaContent(prevState: WalletState.Visa.Content): WalletState {
|
||||
return if (error.cause is RefreshTokenExpiredException) {
|
||||
WalletState.Visa.AccessTokenLocked(
|
||||
walletCardState = prevState.walletCardState,
|
||||
buttons = prevState.buttons,
|
||||
bottomSheetConfig = prevState.bottomSheetConfig,
|
||||
onExploreClick = clickIntents::onExploreClick,
|
||||
onUnlockVisaAccessNotificationClick = clickIntents::onUnlockVisaAccessClick,
|
||||
)
|
||||
} else {
|
||||
prevState.copy(txHistoryState = createErrorState())
|
||||
}
|
||||
}
|
||||
|
||||
private fun createErrorState(): TxHistoryState.Error = when (error) {
|
||||
is TxHistoryListError.DataError -> {
|
||||
TxHistoryState.Error(
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import androidx.paging.PagingData
|
|||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemFlowConverter
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
@ -21,13 +21,7 @@ internal class SetTxHistoryItemsTransformer(
|
|||
is WalletState.SingleCurrency.Content -> prevState.copy(
|
||||
txHistoryState = prevState.txHistoryState.toContentState(),
|
||||
)
|
||||
is WalletState.Visa.Content -> prevState.copy(
|
||||
txHistoryState = prevState.txHistoryState.toContentState(),
|
||||
)
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
-> {
|
||||
is WalletState.SingleCurrency.Locked -> {
|
||||
Timber.w("Impossible to load transactions history for locked wallet")
|
||||
prevState
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,173 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent.Companion.VISA_TYPE
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.card.common.util.getCardsCount
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.visa.exception.RefreshTokenExpiredException
|
||||
import com.tangem.domain.visa.model.VisaCurrency
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.utils.extensions.isZero
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.Days
|
||||
|
||||
internal class SetVisaInfoTransformer(
|
||||
private val userWallet: UserWallet.Cold,
|
||||
private val maybeVisaCurrency: Either<Throwable, VisaCurrency>,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : TypedWalletStateTransformer<WalletState.Visa.Content>(
|
||||
userWalletId = userWallet.walletId,
|
||||
targetStateClass = WalletState.Visa.Content::class,
|
||||
) {
|
||||
|
||||
override fun transformTyped(prevState: WalletState.Visa.Content): WalletState {
|
||||
val visaCurrency = maybeVisaCurrency.getOrElse {
|
||||
if (it is RefreshTokenExpiredException) {
|
||||
return getRefreshTokenExpiredState(prevState)
|
||||
}
|
||||
|
||||
return prevState.copy(
|
||||
buttons = createVisaButtonsDimmed(),
|
||||
walletCardState = getErrorWalletCardState(prevState.walletCardState),
|
||||
balancesAndLimitBlockState = BalancesAndLimitsBlockState.Error,
|
||||
)
|
||||
}
|
||||
|
||||
return prevState.copy(
|
||||
buttons = createVisaButtons(visaCurrency = visaCurrency),
|
||||
walletCardState = getContentWalletCardState(prevState.walletCardState, visaCurrency),
|
||||
balancesAndLimitBlockState = getContentBlockState(visaCurrency),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getContentBlockState(visaCurrency: VisaCurrency) = BalancesAndLimitsBlockState.Content(
|
||||
availableBalance = visaCurrency.limits.remainingOtp.format {
|
||||
crypto(visaCurrency.symbol, visaCurrency.decimals)
|
||||
},
|
||||
limitDays = Days.daysBetween(DateTime.now(), visaCurrency.limits.expirationDate).days.inc(),
|
||||
isEnabled = true,
|
||||
onClick = clickIntents::onBalancesAndLimitsClick,
|
||||
)
|
||||
|
||||
private fun getErrorWalletCardState(prevState: WalletCardState): WalletCardState {
|
||||
return with(prevState) {
|
||||
WalletCardState.Error(
|
||||
id = id,
|
||||
title = title,
|
||||
imageResId = imageResId,
|
||||
dropDownItems = dropDownItems,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getContentWalletCardState(prevState: WalletCardState, visaCurrency: VisaCurrency): WalletCardState {
|
||||
return with(prevState) {
|
||||
WalletCardState.Content(
|
||||
id = id,
|
||||
title = title,
|
||||
additionalInfo = createAdditionalInfo(visaCurrency),
|
||||
imageResId = imageResId,
|
||||
dropDownItems = dropDownItems,
|
||||
balance = visaCurrency.balances.available.format {
|
||||
crypto(visaCurrency.symbol, visaCurrency.decimals)
|
||||
},
|
||||
cardCount = userWallet.getCardsCount(),
|
||||
isZeroBalance = visaCurrency.balances.available.isZero(),
|
||||
isBalanceFlickering = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAdditionalInfo(visaCurrency: VisaCurrency): WalletAdditionalInfo {
|
||||
val fiatAmount = visaCurrency.fiatRate?.let { visaCurrency.balances.available.multiply(it) }
|
||||
.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = visaCurrency.fiatCurrency.code,
|
||||
fiatCurrencySymbol = visaCurrency.fiatCurrency.symbol,
|
||||
)
|
||||
}
|
||||
|
||||
val infoContent = stringReference(
|
||||
value = buildString {
|
||||
append(fiatAmount)
|
||||
append(" • ")
|
||||
append(visaCurrency.networkName)
|
||||
},
|
||||
)
|
||||
|
||||
return WalletAdditionalInfo(
|
||||
hideable = true,
|
||||
content = infoContent,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getRefreshTokenExpiredState(prevState: WalletState.Visa.Content): WalletState {
|
||||
return WalletState.Visa.AccessTokenLocked(
|
||||
walletCardState = prevState.walletCardState,
|
||||
buttons = prevState.buttons,
|
||||
bottomSheetConfig = prevState.bottomSheetConfig,
|
||||
onExploreClick = clickIntents::onExploreClick,
|
||||
onUnlockVisaAccessNotificationClick = clickIntents::onUnlockVisaAccessClick,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createVisaButtonsDimmed(): PersistentList<WalletManageButton> {
|
||||
return persistentListOf(
|
||||
WalletManageButton.Receive(enabled = true, dimContent = true, onClick = {}, onLongClick = null),
|
||||
WalletManageButton.Buy(enabled = true, dimContent = true, onClick = {}),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createVisaButtons(visaCurrency: VisaCurrency): PersistentList<WalletManageButton> {
|
||||
// [Second Visa Iteration] Make VisaCurrency contain CryptoCurrencyStatus
|
||||
val cryptoCurrencyStatus = CryptoCurrencyStatus(
|
||||
currency = visaCurrency.cryptoCurrency,
|
||||
value = CryptoCurrencyStatus.Loaded(
|
||||
amount = visaCurrency.balances.available,
|
||||
fiatAmount = visaCurrency.balances.available.multiply(visaCurrency.fiatRate),
|
||||
fiatRate = visaCurrency.fiatRate,
|
||||
priceChange = visaCurrency.priceChange,
|
||||
stakingBalance = null,
|
||||
yieldSupplyStatus = null,
|
||||
hasCurrentNetworkTransactions = false,
|
||||
pendingTransactions = emptySet(),
|
||||
networkAddress = visaCurrency.paymentAccountAddress,
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
),
|
||||
)
|
||||
|
||||
return persistentListOf(
|
||||
WalletManageButton.Receive(
|
||||
enabled = true,
|
||||
dimContent = false,
|
||||
onClick = {
|
||||
clickIntents.onReceiveClick(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
event = MainScreenAnalyticsEvent.ButtonReceive(),
|
||||
)
|
||||
},
|
||||
onLongClick = {
|
||||
clickIntents.onCopyAddressLongClick(cryptoCurrencyStatus)
|
||||
},
|
||||
),
|
||||
WalletManageButton.Buy(
|
||||
enabled = true,
|
||||
dimContent = false,
|
||||
onClick = {
|
||||
clickIntents.onMultiWalletBuyClick(userWalletId = userWallet.walletId, screenType = VISA_TYPE)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -33,11 +33,6 @@ internal class SetWalletCardDropDownItemsTransformer(
|
|||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
),
|
||||
)
|
||||
is WalletState.Visa.Content -> prevState.copy(
|
||||
walletCardState = prevState.walletCardState.copySealed(
|
||||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
),
|
||||
)
|
||||
is WalletState.MultiCurrency.Locked -> prevState.copy(
|
||||
walletCardState = prevState.walletCardState.copySealed(
|
||||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
|
|
@ -48,16 +43,6 @@ internal class SetWalletCardDropDownItemsTransformer(
|
|||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
),
|
||||
)
|
||||
is WalletState.Visa.Locked -> prevState.copy(
|
||||
walletCardState = prevState.walletCardState.copySealed(
|
||||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
),
|
||||
)
|
||||
is WalletState.Visa.AccessTokenLocked -> prevState.copy(
|
||||
walletCardState = prevState.walletCardState.copySealed(
|
||||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,8 @@ internal class SetWarningsTransformer(
|
|||
return when (prevState) {
|
||||
is WalletState.MultiCurrency.Content -> prevState.copy(warnings = warnings)
|
||||
is WalletState.SingleCurrency.Content -> prevState.copy(warnings = warnings)
|
||||
is WalletState.Visa.Content -> prevState.copy(warnings = warnings)
|
||||
is WalletState.MultiCurrency.Locked,
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
-> {
|
||||
Timber.w("Impossible to update notifications for locked wallet")
|
||||
prevState
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import com.tangem.feature.wallet.child.wallet.model.intents.TangemPayIntents
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.domain.pay.model.CustomerInfo.KycStatus.APPROVED
|
||||
import com.tangem.domain.pay.model.CustomerInfo
|
||||
import java.util.Currency
|
||||
|
||||
/**
|
||||
|
|
@ -43,11 +45,13 @@ internal class TangemPayUpdateInfoStateTransformer(
|
|||
private fun createInitialState(): TangemPayState {
|
||||
val cardInfo = value.info.cardInfo
|
||||
val productInstance = value.info.productInstance
|
||||
val customerId = value.info.customerId
|
||||
|
||||
// when statement copied to WalletTangemPayAnalyticsEventSender. Be careful when editing.
|
||||
return when {
|
||||
value.orderStatus == OrderStatus.CANCELED -> createCancelledState()
|
||||
!value.info.isKycApproved -> createKycInProgressState()
|
||||
value.info.kycStatus != APPROVED && !customerId.isNullOrEmpty() ->
|
||||
createKycInProgressState(kycStatus = value.info.kycStatus, customerId = customerId)
|
||||
cardInfo != null && productInstance != null -> getCardInfoState(cardInfo, productInstance)
|
||||
else -> createIssueProgressState()
|
||||
}
|
||||
|
|
@ -80,13 +84,25 @@ internal class TangemPayUpdateInfoStateTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
private fun createKycInProgressState(): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
description = TextReference.Res(R.string.tangempay_kyc_in_progress),
|
||||
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
|
||||
iconRes = R.drawable.ic_promo_kyc_36,
|
||||
onButtonClick = { tangemPayClickIntents.onKycProgressClicked(userWalletId) },
|
||||
)
|
||||
private fun createKycInProgressState(kycStatus: CustomerInfo.KycStatus, customerId: String): TangemPayState =
|
||||
Progress(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
description = when (kycStatus) {
|
||||
CustomerInfo.KycStatus.REJECTED -> TextReference.Res(R.string.tangempay_kyc_has_failed)
|
||||
else -> TextReference.Res(R.string.tangempay_kyc_in_progress)
|
||||
},
|
||||
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
|
||||
iconRes = R.drawable.ic_promo_kyc_36,
|
||||
onButtonClick = {
|
||||
when (kycStatus) {
|
||||
CustomerInfo.KycStatus.REJECTED -> tangemPayClickIntents.onKycRejectedClicked(
|
||||
userWalletId = userWalletId,
|
||||
customerId = customerId,
|
||||
)
|
||||
else -> tangemPayClickIntents.onKycProgressClicked(userWalletId)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
private fun createIssueProgressState(): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
@ -42,14 +42,11 @@ internal class UnlockWalletTransformer(
|
|||
return when (prevState) {
|
||||
is WalletState.MultiCurrency.Locked,
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
-> walletLoadingStateFactory.create(
|
||||
userWallet = unlockedWallet,
|
||||
)
|
||||
is WalletState.MultiCurrency.Content,
|
||||
is WalletState.SingleCurrency.Content,
|
||||
is WalletState.Visa.Content,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
-> {
|
||||
Timber.e("Impossible to unlock wallet with not locked state")
|
||||
prevState
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import timber.log.Timber
|
||||
|
||||
internal class UpdateMultiWalletActionsTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
private val buyStatus: Lce<Throwable, Any>,
|
||||
private val sellStatus: Lce<Throwable, Any>,
|
||||
private val swapStatus: Lce<Throwable, Any>,
|
||||
) : WalletStateTransformer(userWalletId = userWalletId) {
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return when (prevState) {
|
||||
is WalletState.MultiCurrency.Content -> {
|
||||
prevState.copy(buttons = prevState.buttons.updateButtons())
|
||||
}
|
||||
is WalletState.MultiCurrency.Locked -> {
|
||||
Timber.w("Impossible to load primary currency status for locked wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.SingleCurrency -> {
|
||||
Timber.w("Impossible to load crypto currency actions for multi-currency wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.Visa -> {
|
||||
Timber.w("Impossible to load crypto currency actions for VISA wallet")
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PersistentList<WalletManageButton>.updateButtons(): PersistentList<WalletManageButton> {
|
||||
return map {
|
||||
when (it) {
|
||||
is WalletManageButton.Buy -> {
|
||||
it.copy(
|
||||
enabled = buyStatus.isContent(),
|
||||
dimContent = !buyStatus.isContent(),
|
||||
)
|
||||
}
|
||||
is WalletManageButton.Sell -> {
|
||||
it.copy(
|
||||
enabled = sellStatus.isContent(),
|
||||
dimContent = !sellStatus.isContent(),
|
||||
)
|
||||
}
|
||||
is WalletManageButton.Swap -> {
|
||||
it.copy(
|
||||
enabled = swapStatus.isContent(),
|
||||
dimContent = !swapStatus.isContent(),
|
||||
)
|
||||
}
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
.toPersistentList()
|
||||
}
|
||||
}
|
||||
|
|
@ -21,13 +21,8 @@ internal class UpdateWalletCardsCountTransformer(
|
|||
is WalletState.SingleCurrency.Content -> {
|
||||
prevState.copy(walletCardState = prevState.walletCardState.toUpdatedState())
|
||||
}
|
||||
is WalletState.Visa.Content -> {
|
||||
prevState.copy(walletCardState = prevState.walletCardState.toUpdatedState())
|
||||
}
|
||||
is WalletState.MultiCurrency.Locked,
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
is WalletState.Visa.Locked,
|
||||
is WalletState.Visa.AccessTokenLocked,
|
||||
-> {
|
||||
Timber.e("Impossible to update wallet cards count for locked wallet")
|
||||
prevState
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.domain.visa.model.VisaCurrency
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class BalancesAndLimitsBottomSheetConverter(
|
||||
private val eventSender: WalletEventSender,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Converter<VisaCurrency, BalancesAndLimitsBottomSheetConfig> {
|
||||
|
||||
override fun convert(value: VisaCurrency): BalancesAndLimitsBottomSheetConfig {
|
||||
fun formatAmount(amount: BigDecimal): String = amount.format { crypto(value.symbol, value.decimals) }
|
||||
|
||||
val otpLimit = value.limits.remainingOtp.let(::formatAmount)
|
||||
val noOtpLimit = value.limits.remainingNoOtp.let(::formatAmount)
|
||||
|
||||
return BalancesAndLimitsBottomSheetConfig(
|
||||
balance = BalancesAndLimitsBottomSheetConfig.Balance(
|
||||
totalBalance = value.balances.total.let(::formatAmount),
|
||||
availableBalance = value.balances.available.let(::formatAmount),
|
||||
blockedBalance = value.balances.blocked.let(::formatAmount),
|
||||
debit = value.balances.debt.let(::formatAmount),
|
||||
amlVerified = value.balances.verified.let(::formatAmount),
|
||||
onInfoClick = this::balanceInfoOnClick,
|
||||
),
|
||||
limit = BalancesAndLimitsBottomSheetConfig.Limit(
|
||||
availableBy = DateTimeFormatters.formatDate(date = value.limits.expirationDate),
|
||||
total = otpLimit,
|
||||
other = noOtpLimit,
|
||||
singleTransaction = value.limits.singleTransaction.let(::formatAmount),
|
||||
onInfoClick = { limitInfoOnClick(otpLimit, noOtpLimit) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun balanceInfoOnClick() {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.NoticeBalancesInfo())
|
||||
eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaBalancesInfo))
|
||||
}
|
||||
|
||||
private fun limitInfoOnClick(totalLimit: String, otherLimit: String) {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.NoticeLimitsInfo())
|
||||
eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaLimitsInfo(totalLimit, otherLimit)))
|
||||
}
|
||||
}
|
||||
|
|
@ -43,7 +43,7 @@ internal class TokenListStateConverter(
|
|||
private val shouldShowMainPromo: Boolean,
|
||||
) : Converter<WalletTokensListState, WalletTokensListState> {
|
||||
|
||||
private val yieldSupplyPromoBannerKeyConverter = YieldSupplyPromoBannerKeyConverter(
|
||||
private val yieldSupplyPromoBannerConverter = YieldSupplyPromoBannerConverter(
|
||||
yieldModuleApyMap,
|
||||
shouldShowMainPromo,
|
||||
)
|
||||
|
|
@ -72,7 +72,7 @@ internal class TokenListStateConverter(
|
|||
private fun tokenStatusConverter(accountId: AccountId? = null) = TokenItemStateConverter(
|
||||
appCurrency = appCurrency,
|
||||
yieldModuleApyMap = yieldModuleApyMap,
|
||||
yieldSupplyPromoBannerKey = yieldSupplyPromoBannerKeyConverter.convert(params),
|
||||
promoCryptoCurrencyStatus = yieldSupplyPromoBannerConverter.convert(params),
|
||||
stakingApyMap = stakingAvailabilityMap,
|
||||
onItemClick = { _, status -> onTokenClick(accountId, status) },
|
||||
onItemLongClick = { _, status -> onTokenLongClick(accountId, status) },
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ internal class TxHistoryItemStateConverter(
|
|||
is TransactionType.UnknownOperation,
|
||||
is TransactionType.YieldSupply.Send,
|
||||
TransactionType.YieldSupply.Topup,
|
||||
TransactionType.GaslessFee,
|
||||
-> if (isOutgoing) R.drawable.ic_arrow_up_24 else R.drawable.ic_arrow_down_24
|
||||
}
|
||||
}
|
||||
|
|
@ -114,6 +115,7 @@ internal class TxHistoryItemStateConverter(
|
|||
)
|
||||
}
|
||||
is TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation)
|
||||
TransactionType.GaslessFee -> resourceReference(R.string.gasless_transaction_fee)
|
||||
}
|
||||
|
||||
private fun TxInfo.extractSubtitle(): TextReference {
|
||||
|
|
|
|||
|
|
@ -1,85 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||
|
||||
import com.tangem.core.ui.extensions.capitalize
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.domain.visa.model.VisaCurrency
|
||||
import com.tangem.domain.visa.model.VisaTxDetails
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.VisaWalletIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.VisaTxDetailsBottomSheetConfig
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import org.joda.time.DateTimeZone
|
||||
import java.math.BigDecimal
|
||||
import java.util.Currency
|
||||
|
||||
internal class VisaTxDetailsBottomSheetConverter(
|
||||
private val visaCurrency: VisaCurrency,
|
||||
private val clickIntents: VisaWalletIntents,
|
||||
) : Converter<VisaTxDetails, VisaTxDetailsBottomSheetConfig> {
|
||||
|
||||
override fun convert(value: VisaTxDetails): VisaTxDetailsBottomSheetConfig {
|
||||
return VisaTxDetailsBottomSheetConfig(
|
||||
transaction = createTransaction(value),
|
||||
requests = value.requests.map(::createRequest).toImmutableList(),
|
||||
onDisputeClick = { clickIntents.onDisputeClick(value) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun createTransaction(details: VisaTxDetails): VisaTxDetailsBottomSheetConfig.Transaction {
|
||||
return VisaTxDetailsBottomSheetConfig.Transaction(
|
||||
id = details.id,
|
||||
type = details.type.capitalize(),
|
||||
status = details.status.capitalize(),
|
||||
blockchainAmount = formatNetworkAmount(details.blockchainAmount),
|
||||
transactionAmount = formatFiatAmount(details.transactionAmount, details.fiatCurrency),
|
||||
transactionCurrencyCode = details.transactionCurrencyCode.toString(),
|
||||
merchantName = details.merchantName?.capitalize() ?: UNKNOWN,
|
||||
merchantCity = details.merchantCity?.capitalize() ?: UNKNOWN,
|
||||
merchantCountryCode = details.merchantCountryCode ?: UNKNOWN,
|
||||
merchantCategoryCode = details.merchantCategoryCode ?: UNKNOWN,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createRequest(request: VisaTxDetails.Request): VisaTxDetailsBottomSheetConfig.Request {
|
||||
val localDate = request.requestDate.withZone(DateTimeZone.getDefault())
|
||||
val exploreUrl = request.exploreUrl
|
||||
|
||||
return VisaTxDetailsBottomSheetConfig.Request(
|
||||
id = request.id,
|
||||
type = request.requestType.capitalize(),
|
||||
status = request.requestStatus.capitalize(),
|
||||
blockchainAmount = formatNetworkAmount(request.blockchainAmount),
|
||||
transactionAmount = formatFiatAmount(request.transactionAmount, request.fiatCurrency),
|
||||
currencyCode = request.billingCurrencyCode.toString(),
|
||||
errorCode = request.errorCode,
|
||||
date = DateTimeFormatters.formatDate(localDate, DateTimeFormatters.dateTimeFormatter),
|
||||
txHash = request.txHash ?: UNKNOWN,
|
||||
txStatus = request.txStatus?.capitalize() ?: UNKNOWN,
|
||||
onExploreClick = if (exploreUrl != null) {
|
||||
{ clickIntents.onExploreClick(exploreUrl) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun formatNetworkAmount(amount: BigDecimal): String {
|
||||
return amount.format { crypto(visaCurrency.symbol, visaCurrency.decimals) }
|
||||
}
|
||||
|
||||
private fun formatFiatAmount(amount: BigDecimal, fiatCurrency: Currency): String {
|
||||
return amount.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = fiatCurrency.currencyCode,
|
||||
fiatCurrencySymbol = fiatCurrency.symbol,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val UNKNOWN = "Unknown"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||
|
||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.extensions.capitalize
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.domain.visa.model.VisaCurrency
|
||||
import com.tangem.domain.visa.model.VisaTxHistoryItem
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.VisaWalletIntents
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.utils.converter.Converter
|
||||
import org.joda.time.DateTimeZone
|
||||
|
||||
internal class VisaTxHistoryItemStateConverter(
|
||||
private val visaCurrency: VisaCurrency,
|
||||
private val clickIntents: VisaWalletIntents,
|
||||
) : Converter<VisaTxHistoryItem, TransactionState> {
|
||||
|
||||
override fun convert(value: VisaTxHistoryItem): TransactionState {
|
||||
val localDate = value.date.withZone(DateTimeZone.getDefault())
|
||||
val time = DateTimeFormatters.formatDate(localDate, DateTimeFormatters.timeFormatter)
|
||||
val subtitle = "$time ${StringsSigns.DOT} ${value.status.capitalize()}"
|
||||
|
||||
return TransactionState.Content(
|
||||
txHash = value.id,
|
||||
amount = value.amount.format { crypto(visaCurrency.symbol, visaCurrency.decimals) },
|
||||
// Show tx fiat amount instead of tx time
|
||||
time = value.fiatAmount.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = value.fiatCurrency.currencyCode,
|
||||
fiatCurrencySymbol = value.fiatCurrency.symbol,
|
||||
)
|
||||
},
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.INCOMING,
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference(value = value.merchantName?.capitalize() ?: "Unknown merchant"),
|
||||
subtitle = stringReference(subtitle),
|
||||
timestamp = localDate.millis,
|
||||
onClick = { clickIntents.onVisaTransactionClick(value.id) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,12 +8,12 @@ import com.tangem.lib.crypto.BlockchainUtils
|
|||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class YieldSupplyPromoBannerKeyConverter(
|
||||
internal class YieldSupplyPromoBannerConverter(
|
||||
private val yieldModuleApyMap: Map<String, BigDecimal>,
|
||||
private val shouldShowMainPromo: Boolean,
|
||||
) : Converter<TokenConverterParams, String?> {
|
||||
) : Converter<TokenConverterParams, CryptoCurrencyStatus?> {
|
||||
|
||||
override fun convert(value: TokenConverterParams): String? {
|
||||
override fun convert(value: TokenConverterParams): CryptoCurrencyStatus? {
|
||||
if (!shouldShowMainPromo) return null
|
||||
|
||||
val currencies = when (value) {
|
||||
|
|
@ -31,17 +31,17 @@ internal class YieldSupplyPromoBannerKeyConverter(
|
|||
val max = cryptoCurrencyStatuses.asSequence()
|
||||
.mapNotNull { status ->
|
||||
val token = status.currency as? CryptoCurrency.Token ?: return@mapNotNull null
|
||||
val tokenKey = token.yieldSupplyKey()
|
||||
val shouldIgnoreCase = BlockchainUtils.isCaseInsensitiveContractAddress(token.network.rawId)
|
||||
|
||||
val matchedKey = yieldModuleApyMap.keys.firstOrNull { mapKey ->
|
||||
mapKey.equals(tokenKey, shouldIgnoreCase)
|
||||
mapKey.equals(
|
||||
other = token.yieldSupplyKey(),
|
||||
ignoreCase = BlockchainUtils.isCaseInsensitiveContractAddress(token.network.rawId),
|
||||
)
|
||||
} ?: return@mapNotNull null
|
||||
|
||||
status to matchedKey
|
||||
}
|
||||
.maxByOrNull { (status, _) -> status.value.amount ?: BigDecimal.ZERO }
|
||||
|
||||
return max?.second
|
||||
return max?.first
|
||||
}
|
||||
}
|
||||
|
|
@ -7,10 +7,8 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
|||
internal inline fun UserWallet.createStateByWalletType(
|
||||
multiCurrencyCreator: () -> WalletState.MultiCurrency,
|
||||
singleCurrencyCreator: () -> WalletState.SingleCurrency,
|
||||
visaWalletCreator: () -> WalletState.Visa,
|
||||
): WalletState = when (this) {
|
||||
is UserWallet.Cold -> when {
|
||||
isVisaWallet() -> visaWalletCreator()
|
||||
isWalletWithTokens() -> multiCurrencyCreator()
|
||||
else -> singleCurrencyCreator()
|
||||
}
|
||||
|
|
@ -19,8 +17,4 @@ internal inline fun UserWallet.createStateByWalletType(
|
|||
|
||||
private fun UserWallet.Cold.isWalletWithTokens(): Boolean {
|
||||
return isMultiCurrency || scanResponse.cardTypesResolver.isSingleWalletWithToken()
|
||||
}
|
||||
|
||||
private fun UserWallet.Cold.isVisaWallet(): Boolean {
|
||||
return scanResponse.cardTypesResolver.isVisaWallet()
|
||||
}
|
||||
|
|
@ -30,7 +30,6 @@ internal class WalletLoadingStateFactory(
|
|||
userWallet.createStateByWalletType(
|
||||
multiCurrencyCreator = { createLoadingMultiCurrencyContent(userWallet) },
|
||||
singleCurrencyCreator = { createLoadingSingleCurrencyContent(userWallet) },
|
||||
visaWalletCreator = { createLoadingVisaWalletContent(userWallet) },
|
||||
)
|
||||
}
|
||||
is UserWallet.Hot -> {
|
||||
|
|
@ -92,22 +91,6 @@ internal class WalletLoadingStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
private fun createLoadingVisaWalletContent(userWallet: UserWallet.Cold): WalletState.Visa.Content {
|
||||
return WalletState.Visa.Content(
|
||||
pullToRefreshConfig = createPullToRefreshConfig(),
|
||||
walletCardState = userWallet.toLoadingWalletCardState(),
|
||||
buttons = createVisaDimmedButtons(),
|
||||
warnings = persistentListOf(),
|
||||
bottomSheetConfig = null,
|
||||
balancesAndLimitBlockState = BalancesAndLimitsBlockState.Loading,
|
||||
txHistoryState = TxHistoryState.Content(
|
||||
contentItems = MutableStateFlow(
|
||||
value = TxHistoryState.getDefaultLoadingTransactions(clickIntents::onExploreClick),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createPullToRefreshConfig(): PullToRefreshConfig {
|
||||
return PullToRefreshConfig(
|
||||
onRefresh = { clickIntents.onRefreshSwipe(it.value) },
|
||||
|
|
@ -154,18 +137,6 @@ internal class WalletLoadingStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
private fun createVisaDimmedButtons(): PersistentList<WalletManageButton> {
|
||||
return persistentListOf(
|
||||
WalletManageButton.Receive(
|
||||
enabled = true,
|
||||
dimContent = true,
|
||||
onClick = {},
|
||||
onLongClick = null,
|
||||
),
|
||||
WalletManageButton.Buy(enabled = true, dimContent = true, onClick = {}),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createDimmedButtons(): PersistentList<WalletManageButton> {
|
||||
return persistentListOf(
|
||||
WalletManageButton.Receive(
|
||||
|
|
|
|||
|
|
@ -1,21 +1,25 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.usecase.StakingAvailabilityListUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetShouldShowMainPromoUseCase
|
||||
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 com.tangem.utils.coroutines.combine7
|
||||
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
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
|
|
@ -35,28 +39,40 @@ internal class AccountListSubscriber @AssistedInject constructor(
|
|||
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
|
||||
) : BasicAccountListSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> = combine6(
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> = combine7(
|
||||
flow1 = getAccountStatusListFlow(),
|
||||
flow2 = getAppCurrencyFlow(),
|
||||
flow3 = accountDependencies.expandedAccountsHolder.expandedAccounts(userWallet),
|
||||
flow4 = accountDependencies.isAccountsModeEnabledUseCase(),
|
||||
flow5 = yieldSupplyApyFlow(),
|
||||
flow6 = yieldSupplyGetShouldShowMainPromoFlow(),
|
||||
) { accountList, appCurrency, expandedAccounts, isAccountMode, yieldSupplyApyMap, shouldShowMainPromo ->
|
||||
flow7 = stakingAvailabilityFlow(),
|
||||
) {
|
||||
accountList, appCurrency, expandedAccounts, isAccountMode,
|
||||
yieldSupplyApyMap, shouldShowMainPromo, stakingAvailabilityMap,
|
||||
->
|
||||
updateState(
|
||||
accountList = accountList,
|
||||
appCurrency = appCurrency,
|
||||
expandedAccounts = expandedAccounts,
|
||||
isAccountMode = isAccountMode,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingAvailabilityMap = stakingAvailabilityListUseCase.invokeSync(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrencyList = accountList.flattenCurrencies().map(CryptoCurrencyStatus::currency),
|
||||
),
|
||||
stakingAvailabilityMap = stakingAvailabilityMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
)
|
||||
}
|
||||
|
||||
private fun stakingAvailabilityFlow(): Flow<Map<CryptoCurrency, StakingAvailability>> = getAccountStatusListFlow()
|
||||
.map { accountList -> accountList.flattenCurrencies().map(CryptoCurrencyStatus::currency) }
|
||||
.distinctUntilChanged()
|
||||
.mapLatest { flattenCurrencies ->
|
||||
stakingAvailabilityListUseCase.invokeSync(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrencyList = flattenCurrencies,
|
||||
)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
private fun yieldSupplyApyFlow(): Flow<Map<String, BigDecimal>> {
|
||||
return yieldSupplyApyFlowUseCase().distinctUntilChanged()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,106 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import androidx.paging.map
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.domain.visa.GetVisaCurrencyUseCase
|
||||
import com.tangem.domain.visa.GetVisaTxHistoryUseCase
|
||||
import com.tangem.domain.visa.model.VisaCurrency
|
||||
import com.tangem.domain.visa.model.VisaTxHistoryItem
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
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.SetVisaInfoTransformer
|
||||
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.VisaTxHistoryItemStateConverter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import timber.log.Timber
|
||||
|
||||
internal class VisaWalletSubscriber(
|
||||
private val userWallet: UserWallet.Cold,
|
||||
private val stateController: WalletStateController,
|
||||
private val isRefresh: Boolean,
|
||||
private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase,
|
||||
private val getVisaTxHistoryUseCase: GetVisaTxHistoryUseCase,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
return flow<Any> {
|
||||
setLoadingTxHistoryState()
|
||||
|
||||
val maybeCurrency = getVisaCurrencyUseCase(userWallet.walletId, isRefresh)
|
||||
setLoadedCurrencyState(maybeCurrency)
|
||||
|
||||
val currency = maybeCurrency.getOrElse {
|
||||
Timber.e(it, "Failed to load VISA currency")
|
||||
setFailedTxHistoryState(it)
|
||||
return@flow
|
||||
}
|
||||
val txHistoryItemsFlow = getVisaTxHistoryUseCase(userWallet.walletId, isRefresh = isRefresh)
|
||||
.map { maybeTxHistoryItems ->
|
||||
maybeTxHistoryItems.getOrElse {
|
||||
Timber.e(it, "Failed to load tx history for wallet ${userWallet.walletId}")
|
||||
throw it
|
||||
}
|
||||
}
|
||||
.catch { setFailedTxHistoryState(it) }
|
||||
.cachedIn(coroutineScope)
|
||||
|
||||
setLoadedTxHistoryState(txHistoryItemsFlow, currency)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLoadedCurrencyState(maybeCurrency: Either<Throwable, VisaCurrency>) {
|
||||
stateController.update(
|
||||
SetVisaInfoTransformer(
|
||||
userWallet = userWallet,
|
||||
maybeVisaCurrency = maybeCurrency,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun setLoadingTxHistoryState() {
|
||||
stateController.update(
|
||||
SetTxHistoryCountTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
transactionsCount = 10,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun setFailedTxHistoryState(it: Throwable) {
|
||||
stateController.update(
|
||||
SetTxHistoryItemsErrorTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
error = TxHistoryListError.DataError(it),
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun setLoadedTxHistoryState(itemsFlow: Flow<PagingData<VisaTxHistoryItem>>, currency: VisaCurrency) {
|
||||
val itemConverter = VisaTxHistoryItemStateConverter(currency, clickIntents)
|
||||
|
||||
stateController.update(
|
||||
SetTxHistoryItemsTransformer(
|
||||
userWallet = userWallet,
|
||||
flow = itemsFlow.map { items ->
|
||||
items.map(itemConverter::convert)
|
||||
},
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -17,11 +17,11 @@ internal abstract class WalletSubscriber {
|
|||
|
||||
protected abstract fun create(coroutineScope: CoroutineScope): Flow<*>
|
||||
|
||||
fun subscribe(coroutineScope: CoroutineScope, dispatchers: CoroutineDispatcherProvider): Job {
|
||||
fun subscribe(coroutineScope: CoroutineScope, dispatchers: CoroutineDispatcher): Job {
|
||||
Timber.d("Subscribe on ${this::class.simpleName}")
|
||||
|
||||
return create(coroutineScope)
|
||||
.flowOn(dispatchers.main)
|
||||
.flowOn(dispatchers)
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ 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
|
||||
|
|
@ -43,8 +42,6 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheet
|
||||
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetConfig
|
||||
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
|
||||
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheet
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.expressTransactionsItems
|
||||
|
|
@ -57,7 +54,6 @@ 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
|
||||
|
|
@ -67,6 +63,7 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
import com.tangem.core.ui.test.MarketTooltipTestTags
|
||||
import com.tangem.core.ui.utils.TangemSharedTransitionLayout
|
||||
import com.tangem.core.ui.utils.lineTo
|
||||
import com.tangem.core.ui.utils.moveTo
|
||||
import com.tangem.core.ui.utils.toPx
|
||||
|
|
@ -79,14 +76,10 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistor
|
|||
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
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.BalancesAndLimitsBottomSheet
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.TangemPayMainScreenBlock
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.VisaTxDetailsBottomSheet
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.balancesAndLimitsBlock
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -154,7 +147,6 @@ 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()
|
||||
|
||||
|
|
@ -187,94 +179,81 @@ private fun WalletContent(
|
|||
)
|
||||
} ?: PaddingValues(bottom = TangemTheme.dimens.spacing92 + bottomBarHeight)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.testTag(MainScreenTestTags.SCREEN_CONTAINER),
|
||||
state = listState,
|
||||
contentPadding = contentPadding,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
item(
|
||||
// !!! Type of the key should be saveable via Bundle on Android !!!
|
||||
key = state.wallets.map { it.walletCardState.id.stringValue },
|
||||
contentType = state.wallets.map { it.walletCardState.id },
|
||||
TangemSharedTransitionLayout {
|
||||
LazyColumn(
|
||||
modifier = Modifier.testTag(MainScreenTestTags.SCREEN_CONTAINER),
|
||||
state = listState,
|
||||
contentPadding = contentPadding,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
WalletsList(
|
||||
lazyListState = walletsListState,
|
||||
wallets = state.wallets.map(WalletState::walletCardState).toImmutableList(),
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
)
|
||||
}
|
||||
|
||||
when (selectedWallet) {
|
||||
is WalletState.MultiCurrency,
|
||||
is WalletState.Visa,
|
||||
-> {
|
||||
actions(
|
||||
actions = selectedWallet.buttons,
|
||||
selectedWalletIndex = selectedWalletIndex,
|
||||
modifier = movableItemModifier.padding(top = betweenItemsPadding),
|
||||
)
|
||||
}
|
||||
is WalletState.SingleCurrency -> {
|
||||
lazyActions(
|
||||
actions = selectedWallet.buttons,
|
||||
selectedWalletIndex = selectedWalletIndex,
|
||||
modifier = movableItemModifier.padding(top = betweenItemsPadding),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
notifications(configs = selectedWallet.warnings, modifier = itemModifier)
|
||||
|
||||
if (selectedWallet is WalletState.MultiCurrency) {
|
||||
item(
|
||||
key = "TangemPayMainScreenBlock",
|
||||
contentType = selectedWallet.tangemPayState::class.java,
|
||||
// !!! Type of the key should be saveable via Bundle on Android !!!
|
||||
key = state.wallets.map { it.walletCardState.id.stringValue },
|
||||
contentType = state.wallets.map { it.walletCardState.id },
|
||||
) {
|
||||
TangemPayMainScreenBlock(
|
||||
state = selectedWallet.tangemPayState,
|
||||
WalletsList(
|
||||
modifier = Modifier.animateItem(fadeInSpec = null, fadeOutSpec = null),
|
||||
lazyListState = walletsListState,
|
||||
wallets = state.wallets.map(WalletState::walletCardState).toImmutableList(),
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
(selectedWallet as? WalletState.SingleCurrency)?.let { walletState ->
|
||||
walletState.marketPriceBlockState?.let { marketPriceBlockState ->
|
||||
marketPriceBlock(state = marketPriceBlockState, modifier = itemModifier)
|
||||
when (selectedWallet) {
|
||||
is WalletState.MultiCurrency -> {
|
||||
actions(
|
||||
actions = selectedWallet.buttons,
|
||||
selectedWalletIndex = selectedWalletIndex,
|
||||
modifier = movableItemModifier.padding(top = betweenItemsPadding),
|
||||
)
|
||||
}
|
||||
is WalletState.SingleCurrency -> {
|
||||
lazyActions(
|
||||
actions = selectedWallet.buttons,
|
||||
selectedWalletIndex = selectedWalletIndex,
|
||||
modifier = movableItemModifier.padding(top = betweenItemsPadding),
|
||||
)
|
||||
}
|
||||
}
|
||||
if (walletState is WalletState.SingleCurrency.Content) {
|
||||
expressTransactionsItems(
|
||||
expressTxs = walletState.expressTxsToDisplay,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
(selectedWallet as? WalletState.Visa.Content)?.let {
|
||||
balancesAndLimitsBlock(
|
||||
modifier = itemModifier,
|
||||
state = it.balancesAndLimitBlockState,
|
||||
notifications(configs = selectedWallet.warnings, modifier = itemModifier)
|
||||
|
||||
if (selectedWallet is WalletState.MultiCurrency) {
|
||||
item(
|
||||
key = "TangemPayMainScreenBlock",
|
||||
contentType = selectedWallet.tangemPayState::class.java,
|
||||
) {
|
||||
TangemPayMainScreenBlock(
|
||||
state = selectedWallet.tangemPayState,
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
(selectedWallet as? WalletState.SingleCurrency)?.let { walletState ->
|
||||
walletState.marketPriceBlockState?.let { marketPriceBlockState ->
|
||||
marketPriceBlock(state = marketPriceBlockState, modifier = itemModifier)
|
||||
}
|
||||
if (walletState is WalletState.SingleCurrency.Content) {
|
||||
expressTransactionsItems(
|
||||
expressTxs = walletState.expressTxsToDisplay,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
contentItems(
|
||||
state = selectedWallet,
|
||||
txHistoryItems = txHistoryItems,
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
modifier = movableItemModifier,
|
||||
)
|
||||
|
||||
nftCollections(state = selectedWallet, itemModifier = itemModifier)
|
||||
|
||||
organizeTokens(state = selectedWallet, itemModifier = itemModifier)
|
||||
}
|
||||
|
||||
contentItems(
|
||||
state = selectedWallet,
|
||||
txHistoryItems = txHistoryItems,
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
modifier = movableItemModifier,
|
||||
portfolioVisibleState = {
|
||||
findPortfolioVisibleState(
|
||||
portfolio = it,
|
||||
expandedState = expandedState,
|
||||
collapsedState = collapsedState,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
nftCollections(state = selectedWallet, itemModifier = itemModifier)
|
||||
|
||||
organizeTokens(state = selectedWallet, itemModifier = itemModifier)
|
||||
}
|
||||
|
||||
ShowBottomSheet(bottomSheetConfig = selectedWallet.bottomSheetConfig)
|
||||
|
|
@ -307,61 +286,6 @@ 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
|
||||
|
|
@ -409,7 +333,7 @@ private inline fun BaseScaffoldWithMarkets(
|
|||
}
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalMainBottomSheetColor provides remember { mutableStateOf(background) },
|
||||
LocalMainBottomSheetColor provides remember(background) { mutableStateOf(background) },
|
||||
) {
|
||||
val backgroundColor = LocalMainBottomSheetColor.current
|
||||
var isSearchFieldFocused by remember { mutableStateOf(false) }
|
||||
|
|
@ -449,20 +373,21 @@ private inline fun BaseScaffoldWithMarkets(
|
|||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.sizeIn(maxHeight = maxHeight - statusBarHeight),
|
||||
modifier = Modifier
|
||||
// expand bottom sheet when clicked on the header
|
||||
.clickable(
|
||||
enabled = bottomSheetState.currentValue == TangemSheetValue.PartiallyExpanded,
|
||||
indication = null,
|
||||
interactionSource = null,
|
||||
) {
|
||||
coroutineScope.launch { bottomSheetState.expand() }
|
||||
}
|
||||
.sizeIn(maxHeight = maxHeight - statusBarHeight),
|
||||
) {
|
||||
Hand(Modifier.drawBehind { drawRect(backgroundColor.value) })
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
// expand bottom sheet when clicked on the header
|
||||
.clickable(
|
||||
enabled = bottomSheetState.currentValue == TangemSheetValue.PartiallyExpanded,
|
||||
indication = null,
|
||||
interactionSource = null,
|
||||
) {
|
||||
coroutineScope.launch { bottomSheetState.expand() }
|
||||
}
|
||||
.onFocusChanged {
|
||||
isSearchFieldFocused = it.isFocused
|
||||
},
|
||||
|
|
@ -492,7 +417,7 @@ private inline fun BaseScaffoldWithMarkets(
|
|||
color = if (state.showMarketsOnboarding) {
|
||||
Color.Black.copy(alpha = .65f)
|
||||
} else {
|
||||
BottomSheetDefaults.ScrimColor
|
||||
Color.Black.copy(alpha = .40f)
|
||||
},
|
||||
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded ||
|
||||
state.showMarketsOnboarding,
|
||||
|
|
@ -782,11 +707,8 @@ private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
|
|||
if (bottomSheetConfig != null) {
|
||||
when (bottomSheetConfig.content) {
|
||||
is WalletBottomSheetConfig -> WalletBottomSheet(config = bottomSheetConfig)
|
||||
is TokenReceiveBottomSheetConfig -> TokenReceiveBottomSheet(config = bottomSheetConfig)
|
||||
is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig)
|
||||
is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig)
|
||||
is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig)
|
||||
is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig)
|
||||
is ExpressStatusBottomSheetConfig -> ExpressStatusBottomSheet(config = bottomSheetConfig)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import androidx.compose.animation.core.calculateTargetValue
|
|||
import androidx.compose.animation.splineBasedDecay
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
|
||||
import androidx.compose.foundation.lazy.LazyListLayoutInfo
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.ui.unit.Density
|
||||
|
|
@ -23,7 +22,7 @@ import kotlin.math.sign
|
|||
* This position should be considered with regard to the start edge of the item and the placement
|
||||
* within the viewport.
|
||||
*
|
||||
* @return A [SnapLayoutInfoProvider] that can be used with [SnapFlingBehavior]
|
||||
* @return A [SnapLayoutInfoProvider] that can be used with snap fling behavior
|
||||
*/
|
||||
@Suppress("FunctionNaming")
|
||||
@ExperimentalFoundationApi
|
||||
|
|
|
|||
|
|
@ -18,12 +18,11 @@ import androidx.compose.runtime.derivedStateOf
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.LocalWindowSize
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
|
|
@ -48,13 +47,14 @@ internal fun WalletsList(
|
|||
lazyListState: LazyListState,
|
||||
wallets: ImmutableList<WalletCardState>,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val horizontalCardPadding = TangemTheme.dimens.spacing16
|
||||
val screenWidth = LocalConfiguration.current.screenWidthDp.dp
|
||||
val screenWidth = LocalWindowSize.current.width
|
||||
val itemWidth by remember(screenWidth) { derivedStateOf { screenWidth - horizontalCardPadding * 2 } }
|
||||
|
||||
LazyRow(
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
modifier = modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
state = lazyListState,
|
||||
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
|
|
|
|||
|
|
@ -2,12 +2,10 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common
|
|||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.components.FontSizeRange
|
||||
import com.tangem.core.ui.components.buttons.HorizontalActionChips
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
|
||||
|
|
@ -66,16 +64,10 @@ internal fun LazyListScope.actions(
|
|||
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing8),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
val fontSizeRange = FontSizeRange(min = 10.sp, max = 14.sp)
|
||||
var fontSizeValue by remember { mutableFloatStateOf(fontSizeRange.max.value) }
|
||||
|
||||
actions.fastForEach { action ->
|
||||
key(action::class.java) {
|
||||
MultiCurrencyAction(
|
||||
config = action.config,
|
||||
fontSizeValue = fontSizeValue.sp,
|
||||
fontSizeRange = fontSizeRange,
|
||||
onFontSizeChange = { fontSizeValue = it },
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ import androidx.compose.foundation.indication
|
|||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.PressInteraction
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.TextAutoSize
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
|
|
@ -32,18 +34,11 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.ConstraintLayoutScope
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import com.tangem.core.ui.components.FontSizeRange
|
||||
import androidx.compose.ui.unit.*
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.ResizableText
|
||||
import com.tangem.core.ui.components.text.applyBladeBrush
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.conditional
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemDimens
|
||||
|
|
@ -51,6 +46,7 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDropDownItems
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.fastForEach
|
||||
|
|
@ -69,89 +65,6 @@ private const val HALF_OF_ITEM_WIDTH = 0.5
|
|||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries")
|
||||
CardContainer(
|
||||
dropDownItems = state.dropDownItems,
|
||||
isLockedState = state is WalletCardState.LockedContent,
|
||||
modifier = modifier,
|
||||
) { itemSize ->
|
||||
val (titleRef, balanceRef, additionalTextRef, imageRef) = createRefs()
|
||||
|
||||
val contentVerticalMargin = TangemTheme.dimens.spacing12
|
||||
TitleText(
|
||||
text = state.title,
|
||||
modifier = Modifier.constrainAs(titleRef) {
|
||||
start.linkTo(parent.start)
|
||||
top.linkTo(anchor = parent.top, margin = contentVerticalMargin)
|
||||
end.linkTo(imageRef.start)
|
||||
width = Dimension.fillToConstraints
|
||||
},
|
||||
)
|
||||
|
||||
var balanceWidth by remember { mutableIntStateOf(value = Int.MIN_VALUE) }
|
||||
Balance(
|
||||
state = state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier
|
||||
.onSizeChanged { balanceWidth = it.width }
|
||||
.padding(vertical = TangemTheme.dimens.spacing8)
|
||||
.constrainAs(balanceRef) {
|
||||
start.linkTo(parent.start)
|
||||
top.linkTo(anchor = titleRef.bottom)
|
||||
bottom.linkTo(anchor = additionalTextRef.top)
|
||||
},
|
||||
)
|
||||
|
||||
val additionalText by remember(state.additionalInfo, isBalanceHidden) {
|
||||
mutableStateOf(
|
||||
state.additionalInfo?.content?.orMaskWithStars(
|
||||
maskWithStars = state.additionalInfo?.hideable == true && isBalanceHidden,
|
||||
),
|
||||
)
|
||||
}
|
||||
AdditionalInfo(
|
||||
text = additionalText,
|
||||
modifier = Modifier.constrainAs(additionalTextRef) {
|
||||
start.linkTo(parent.start)
|
||||
top.linkTo(balanceRef.bottom)
|
||||
bottom.linkTo(anchor = parent.bottom, margin = contentVerticalMargin)
|
||||
|
||||
if (additionalText != null) {
|
||||
width = if (state.imageResId != null) {
|
||||
end.linkTo(imageRef.start)
|
||||
Dimension.fillToConstraints
|
||||
} else {
|
||||
Dimension.wrapContent
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// If balance has a large width then image must be hidden
|
||||
val hasSpaceForImage by remember(key1 = balanceWidth, key2 = itemSize.width) {
|
||||
mutableStateOf(value = balanceWidth < itemSize.width * HALF_OF_ITEM_WIDTH)
|
||||
}
|
||||
|
||||
if (hasSpaceForImage) {
|
||||
Image(
|
||||
id = state.imageResId,
|
||||
modifier = Modifier.constrainAs(imageRef) {
|
||||
end.linkTo(parent.end)
|
||||
bottom.linkTo(parent.bottom)
|
||||
height = Dimension.fillToConstraints
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CardContainer(
|
||||
dropDownItems: ImmutableList<WalletDropDownItems>,
|
||||
isLockedState: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable (ConstraintLayoutScope.(IntSize) -> Unit),
|
||||
) {
|
||||
var isMenuVisible by rememberSaveable { mutableStateOf(value = false) }
|
||||
var pressOffset by remember { mutableStateOf(value = DpOffset.Zero) }
|
||||
var itemSize by remember { mutableStateOf(value = IntSize.Zero) }
|
||||
|
|
@ -166,7 +79,7 @@ private fun CardContainer(
|
|||
.onSizeChanged { itemSize = it }
|
||||
.testTag(MainScreenTestTags.TOTAL_BALANCE_CONTAINER)
|
||||
.then(
|
||||
if (isLockedState || dropDownItems.isEmpty()) {
|
||||
if (state is WalletCardState.LockedContent || state.dropDownItems.isEmpty()) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier
|
||||
|
|
@ -189,15 +102,19 @@ private fun CardContainer(
|
|||
}
|
||||
},
|
||||
),
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
color = TangemTheme.colors.background.primary,
|
||||
) {
|
||||
ConstraintLayout(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
content(itemSize)
|
||||
CardContainer(
|
||||
state = state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
itemSize = itemSize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -209,10 +126,65 @@ private fun CardContainer(
|
|||
pressOffset = pressOffset,
|
||||
itemHeight = itemHeight,
|
||||
onDismissRequest = { isMenuVisible = false },
|
||||
dropDownItems = dropDownItems,
|
||||
dropDownItems = state.dropDownItems,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries")
|
||||
@Composable
|
||||
private fun CardContainer(state: WalletCardState, isBalanceHidden: Boolean, itemSize: IntSize) {
|
||||
var balanceWidth by remember { mutableIntStateOf(value = Int.MIN_VALUE) }
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.padding(vertical = 12.dp),
|
||||
) {
|
||||
TitleText(
|
||||
text = state.title,
|
||||
modifier = Modifier,
|
||||
)
|
||||
Balance(
|
||||
state = state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier
|
||||
.onSizeChanged { balanceWidth = it.width }
|
||||
.padding(vertical = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
|
||||
val additionalText by remember(state.additionalInfo, isBalanceHidden) {
|
||||
mutableStateOf(
|
||||
state.additionalInfo?.content?.orMaskWithStars(
|
||||
maskWithStars = state.additionalInfo?.hideable == true && isBalanceHidden,
|
||||
),
|
||||
)
|
||||
}
|
||||
AdditionalInfo(
|
||||
text = additionalText,
|
||||
modifier = Modifier.conditional(
|
||||
state.imageResId == null,
|
||||
) { fillMaxWidth() },
|
||||
)
|
||||
}
|
||||
|
||||
// If balance has a large width then image must be hidden
|
||||
val hasSpaceForImage by remember(key1 = balanceWidth, key2 = itemSize.width) {
|
||||
mutableStateOf(value = balanceWidth < itemSize.width * HALF_OF_ITEM_WIDTH)
|
||||
}
|
||||
|
||||
if (hasSpaceForImage) {
|
||||
Image(
|
||||
id = state.imageResId,
|
||||
modifier = Modifier.wrapContentWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun ManageWalletContextMenu(
|
||||
|
|
@ -282,10 +254,13 @@ private fun Balance(state: WalletCardState, isBalanceHidden: Boolean, modifier:
|
|||
) { balance ->
|
||||
when (state) {
|
||||
is WalletCardState.Content -> {
|
||||
ResizableText(
|
||||
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size32),
|
||||
Text(
|
||||
text = balance,
|
||||
fontSizeRange = FontSizeRange(min = 16.sp, max = TangemTheme.typography.h2.fontSize),
|
||||
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size32),
|
||||
autoSize = TextAutoSize.StepBased(
|
||||
minFontSize = 16.sp,
|
||||
maxFontSize = TangemTheme.typography.h2.fontSize,
|
||||
),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.h2
|
||||
|
|
@ -401,6 +376,16 @@ private fun Preview_WalletCard(
|
|||
private class WalletCardStateProvider : CollectionPreviewParameterProvider<WalletCardState>(
|
||||
collection = listOf(
|
||||
WalletPreviewData.walletCardContentState,
|
||||
WalletPreviewData.walletCardContentState.copy(
|
||||
balance = "0.00",
|
||||
),
|
||||
WalletPreviewData.walletCardContentState.copy(
|
||||
title = "Title",
|
||||
additionalInfo = WalletAdditionalInfo(
|
||||
hideable = false,
|
||||
content = TextReference.Str("3 cards"),
|
||||
),
|
||||
),
|
||||
WalletPreviewData.walletCardContentState.copy(
|
||||
isBalanceFlickering = true,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
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
|
||||
|
|
@ -24,17 +22,13 @@ 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, portfolioVisibleState)
|
||||
tokensListItems(state.tokensListState, modifier, isBalanceHidden)
|
||||
}
|
||||
is WalletState.SingleCurrency -> {
|
||||
txHistoryItems(state.txHistoryState, txHistoryItems, isBalanceHidden, modifier)
|
||||
}
|
||||
is WalletState.Visa -> {
|
||||
txHistoryItems(state.txHistoryState, txHistoryItems, isBalanceHidden, modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,17 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.animation.core.tween
|
||||
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.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
|
|
@ -20,12 +24,12 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
import com.tangem.core.ui.utils.lazyListItemPosition
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
internal fun LazyListScope.portfolioContentItems(
|
||||
items: ImmutableList<TokensListItemUM.Portfolio>,
|
||||
modifier: Modifier = Modifier,
|
||||
isBalanceHidden: Boolean,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
items.forEachIndexed { index, item ->
|
||||
portfolioTokensList(
|
||||
|
|
@ -33,7 +37,6 @@ internal fun LazyListScope.portfolioContentItems(
|
|||
modifier = modifier,
|
||||
portfolioIndex = index,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
portfolioVisibleState = portfolioVisibleState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -43,35 +46,34 @@ internal fun LazyListScope.portfolioTokensList(
|
|||
modifier: Modifier,
|
||||
portfolioIndex: Int,
|
||||
isBalanceHidden: Boolean,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
val tokens = portfolio.tokens
|
||||
val isExpanded = portfolio.isExpanded
|
||||
val lastIndex = tokens.lastIndex.inc()
|
||||
|
||||
portfolioItem(
|
||||
portfolio = portfolio,
|
||||
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(
|
||||
currentIndex = 1,
|
||||
lastIndex = lastIndex,
|
||||
modifier = modifier
|
||||
.animateItem()
|
||||
.animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null)
|
||||
.roundedShapeItemDecoration(
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
currentIndex = 1,
|
||||
lastIndex = 1,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
),
|
||||
visibleState = appear,
|
||||
visible = isExpanded,
|
||||
) {
|
||||
NonContentItemContent(
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing28),
|
||||
|
|
@ -82,23 +84,23 @@ internal fun LazyListScope.portfolioTokensList(
|
|||
}
|
||||
itemsIndexed(
|
||||
items = tokens,
|
||||
key = { _, item -> item.id },
|
||||
key = { _, item -> item.id.toString() + "-portfolio-${portfolio.id}" },
|
||||
contentType = { _, item -> item::class.java },
|
||||
itemContent = { tokenIndex, token ->
|
||||
val indexWithHeader = tokenIndex.inc()
|
||||
val lastIndex = tokens.lastIndex.inc()
|
||||
val appear = portfolioVisibleState(portfolio)
|
||||
SlideInItemVisibility(
|
||||
currentIndex = tokenIndex,
|
||||
lastIndex = lastIndex,
|
||||
modifier = modifier
|
||||
.testModifier(indexWithHeader)
|
||||
.animateItem()
|
||||
.animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null)
|
||||
.roundedShapeItemDecoration(
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
currentIndex = indexWithHeader,
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
),
|
||||
visibleState = appear,
|
||||
visible = isExpanded,
|
||||
) {
|
||||
val modifier = if (indexWithHeader == lastIndex) Modifier.padding(bottom = 8.dp) else Modifier
|
||||
PortfolioTokensListItem(
|
||||
|
|
@ -111,16 +113,15 @@ internal fun LazyListScope.portfolioTokensList(
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun LazyListScope.portfolioItem(
|
||||
portfolio: TokensListItemUM.Portfolio,
|
||||
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()
|
||||
|
|
@ -128,60 +129,65 @@ private fun LazyListScope.portfolioItem(
|
|||
}
|
||||
|
||||
item(
|
||||
key = "account-${portfolio.id}-isExpanded$isExpanded",
|
||||
contentType = "account-isExpanded$isExpanded",
|
||||
key = "account-${portfolio.id}",
|
||||
contentType = "account-content",
|
||||
) {
|
||||
val anchorModifier = modifier
|
||||
.testModifier(portfolioIndex)
|
||||
.animateItem()
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = 0,
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
)
|
||||
val appear = portfolioVisibleState(portfolio)
|
||||
if (isExpanded) {
|
||||
SlideInItemVisibility(
|
||||
modifier = anchorModifier,
|
||||
visibleState = appear,
|
||||
) {
|
||||
PortfolioListItem(
|
||||
state = portfolio,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
AnimatedVisibility(
|
||||
modifier = anchorModifier,
|
||||
visibleState = appear,
|
||||
enter = fadeIn(),
|
||||
exit = ExitTransition.None,
|
||||
) {
|
||||
PortfolioListItem(
|
||||
state = portfolio,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
var lastIndexProxy by remember { mutableIntStateOf(lastIndex) }
|
||||
|
||||
// When collapsing the portfolio, we delay updating lastIndexProxy to allow
|
||||
// shrinking animation to complete before changing the shape.
|
||||
LaunchedEffect(lastIndex) {
|
||||
if (lastIndex != 0) {
|
||||
lastIndexProxy = lastIndex
|
||||
return@LaunchedEffect
|
||||
}
|
||||
delay(timeMillis = minOf(50 * tokens.size, 350).toLong())
|
||||
lastIndexProxy = 0
|
||||
}
|
||||
|
||||
PortfolioListItem(
|
||||
state = portfolio,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = modifier
|
||||
.testModifier(portfolioIndex)
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = 0,
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
lastIndex = lastIndexProxy,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun SlideInItemVisibility(
|
||||
visibleState: MutableTransitionState<Boolean>,
|
||||
visible: Boolean,
|
||||
currentIndex: Int,
|
||||
lastIndex: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable() AnimatedVisibilityScope.() -> Unit,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val maxDelay = 250
|
||||
val delayEnter = minOf(50 * currentIndex, maxDelay)
|
||||
val delayExit = minOf(50 * (lastIndex - currentIndex - 1), maxDelay)
|
||||
|
||||
AnimatedVisibility(
|
||||
modifier = modifier,
|
||||
visibleState = visibleState,
|
||||
enter = slideInVertically(
|
||||
animationSpec = tween(easing = LinearOutSlowInEasing),
|
||||
initialOffsetY = { it },
|
||||
) + fadeIn(),
|
||||
exit = ExitTransition.None,
|
||||
visible = visible,
|
||||
enter = fadeIn(
|
||||
tween(100, delayMillis = delayEnter),
|
||||
) + expandVertically(
|
||||
tween(100, delayMillis = delayEnter),
|
||||
expandFrom = Alignment.Top,
|
||||
),
|
||||
exit = fadeOut(
|
||||
tween(100, delayMillis = delayExit),
|
||||
) + shrinkVertically(
|
||||
tween(100, delayMillis = delayExit),
|
||||
shrinkTowards = Alignment.Top,
|
||||
),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrenc
|
|||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.TextAutoSize
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.FontSizeRange
|
||||
import com.tangem.core.ui.components.ResizableText
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionBaseButton
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonContent
|
||||
|
|
@ -19,13 +19,7 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
internal fun MultiCurrencyAction(
|
||||
config: ActionButtonConfig,
|
||||
fontSizeValue: TextUnit,
|
||||
fontSizeRange: FontSizeRange,
|
||||
onFontSizeChange: (Float) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
internal fun MultiCurrencyAction(config: ActionButtonConfig, modifier: Modifier = Modifier) {
|
||||
ActionBaseButton(
|
||||
config = config,
|
||||
shape = RoundedCornerShape(size = TangemTheme.dimens.radius12),
|
||||
|
|
@ -33,11 +27,12 @@ internal fun MultiCurrencyAction(
|
|||
ActionButtonContent(
|
||||
config = config,
|
||||
text = { color ->
|
||||
ResizableText(
|
||||
Text(
|
||||
text = config.text.resolveReference(),
|
||||
fontSizeValue = fontSizeValue,
|
||||
fontSizeRange = fontSizeRange,
|
||||
onFontSizeChange = onFontSizeChange,
|
||||
autoSize = TextAutoSize.StepBased(
|
||||
minFontSize = 10.sp,
|
||||
maxFontSize = TangemTheme.typography.button.fontSize,
|
||||
),
|
||||
color = color,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
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
|
||||
|
|
@ -41,14 +40,12 @@ 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,
|
||||
|
|
|
|||
|
|
@ -1,185 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBlockState
|
||||
|
||||
private const val BALANCES_AND_LIMITS_BLOCK_KEY = "BalancesAndLimitsBlock"
|
||||
|
||||
internal fun LazyListScope.balancesAndLimitsBlock(state: BalancesAndLimitsBlockState, modifier: Modifier = Modifier) {
|
||||
item(key = BALANCES_AND_LIMITS_BLOCK_KEY, contentType = BALANCES_AND_LIMITS_BLOCK_KEY) {
|
||||
BalancesAndLimitsBlock(state, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BalancesAndLimitsBlock(state: BalancesAndLimitsBlockState, modifier: Modifier = Modifier) {
|
||||
val onClick: () -> Unit = remember(state) {
|
||||
{ (state as? BalancesAndLimitsBlockState.Content)?.onClick?.invoke() }
|
||||
}
|
||||
val isEnabled: Boolean = remember(state) {
|
||||
state is BalancesAndLimitsBlockState.Content && state.isEnabled
|
||||
}
|
||||
|
||||
ContentContainer(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
enabled = isEnabled,
|
||||
onClick = onClick,
|
||||
title = {
|
||||
Text(
|
||||
text = "Balances & Limits",
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Content(state = state)
|
||||
},
|
||||
endIcon = {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = R.drawable.ic_chevron_right_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private inline fun ContentContainer(
|
||||
enabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
noinline onClick: () -> Unit,
|
||||
crossinline title: @Composable () -> Unit,
|
||||
crossinline content: @Composable () -> Unit,
|
||||
crossinline endIcon: @Composable () -> Unit,
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius16),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
contentColor = TangemTheme.colors.text.primary1,
|
||||
disabledContainerColor = TangemTheme.colors.background.primary,
|
||||
disabledContentColor = TangemTheme.colors.text.primary1,
|
||||
),
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(all = TangemTheme.dimens.spacing12)
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size48),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing6),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
title()
|
||||
content()
|
||||
}
|
||||
endIcon()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(state: BalancesAndLimitsBlockState, modifier: Modifier = Modifier) {
|
||||
AnimatedContent(
|
||||
modifier = modifier,
|
||||
targetState = state,
|
||||
label = "Update the balances and limits block",
|
||||
) { blockState ->
|
||||
when (blockState) {
|
||||
is BalancesAndLimitsBlockState.Content -> with(blockState) {
|
||||
AvailableLimit(
|
||||
availableBalance = availableBalance,
|
||||
limitDays = limitDays,
|
||||
)
|
||||
}
|
||||
is BalancesAndLimitsBlockState.Error -> {
|
||||
Text(
|
||||
text = "–",
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
is BalancesAndLimitsBlockState.Loading -> {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(TangemTheme.dimens.size200)
|
||||
.height(TangemTheme.dimens.size20),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AvailableLimit(availableBalance: String, limitDays: Int, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
Text(
|
||||
text = availableBalance,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = "available for $limitDays day(s)",
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun BalancesAndLimitsBlockPreview(
|
||||
@PreviewParameter(BalancesAndLimitsBlockParameterProvider::class) state: BalancesAndLimitsBlockState,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
BalancesAndLimitsBlock(state)
|
||||
}
|
||||
}
|
||||
|
||||
private class BalancesAndLimitsBlockParameterProvider : CollectionPreviewParameterProvider<BalancesAndLimitsBlockState>(
|
||||
collection = listOf(
|
||||
BalancesAndLimitsBlockState.Loading,
|
||||
BalancesAndLimitsBlockState.Error,
|
||||
BalancesAndLimitsBlockState.Content(
|
||||
availableBalance = "400.00 USDT",
|
||||
limitDays = 7,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBottomSheetConfig
|
||||
|
||||
@Composable
|
||||
internal fun BalancesAndLimitsBottomSheet(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet(
|
||||
config = config,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
) { content: BalancesAndLimitsBottomSheetConfig ->
|
||||
BalancesAndLimitsContent(content)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BalancesAndLimitsContent(config: BalancesAndLimitsBottomSheetConfig, modifier: Modifier = Modifier) {
|
||||
ContentContainer(
|
||||
modifier = modifier,
|
||||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.visa_main_balances_and_limits),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
},
|
||||
firstBlock = {
|
||||
BalancesBlock(balances = config.balance)
|
||||
},
|
||||
secondBlock = {
|
||||
LimitsBlock(limits = config.limit)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BalancesBlock(balances: BalancesAndLimitsBottomSheetConfig.Balance, modifier: Modifier = Modifier) {
|
||||
BlockContent(
|
||||
modifier = modifier,
|
||||
title = resourceReference(R.string.common_balance),
|
||||
content = {
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_balance_limits_details_total),
|
||||
value = balances.totalBalance,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_balance_limits_details_aml_verified),
|
||||
value = balances.amlVerified,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_balance_limits_details_available),
|
||||
value = balances.availableBalance,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_balance_limits_details_blocked),
|
||||
value = balances.blockedBalance,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_balance_limits_details_debt),
|
||||
value = balances.debit,
|
||||
)
|
||||
},
|
||||
description = {
|
||||
InfoButton(onClick = balances.onInfoClick)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LimitsBlock(limits: BalancesAndLimitsBottomSheetConfig.Limit, modifier: Modifier = Modifier) {
|
||||
BlockContent(
|
||||
modifier = modifier,
|
||||
title = resourceReference(R.string.visa_balance_limits_details_limits),
|
||||
content = {
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_balance_limits_details_total),
|
||||
value = limits.total,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_balance_limits_details_no_otp_limit),
|
||||
value = limits.other,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_balance_limits_details_single_transaction),
|
||||
value = limits.singleTransaction,
|
||||
)
|
||||
},
|
||||
description = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.visa_main_available_till_date, limits.availableBy),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
InfoButton(onClick = limits.onInfoClick)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
IconButton(
|
||||
modifier = modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = onClick,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size16),
|
||||
painter = painterResource(id = R.drawable.ic_information_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private inline fun ContentContainer(
|
||||
modifier: Modifier = Modifier,
|
||||
title: @Composable BoxScope.() -> Unit,
|
||||
firstBlock: @Composable ColumnScope.() -> Unit,
|
||||
secondBlock: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.background(TangemTheme.colors.background.secondary),
|
||||
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing12),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size44),
|
||||
contentAlignment = Alignment.Center,
|
||||
content = title,
|
||||
)
|
||||
firstBlock()
|
||||
secondBlock()
|
||||
SpacerH16()
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun BalancesAndLimitsBottomSheetPreview(
|
||||
@PreviewParameter(BalancesAndLimitsBottomSheetParameterProvider::class) state: BalancesAndLimitsBottomSheetConfig,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
BalancesAndLimitsContent(state)
|
||||
}
|
||||
}
|
||||
|
||||
private class BalancesAndLimitsBottomSheetParameterProvider :
|
||||
CollectionPreviewParameterProvider<BalancesAndLimitsBottomSheetConfig>(
|
||||
collection = listOf(
|
||||
BalancesAndLimitsBottomSheetConfig(
|
||||
balance = BalancesAndLimitsBottomSheetConfig.Balance(
|
||||
totalBalance = "492.45 USDT",
|
||||
availableBalance = "392.45 USDT",
|
||||
blockedBalance = "36.00 USDT",
|
||||
debit = "00.00 USDT",
|
||||
amlVerified = "356.45 USDT",
|
||||
onInfoClick = {},
|
||||
),
|
||||
limit = BalancesAndLimitsBottomSheetConfig.Limit(
|
||||
availableBy = "Nov, 11 USDT",
|
||||
total = "563.00 USDT",
|
||||
other = "100.00 USDT",
|
||||
singleTransaction = "100.00 USDT",
|
||||
onInfoClick = {},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
private const val BLOCK_ITEM_NAME_WEIGHT = .45f
|
||||
private const val BLOCK_ITEM_VALUE_WEIGHT = .55f
|
||||
|
||||
@Composable
|
||||
internal inline fun BlockContent(
|
||||
title: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
description: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color = TangemTheme.colors.background.primary,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing12)
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size42),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
SpacerWMax()
|
||||
description()
|
||||
}
|
||||
content()
|
||||
SpacerH8()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun BlockItem(title: TextReference, value: String, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size32)
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing8,
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(BLOCK_ITEM_NAME_WEIGHT),
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Start,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.weight(BLOCK_ITEM_VALUE_WEIGHT),
|
||||
text = value,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetV2
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class KycRejectedComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted params: Params,
|
||||
) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: KycRejectedModel = getOrCreateModel(params = params)
|
||||
|
||||
override fun dismiss() {
|
||||
model.onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
MessageBottomSheetV2(state = state, onDismissRequest = ::dismiss)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val callbacks: KycRejectedCallbacks,
|
||||
val walletId: UserWalletId,
|
||||
val customerId: String,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(appComponentContext: AppComponentContext, params: Params): KycRejectedComponent
|
||||
}
|
||||
}
|
||||
|
||||
internal interface KycRejectedCallbacks {
|
||||
fun onClickYourProfile(userWalletId: UserWalletId)
|
||||
fun onClickGoToSupport(customerId: String)
|
||||
fun onClickHideKyc(userWalletId: UserWalletId)
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.res.R
|
||||
import com.tangem.core.ui.components.bottomsheets.message.*
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.styledResourceReference
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class KycRejectedModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<KycRejectedComponent.Params>()
|
||||
|
||||
val uiState: StateFlow<MessageBottomSheetUMV2>
|
||||
field = MutableStateFlow(getInitialState())
|
||||
|
||||
private fun getInitialState(): MessageBottomSheetUMV2 {
|
||||
return bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(com.tangem.core.ui.R.drawable.ic_heart_broken_32) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Warning
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.Warning
|
||||
}
|
||||
title = resourceReference(R.string.tangempay_kyc_rejected)
|
||||
body = combinedReference(
|
||||
resourceReference(R.string.tangempay_kyc_rejected_description),
|
||||
stringReference(" "),
|
||||
styledResourceReference(
|
||||
id = R.string.tangempay_kyc_rejected_description_span,
|
||||
spanStyleReference = {
|
||||
TangemTheme.typography.body2.copy(TangemTheme.colors.text.accent).toSpanStyle()
|
||||
.copy(textDecoration = TextDecoration.None)
|
||||
},
|
||||
onClick = {
|
||||
params.callbacks.onClickYourProfile(userWalletId = params.walletId)
|
||||
onDismiss()
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.tangempay_go_to_support)
|
||||
onClick = {
|
||||
params.callbacks.onClickGoToSupport(customerId = params.customerId)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
secondaryButton {
|
||||
text = resourceReference(R.string.tangempay_kyc_rejected_button_text)
|
||||
onClick = {
|
||||
params.callbacks.onClickHideKyc(userWalletId = params.walletId)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
}.messageBottomSheetUMV2
|
||||
}
|
||||
|
||||
fun onDismiss() {
|
||||
params.onDismiss()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,255 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
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.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.components.SecondaryButtonIconStart
|
||||
import com.tangem.core.ui.components.SpacerW12
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.VisaTxDetailsBottomSheetConfig
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
internal fun VisaTxDetailsBottomSheet(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet(
|
||||
config = config,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
) { content: VisaTxDetailsBottomSheetConfig ->
|
||||
VisaTxDetailsBottomSheetContent(content)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun VisaTxDetailsBottomSheetContent(config: VisaTxDetailsBottomSheetConfig, modifier: Modifier = Modifier) {
|
||||
Column {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size44)
|
||||
.background(TangemTheme.colors.background.secondary),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.visa_transaction_details_header),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = modifier.background(TangemTheme.colors.background.secondary),
|
||||
contentPadding = PaddingValues(
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing12),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
item {
|
||||
TransactionBlock(config.transaction)
|
||||
}
|
||||
|
||||
items(config.requests) { item ->
|
||||
BlockchainRequestBlock(item)
|
||||
}
|
||||
|
||||
item {
|
||||
DisputeButton(config.onDisputeClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TransactionBlock(transaction: VisaTxDetailsBottomSheetConfig.Transaction, modifier: Modifier = Modifier) {
|
||||
BlockContent(
|
||||
modifier = modifier,
|
||||
title = resourceReference(R.string.visa_transaction_details_title),
|
||||
content = {
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_type),
|
||||
value = transaction.type,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_status),
|
||||
value = transaction.status,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_blockchain_amount),
|
||||
value = transaction.blockchainAmount,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_transaction_amount),
|
||||
value = transaction.transactionAmount,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_currency_code),
|
||||
value = transaction.transactionCurrencyCode,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_merchant_name),
|
||||
value = transaction.merchantName,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_merchant_city),
|
||||
value = transaction.merchantCity,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_merchant_country_code),
|
||||
value = transaction.merchantCountryCode,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_merchant_category_code),
|
||||
value = transaction.merchantCategoryCode,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun BlockchainRequestBlock(request: VisaTxDetailsBottomSheetConfig.Request, modifier: Modifier = Modifier) {
|
||||
BlockContent(
|
||||
modifier = modifier,
|
||||
title = resourceReference(R.string.visa_transaction_details_transaction_request),
|
||||
description = {
|
||||
if (request.onExploreClick != null) {
|
||||
Row(
|
||||
modifier = Modifier.clickable(onClick = request.onExploreClick),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_compass_24),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(size = TangemTheme.dimens.size18),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.common_explore),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption1,
|
||||
)
|
||||
}
|
||||
SpacerW12()
|
||||
}
|
||||
},
|
||||
content = {
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_type),
|
||||
value = request.type,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_status),
|
||||
value = request.status,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_blockchain_amount),
|
||||
value = request.blockchainAmount,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_transaction_amount),
|
||||
value = request.transactionAmount,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_currency_code),
|
||||
value = request.currencyCode,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_error_code),
|
||||
value = request.errorCode.toString(),
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_date),
|
||||
value = request.date,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_transaction_hash),
|
||||
value = request.txHash,
|
||||
)
|
||||
BlockItem(
|
||||
title = resourceReference(R.string.visa_transaction_details_transaction_status),
|
||||
value = request.txStatus,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisputeButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
SecondaryButtonIconStart(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
text = stringResourceSafe(R.string.visa_tx_dispute_button),
|
||||
iconResId = R.drawable.ic_alert_triangle_20,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun VisaTxDetailsBottomSheetPreview(
|
||||
@PreviewParameter(VisaTxDetailsBottomSheetParameterProvider::class) state: VisaTxDetailsBottomSheetConfig,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
VisaTxDetailsBottomSheetContent(state)
|
||||
}
|
||||
}
|
||||
|
||||
private class VisaTxDetailsBottomSheetParameterProvider :
|
||||
CollectionPreviewParameterProvider<VisaTxDetailsBottomSheetConfig>(
|
||||
collection = listOf(
|
||||
VisaTxDetailsBottomSheetConfig(
|
||||
transaction = VisaTxDetailsBottomSheetConfig.Transaction(
|
||||
id = "518385816101345408",
|
||||
type = "payment",
|
||||
status = "authorized",
|
||||
blockchainAmount = "1.0614 USDT",
|
||||
transactionAmount = "0.99 €",
|
||||
transactionCurrencyCode = "978",
|
||||
merchantName = "SQ *FORMATIVE",
|
||||
merchantCity = "London",
|
||||
merchantCountryCode = "GB",
|
||||
merchantCategoryCode = "5814",
|
||||
),
|
||||
requests = persistentListOf(
|
||||
VisaTxDetailsBottomSheetConfig.Request(
|
||||
id = "524582128501966718",
|
||||
type = "authorize_payment",
|
||||
status = "accepted",
|
||||
blockchainAmount = "1.0593 USDT",
|
||||
transactionAmount = "0.99 €",
|
||||
currencyCode = "978",
|
||||
errorCode = 0,
|
||||
date = "2023-12-01 14:20:09.230 +0300",
|
||||
txHash = "0xc458f0204fe43b82c775004baabb38435b5595f4307d8c3ac74625c827be7c29",
|
||||
txStatus = "confirmed",
|
||||
onExploreClick = {},
|
||||
),
|
||||
),
|
||||
onDisputeClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// endregion Preview
|
||||
|
|
@ -5,7 +5,6 @@ import com.tangem.domain.models.PortfolioId
|
|||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.yieldSupplyKey
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.network.NetworkAddress
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
|
|
@ -15,7 +14,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.TokenCon
|
|||
import org.junit.Test
|
||||
import java.math.BigDecimal
|
||||
|
||||
class YieldSupplyPromoBannerKeyConverterTest {
|
||||
class YieldSupplyPromoBannerConverterTest {
|
||||
|
||||
@Test
|
||||
fun `GIVEN promo disabled WHEN convert THEN return null`() {
|
||||
|
|
@ -26,8 +25,8 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
|
||||
tokenList = tokenList,
|
||||
)
|
||||
val converter = YieldSupplyPromoBannerKeyConverter(
|
||||
yieldModuleApyMap = mapOf(token.yieldSupplyKey() to BigDecimal("0.10")),
|
||||
val converter = YieldSupplyPromoBannerConverter(
|
||||
yieldModuleApyMap = mapOf("${token.network.backendId}_${token.contractAddress}" to BigDecimal("0.10")),
|
||||
shouldShowMainPromo = false,
|
||||
)
|
||||
|
||||
|
|
@ -44,7 +43,7 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
|
||||
tokenList = ungroupedTokenList(status),
|
||||
)
|
||||
val converter = YieldSupplyPromoBannerKeyConverter(
|
||||
val converter = YieldSupplyPromoBannerConverter(
|
||||
yieldModuleApyMap = emptyMap(),
|
||||
shouldShowMainPromo = true,
|
||||
)
|
||||
|
|
@ -62,8 +61,8 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
|
||||
tokenList = ungroupedTokenList(statusActive),
|
||||
)
|
||||
val converter = YieldSupplyPromoBannerKeyConverter(
|
||||
yieldModuleApyMap = mapOf(token.yieldSupplyKey() to BigDecimal("0.12")),
|
||||
val converter = YieldSupplyPromoBannerConverter(
|
||||
yieldModuleApyMap = mapOf("${token.network.backendId}_${token.contractAddress}" to BigDecimal("0.12")),
|
||||
shouldShowMainPromo = true,
|
||||
)
|
||||
|
||||
|
|
@ -73,7 +72,7 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN multiple candidates EVM case insensitive WHEN convert THEN return key of max amount`() {
|
||||
fun `GIVEN multiple candidates EVM case insensitive WHEN convert THEN return status of max amount`() {
|
||||
val evmNetworkId = "ETH"
|
||||
val tokenSmall = createToken(networkId = evmNetworkId, backendId = evmNetworkId, contract = "0xAbCd")
|
||||
val tokenBig = createToken(networkId = evmNetworkId, backendId = evmNetworkId, contract = "0xBEEF")
|
||||
|
|
@ -90,15 +89,14 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
|
||||
tokenList = ungroupedTokenList(statusSmall, statusBig),
|
||||
)
|
||||
val converter = YieldSupplyPromoBannerKeyConverter(
|
||||
val converter = YieldSupplyPromoBannerConverter(
|
||||
yieldModuleApyMap = apyMap,
|
||||
shouldShowMainPromo = true,
|
||||
)
|
||||
|
||||
val result = converter.convert(params)
|
||||
|
||||
val expectedKey = "${tokenBig.network.backendId}_${tokenBig.contractAddress.uppercase()}"
|
||||
assertThat(result).isEqualTo(expectedKey)
|
||||
assertThat(result).isEqualTo(statusBig)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -114,7 +112,7 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
|
||||
tokenList = ungroupedTokenList(status),
|
||||
)
|
||||
val converter = YieldSupplyPromoBannerKeyConverter(
|
||||
val converter = YieldSupplyPromoBannerConverter(
|
||||
yieldModuleApyMap = apyMap,
|
||||
shouldShowMainPromo = true,
|
||||
)
|
||||
|
|
@ -132,8 +130,8 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
|
||||
tokenList = ungroupedTokenList(status),
|
||||
)
|
||||
val converter = YieldSupplyPromoBannerKeyConverter(
|
||||
yieldModuleApyMap = mapOf(token.yieldSupplyKey() to BigDecimal("0.10")),
|
||||
val converter = YieldSupplyPromoBannerConverter(
|
||||
yieldModuleApyMap = mapOf("${token.network.backendId}_${token.contractAddress}" to BigDecimal("0.10")),
|
||||
shouldShowMainPromo = true,
|
||||
)
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue