Updated on 2026-08-14
This commit is contained in:
commit
c8024b166d
656 changed files with 18270 additions and 4656 deletions
|
|
@ -11,12 +11,11 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.nft.FetchNFTCollectionsUseCase
|
||||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
|
|
@ -44,7 +43,6 @@ import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
|||
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -79,8 +77,6 @@ internal class WalletModel @Inject constructor(
|
|||
private val onrampStatusFactory: OnrampStatusFactory,
|
||||
private val biometryFeatureToggles: BiometryFeatureToggles,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val fetchNFTCollectionsUseCase: FetchNFTCollectionsUseCase,
|
||||
private val walletsRepository: WalletsRepository,
|
||||
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
|
||||
val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
val innerWalletRouter: InnerWalletRouter,
|
||||
|
|
@ -92,7 +88,6 @@ internal class WalletModel @Inject constructor(
|
|||
private val walletsUpdateJobHolder = JobHolder()
|
||||
private val refreshWalletJobHolder = JobHolder()
|
||||
private val expressStatusJobHolder = JobHolder()
|
||||
private val walletsNFTsUpdateJobHolder = JobHolder()
|
||||
private var needToRefreshWallet = false
|
||||
|
||||
private var expressTxStatusTaskScheduler = SingleTaskScheduler<Unit>()
|
||||
|
|
@ -107,10 +102,7 @@ internal class WalletModel @Inject constructor(
|
|||
subscribeToUserWalletsUpdates()
|
||||
subscribeOnBalanceHiding()
|
||||
subscribeOnSelectedWalletFlow()
|
||||
subscribeToScreenBackgroundState()
|
||||
subscribeOnPushNotificationsPermission()
|
||||
subscribeOnExpressTransactionsUpdates()
|
||||
subscribeOnNFTUpdates()
|
||||
|
||||
clickIntents.initialize(innerWalletRouter, modelScope)
|
||||
}
|
||||
|
|
@ -223,6 +215,8 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
|
||||
walletDeepLinksHandler.registerForWallet(scope = modelScope, userWallet = selectedWallet)
|
||||
subscribeOnExpressTransactionsUpdates(selectedWallet)
|
||||
subscribeToScreenBackgroundState(selectedWallet)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
|
|
@ -231,7 +225,7 @@ internal class WalletModel @Inject constructor(
|
|||
|
||||
// We need to update the current wallet quotes if the application was in the background for more than 10 seconds
|
||||
// and then returned to the foreground
|
||||
private fun subscribeToScreenBackgroundState() {
|
||||
private fun subscribeToScreenBackgroundState(userWallet: UserWallet) {
|
||||
screenLifecycleProvider.isBackgroundState
|
||||
.onEach { isBackground ->
|
||||
expressTxStatusTaskScheduler.cancelTask()
|
||||
|
|
@ -241,53 +235,31 @@ internal class WalletModel @Inject constructor(
|
|||
isBackground -> needToRefreshTimer()
|
||||
needToRefreshWallet && !isBackground -> {
|
||||
triggerRefreshWalletQuotes()
|
||||
subscribeOnExpressTransactionsUpdates()
|
||||
subscribeOnExpressTransactionsUpdates(userWallet)
|
||||
}
|
||||
!isBackground -> subscribeOnExpressTransactionsUpdates()
|
||||
!isBackground -> subscribeOnExpressTransactionsUpdates(userWallet)
|
||||
}
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
.saveIn(expressStatusJobHolder)
|
||||
}
|
||||
|
||||
private fun subscribeOnExpressTransactionsUpdates() {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
expressTxStatusTaskScheduler.cancelTask()
|
||||
expressTxStatusTaskScheduler.scheduleTask(
|
||||
modelScope,
|
||||
PeriodicTask(
|
||||
isDelayFirst = false,
|
||||
delay = EXPRESS_STATUS_UPDATE_DELAY,
|
||||
task = {
|
||||
runCatching { onrampStatusFactory.updateOnrmapTransactionStatuses() }
|
||||
},
|
||||
onSuccess = { /* no-op */ },
|
||||
onError = { /* no-op */ },
|
||||
),
|
||||
)
|
||||
}.saveIn(expressStatusJobHolder)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun subscribeOnNFTUpdates() {
|
||||
getWalletsUseCase()
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.flatMapLatest { wallets ->
|
||||
wallets
|
||||
.map { wallet ->
|
||||
walletsRepository.nftEnabledStatus(wallet.walletId)
|
||||
.distinctUntilChanged()
|
||||
.onEach { nftEnabled ->
|
||||
if (nftEnabled) {
|
||||
fetchNFTCollectionsUseCase.invoke(wallet.walletId)
|
||||
}
|
||||
}
|
||||
private fun subscribeOnExpressTransactionsUpdates(userWallet: UserWallet) {
|
||||
expressTxStatusTaskScheduler.cancelTask()
|
||||
expressTxStatusTaskScheduler.scheduleTask(
|
||||
modelScope,
|
||||
PeriodicTask(
|
||||
isDelayFirst = false,
|
||||
delay = EXPRESS_STATUS_UPDATE_DELAY,
|
||||
task = {
|
||||
runCatching {
|
||||
onrampStatusFactory.updateOnrmapTransactionStatuses(userWallet)
|
||||
}
|
||||
.merge()
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(walletsNFTsUpdateJobHolder)
|
||||
},
|
||||
onSuccess = { /* no-op */ },
|
||||
onError = { /* no-op */ },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun needToRefreshTimer() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
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.GetCardInfoUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
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
|
||||
|
|
@ -20,22 +27,30 @@ internal interface VisaWalletIntents {
|
|||
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 getCardInfoUseCase: GetCardInfoUseCase,
|
||||
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)
|
||||
BalancesAndLimitsBottomSheetConverter(eventSender, analyticsEventHandler)
|
||||
}
|
||||
|
||||
override fun onBalancesAndLimitsClick() {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.LimitsClicked)
|
||||
modelScope.launch(dispatchers.main) {
|
||||
val userWalletId = stateController.getSelectedWalletId()
|
||||
val balancesAndLimits = getVisaCurrencyUseCase(userWalletId)
|
||||
|
|
@ -76,6 +91,23 @@ internal class VisaWalletIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
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 = getCardInfoUseCase.invoke(userWallet.scanResponse).getOrNull() ?: return@launch
|
||||
|
||||
sendFeedbackEmailUseCase(
|
||||
FeedbackEmailType.Visa.Dispute(
|
||||
cardInfo = cardInfo,
|
||||
visaTxDetails = txDetails,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.appcurrency.extenstions.unwrap
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.nft.FetchNFTCollectionsUseCase
|
||||
import com.tangem.domain.onramp.FetchHotCryptoUseCase
|
||||
import com.tangem.domain.settings.NeverToShowWalletsScrollPreview
|
||||
import com.tangem.domain.tokens.FetchCardTokenListUseCase
|
||||
|
|
@ -15,7 +14,6 @@ import com.tangem.domain.tokens.FetchTokenListUseCase.RefreshMode
|
|||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.domain.wallets.usecase.SelectWalletUseCase
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.IsWalletNFTEnabledSyncUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
|
||||
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
|
|
@ -52,8 +50,6 @@ internal class WalletClickIntents @Inject constructor(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val onrampFeatureToggles: OnrampFeatureToggles,
|
||||
private val fetchHotCryptoUseCase: FetchHotCryptoUseCase,
|
||||
private val fetchNFTCollectionsUseCase: FetchNFTCollectionsUseCase,
|
||||
private val isWalletNFTEnabledSyncUseCase: IsWalletNFTEnabledSyncUseCase,
|
||||
) : BaseWalletClickIntents(),
|
||||
WalletCardClickIntents by walletCardClickIntentsImplementor,
|
||||
WalletWarningsClickIntents by warningsClickIntentsImplementer,
|
||||
|
|
@ -142,10 +138,6 @@ internal class WalletClickIntents @Inject constructor(
|
|||
async { rampStateManager.fetchSellServiceData() }.let(::add)
|
||||
|
||||
async { fetchHotCryptoUseCase() }.let(::add)
|
||||
|
||||
if (isWalletNFTEnabledSyncUseCase.invoke(userWallet.walletId)) {
|
||||
async { fetchNFTCollectionsUseCase(userWalletId = userWallet.walletId) }.let(::add)
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ package com.tangem.feature.wallet.child.wallet.model.intents
|
|||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.nft.analytics.NFTAnalyticsEvent
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||
|
|
@ -12,7 +14,6 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
|||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
|
||||
|
|
@ -37,7 +38,7 @@ internal interface WalletContentClickIntents {
|
|||
|
||||
fun onOrganizeTokensClick()
|
||||
|
||||
fun onOnrampSuccessClick(externalTxId: String)
|
||||
fun onOnrampSuccessClick(txId: String)
|
||||
|
||||
fun onDismissMarketsOnboarding()
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ internal interface WalletContentClickIntents {
|
|||
|
||||
fun onDisposeExpressStatus()
|
||||
|
||||
fun onNFTClick(userWalletId: UserWalletId)
|
||||
fun onNFTClick(userWallet: UserWallet)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -75,6 +76,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val walletEventSender: WalletEventSender,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : BaseWalletClickIntents(), WalletContentClickIntents {
|
||||
|
||||
override fun onDetailsClick() {
|
||||
|
|
@ -114,8 +116,8 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
router.openOrganizeTokensScreen(userWalletId = stateHolder.getSelectedWalletId())
|
||||
}
|
||||
|
||||
override fun onOnrampSuccessClick(externalTxId: String) {
|
||||
router.openOnrampSuccessScreen(externalTxId = externalTxId)
|
||||
override fun onOnrampSuccessClick(txId: String) {
|
||||
router.openOnrampSuccessScreen(txId = txId)
|
||||
}
|
||||
|
||||
override fun onDismissMarketsOnboarding() {
|
||||
|
|
@ -239,7 +241,20 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
stateHolder.update(CloseBottomSheetTransformer(userWalletId))
|
||||
}
|
||||
|
||||
override fun onNFTClick(userWalletId: UserWalletId) {
|
||||
router.openNFTCollectionsScreen(userWalletId)
|
||||
override fun onNFTClick(userWallet: UserWallet) {
|
||||
val selectedWallet = stateHolder.getSelectedWallet() as? WalletState.MultiCurrency.Content ?: return
|
||||
val analyticsState = when (val nftState = selectedWallet.nftState) {
|
||||
is WalletNFTItemUM.Content -> NFTAnalyticsEvent.NFTListScreenOpened.State.Full(nftState.assetsCount)
|
||||
is WalletNFTItemUM.Empty -> NFTAnalyticsEvent.NFTListScreenOpened.State.Empty
|
||||
is WalletNFTItemUM.Failed,
|
||||
is WalletNFTItemUM.Hidden,
|
||||
is WalletNFTItemUM.Loading,
|
||||
-> null
|
||||
}
|
||||
analyticsState?.let {
|
||||
analyticsEventHandler.send(NFTAnalyticsEvent.NFTListScreenOpened(analyticsState))
|
||||
}
|
||||
|
||||
router.openNFT(userWallet)
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetCo
|
|||
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.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.WrappedList
|
||||
|
|
@ -78,7 +79,7 @@ interface WalletCurrencyActionsClickIntents {
|
|||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
)
|
||||
|
||||
fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus, event: AnalyticsEvent? = null)
|
||||
|
||||
fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus, yield: Yield?)
|
||||
|
||||
|
|
@ -94,7 +95,7 @@ interface WalletCurrencyActionsClickIntents {
|
|||
|
||||
fun onAnalyticsClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onMultiWalletBuyClick(userWalletId: UserWalletId)
|
||||
fun onMultiWalletBuyClick(userWalletId: UserWalletId, screenType: String)
|
||||
|
||||
fun onMultiWalletSellClick(userWalletId: UserWalletId)
|
||||
|
||||
|
|
@ -151,7 +152,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
appRouter.push(route)
|
||||
}
|
||||
|
||||
override fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
override fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus, event: AnalyticsEvent?) {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
|
||||
analyticsEventHandler.send(
|
||||
|
|
@ -166,6 +167,8 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
event = TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrencyStatus.currency.symbol),
|
||||
)
|
||||
|
||||
event?.let { analyticsEventHandler.send(it) }
|
||||
|
||||
stateHolder.showBottomSheet(
|
||||
createReceiveBottomSheetContent(
|
||||
currency = cryptoCurrencyStatus.currency,
|
||||
|
|
@ -470,7 +473,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onMultiWalletBuyClick(userWalletId: UserWalletId) {
|
||||
override fun onMultiWalletBuyClick(userWalletId: UserWalletId, screenType: String) {
|
||||
onMultiWalletActionClick(
|
||||
statusFlow = if (onrampFeatureToggles.isFeatureEnabled) {
|
||||
rampStateManager.getExpressInitializationStatus(userWalletId)
|
||||
|
|
@ -478,7 +481,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
rampStateManager.getBuyInitializationStatus()
|
||||
},
|
||||
route = AppRoute.BuyCrypto(userWalletId = userWalletId),
|
||||
eventCreator = MainScreenAnalyticsEvent::ButtonBuy,
|
||||
eventCreator = { MainScreenAnalyticsEvent.ButtonBuy(status = it, screenType = screenType) },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
|
|
@ -67,13 +68,13 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun openOnrampSuccessScreen(externalTxId: String) {
|
||||
override fun openOnrampSuccessScreen(txId: String) {
|
||||
// finish current onramp flow and show onramp success screen
|
||||
val replaceOnrampScreens = router.stack
|
||||
.filterNot { it is AppRoute.Onramp }
|
||||
.toMutableList()
|
||||
|
||||
replaceOnrampScreens.add(AppRoute.OnrampSuccess(externalTxId))
|
||||
replaceOnrampScreens.add(AppRoute.OnrampSuccess(txId))
|
||||
|
||||
router.replaceAll(*replaceOnrampScreens.toTypedArray())
|
||||
}
|
||||
|
|
@ -116,7 +117,12 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
reduxStateHolder.dispatchDialogShow(StateDialog.ScanFailsDialog(StateDialog.ScanFailsSource.MAIN, onTryAgain))
|
||||
}
|
||||
|
||||
override fun openNFTCollectionsScreen(userWalletId: UserWalletId) {
|
||||
router.push(AppRoute.NFTCollections(userWalletId))
|
||||
override fun openNFT(userWallet: UserWallet) {
|
||||
router.push(
|
||||
AppRoute.NFT(
|
||||
userWalletId = userWallet.walletId,
|
||||
walletName = userWallet.name,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable
|
|||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
|
|
@ -33,8 +34,8 @@ internal interface InnerWalletRouter {
|
|||
/** Open onboarding screen */
|
||||
fun openOnboardingScreen(scanResponse: ScanResponse, continueBackup: Boolean = false)
|
||||
|
||||
/** Open onramp success screen for [externalTxId] */
|
||||
fun openOnrampSuccessScreen(externalTxId: String)
|
||||
/** Open onramp success screen for [txId] */
|
||||
fun openOnrampSuccessScreen(txId: String)
|
||||
|
||||
/** Open transaction history website by [url] */
|
||||
fun openUrl(url: String)
|
||||
|
|
@ -58,5 +59,5 @@ internal interface InnerWalletRouter {
|
|||
fun openScanFailedDialog(onTryAgain: () -> Unit)
|
||||
|
||||
/** Open NFT collections screen */
|
||||
fun openNFTCollectionsScreen(userWalletId: UserWalletId)
|
||||
fun openNFT(userWallet: UserWallet)
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import com.tangem.domain.onramp.OnrampUpdateTransactionStatusUseCase
|
|||
import com.tangem.domain.onramp.model.OnrampStatus
|
||||
import com.tangem.domain.onramp.model.OnrampStatus.Status.*
|
||||
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -40,22 +41,22 @@ internal class OnrampStatusFactory @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun updateOnrmapTransactionStatuses() = withContext(dispatchers.io) {
|
||||
suspend fun updateOnrmapTransactionStatuses(userWallet: UserWallet) = withContext(dispatchers.io) {
|
||||
val singleWalletState = stateHolder.getSelectedWallet() as? WalletState.SingleCurrency.Content
|
||||
?: return@withContext
|
||||
|
||||
singleWalletState.expressTxs.map { tx ->
|
||||
async {
|
||||
if (tx is ExpressTransactionStateUM.OnrampUM) {
|
||||
updateOnrampTxStatus(tx)
|
||||
updateOnrampTxStatus(userWallet, tx)
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
}
|
||||
|
||||
private suspend fun updateOnrampTxStatus(onrampTx: ExpressTransactionStateUM.OnrampUM) {
|
||||
private suspend fun updateOnrampTxStatus(userWallet: UserWallet, onrampTx: ExpressTransactionStateUM.OnrampUM) {
|
||||
if (!onrampTx.activeStatus.isTerminal) {
|
||||
getOnrampStatusUseCase(onrampTx.info.txId).fold(
|
||||
getOnrampStatusUseCase(userWallet = userWallet, onrampTx.info.txId).fold(
|
||||
ifLeft = {
|
||||
Timber.e("Couldn't update onramp status. $it")
|
||||
},
|
||||
|
|
@ -93,10 +94,12 @@ internal class OnrampStatusFactory @Inject constructor(
|
|||
PaymentProcessing,
|
||||
Paid,
|
||||
Sending,
|
||||
RefundInProgress,
|
||||
-> ExpressAnalyticsStatus.InProgress
|
||||
Verifying -> ExpressAnalyticsStatus.KYC
|
||||
Failed -> ExpressAnalyticsStatus.Fail
|
||||
Finished -> ExpressAnalyticsStatus.Done
|
||||
Refunded -> ExpressAnalyticsStatus.Refunded
|
||||
null -> null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
internal data class VisaTxDetailsBottomSheetConfig(
|
||||
val transaction: Transaction,
|
||||
val requests: ImmutableList<Request>,
|
||||
val onDisputeClick: () -> Unit,
|
||||
) : TangemBottomSheetConfigContent {
|
||||
|
||||
data class Transaction(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.nft.models.*
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNFTItemUM
|
||||
|
|
@ -16,14 +15,8 @@ internal class SetNFTCollectionsTransformer(
|
|||
override fun transform(prevState: WalletState): WalletState = when (prevState) {
|
||||
is WalletState.MultiCurrency.Content -> prevState.copy(
|
||||
nftState = when {
|
||||
nftCollections.allCollectionsFailed() ->
|
||||
WalletNFTItemUM.Failed
|
||||
nftCollections.anyCollectionFailed() && nftCollections.allLoadedCollectionsEmpty() ->
|
||||
WalletNFTItemUM.Failed
|
||||
nftCollections.allCollectionsLoaded() && nftCollections.allCollectionsEmpty() ->
|
||||
nftCollections.allLoadedCollectionsEmpty() ->
|
||||
WalletNFTItemUM.Empty(onItemClick)
|
||||
!nftCollections.allCollectionsLoaded() && nftCollections.allCollectionsEmpty() ->
|
||||
WalletNFTItemUM.Loading
|
||||
else -> createContentNFTItemUM(onItemClick)
|
||||
},
|
||||
)
|
||||
|
|
@ -41,10 +34,6 @@ internal class SetNFTCollectionsTransformer(
|
|||
.map { it.content }
|
||||
.filterIsInstance<NFTCollections.Content.Collections>()
|
||||
|
||||
val isFlickering = collectionsContent
|
||||
.map { it.source }
|
||||
.any { it == StatusSource.CACHE }
|
||||
|
||||
val collections = collectionsContent
|
||||
.mapNotNull { it.collections }
|
||||
.flatten()
|
||||
|
|
@ -65,7 +54,7 @@ internal class SetNFTCollectionsTransformer(
|
|||
collectionsCount = collections.size,
|
||||
assetsCount = collections
|
||||
.sumOf { it.count },
|
||||
isFlickering = isFlickering,
|
||||
isFlickering = false,
|
||||
onItemClick = onItemClick,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ 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
|
||||
|
|
@ -144,17 +146,23 @@ internal class SetVisaInfoTransformer(
|
|||
),
|
||||
)
|
||||
|
||||
return return persistentListOf(
|
||||
return persistentListOf(
|
||||
WalletManageButton.Receive(
|
||||
enabled = true,
|
||||
dimContent = false,
|
||||
onClick = { clickIntents.onReceiveClick(cryptoCurrencyStatus = cryptoCurrencyStatus) },
|
||||
onLongClick = { clickIntents.onCopyAddressLongClick(cryptoCurrencyStatus = cryptoCurrencyStatus) },
|
||||
onClick = {
|
||||
clickIntents.onReceiveClick(cryptoCurrencyStatus, MainScreenAnalyticsEvent.ButtonReceive)
|
||||
},
|
||||
onLongClick = {
|
||||
clickIntents.onCopyAddressLongClick(cryptoCurrencyStatus)
|
||||
},
|
||||
),
|
||||
WalletManageButton.Buy(
|
||||
enabled = true,
|
||||
dimContent = false,
|
||||
onClick = { clickIntents.onMultiWalletBuyClick(userWalletId = userWallet.walletId) },
|
||||
onClick = {
|
||||
clickIntents.onMultiWalletBuyClick(userWalletId = userWallet.walletId, screenType = VISA_TYPE)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
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
|
||||
|
|
@ -13,6 +15,7 @@ import java.math.BigDecimal
|
|||
|
||||
internal class BalancesAndLimitsBottomSheetConverter(
|
||||
private val eventSender: WalletEventSender,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Converter<VisaCurrency, BalancesAndLimitsBottomSheetConfig> {
|
||||
|
||||
override fun convert(value: VisaCurrency): BalancesAndLimitsBottomSheetConfig {
|
||||
|
|
@ -28,23 +31,25 @@ internal class BalancesAndLimitsBottomSheetConverter(
|
|||
blockedBalance = value.balances.blocked.let(::formatAmount),
|
||||
debit = value.balances.debt.let(::formatAmount),
|
||||
amlVerified = value.balances.verified.let(::formatAmount),
|
||||
onInfoClick = this::showBalanceInfo,
|
||||
onInfoClick = this::balanceInfoOnClick,
|
||||
),
|
||||
limit = BalancesAndLimitsBottomSheetConfig.Limit(
|
||||
availableBy = DateTimeFormatters.formatDate(date = value.limits.expirationDate),
|
||||
total = otpLimit,
|
||||
other = noOtpLimit,
|
||||
singleTransaction = value.limits.singleTransaction.let(::formatAmount),
|
||||
onInfoClick = { showLimitInfo(otpLimit, noOtpLimit) },
|
||||
onInfoClick = { limitInfoOnClick(otpLimit, noOtpLimit) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun showBalanceInfo() {
|
||||
private fun balanceInfoOnClick() {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.NoticeBalancesInfo)
|
||||
eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaBalancesInfo))
|
||||
}
|
||||
|
||||
private fun showLimitInfo(totalLimit: String, otherLimit: String) {
|
||||
private fun limitInfoOnClick(totalLimit: String, otherLimit: String) {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.NoticeLimitsInfo)
|
||||
eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaLimitsInfo(totalLimit, otherLimit)))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressLinkUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusItemState
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusItemUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusUM
|
||||
import com.tangem.common.ui.expressStatus.state.*
|
||||
import com.tangem.common.ui.notifications.ExpressNotificationsUM
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
|
|
@ -22,11 +19,8 @@ import com.tangem.domain.onramp.model.OnrampStatus
|
|||
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -142,8 +136,11 @@ internal class SingleWalletOnrampTransactionConverter(
|
|||
|
||||
private fun getIconState(status: OnrampStatus.Status): ExpressTransactionStateIconUM {
|
||||
return when (status) {
|
||||
OnrampStatus.Status.Verifying -> ExpressTransactionStateIconUM.Warning
|
||||
OnrampStatus.Status.Failed -> ExpressTransactionStateIconUM.Error
|
||||
OnrampStatus.Status.Verifying,
|
||||
-> ExpressTransactionStateIconUM.Warning
|
||||
OnrampStatus.Status.Refunded,
|
||||
OnrampStatus.Status.Failed,
|
||||
-> ExpressTransactionStateIconUM.Error
|
||||
else -> ExpressTransactionStateIconUM.None
|
||||
}
|
||||
}
|
||||
|
|
@ -226,6 +223,10 @@ internal class SingleWalletOnrampTransactionConverter(
|
|||
wrappedList(currency.name),
|
||||
)
|
||||
}
|
||||
this == OnrampStatus.Status.RefundInProgress ||
|
||||
this == OnrampStatus.Status.Refunded -> {
|
||||
resourceReference(R.string.express_exchange_status_failed)
|
||||
}
|
||||
else -> {
|
||||
resourceReference(R.string.express_status_bought, wrappedList(currency.name))
|
||||
}
|
||||
|
|
@ -247,6 +248,12 @@ internal class SingleWalletOnrampTransactionConverter(
|
|||
wrappedList(currency.name),
|
||||
)
|
||||
}
|
||||
this == OnrampStatus.Status.RefundInProgress -> {
|
||||
resourceReference(R.string.express_exchange_status_refunding)
|
||||
}
|
||||
this == OnrampStatus.Status.Refunded -> {
|
||||
resourceReference(R.string.express_exchange_status_refunded)
|
||||
}
|
||||
else -> {
|
||||
resourceReference(
|
||||
R.string.express_exchange_status_sent,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ internal class VisaTxDetailsBottomSheetConverter(
|
|||
return VisaTxDetailsBottomSheetConfig(
|
||||
transaction = createTransaction(value),
|
||||
requests = value.requests.map(::createRequest).toImmutableList(),
|
||||
onDisputeClick = { clickIntents.onDisputeClick(value) },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.utils
|
||||
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent.Companion.WALLET_TYPE
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
|
|
@ -103,7 +104,7 @@ internal class WalletLoadingStateFactory(
|
|||
WalletManageButton.Buy(
|
||||
enabled = true,
|
||||
dimContent = false,
|
||||
onClick = { clickIntents.onMultiWalletBuyClick(userWalletId = userWallet.walletId) },
|
||||
onClick = { clickIntents.onMultiWalletBuyClick(userWalletId = userWallet.walletId, WALLET_TYPE) },
|
||||
),
|
||||
WalletManageButton.Swap(
|
||||
enabled = true,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ internal class WalletNFTListSubscriber(
|
|||
SetNFTCollectionsTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
nftCollections = it,
|
||||
onItemClick = { clickIntents.onNFTClick(userWallet.walletId) },
|
||||
onItemClick = { clickIntents.onNFTClick(userWallet) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ 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
|
||||
|
|
@ -15,6 +16,7 @@ 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
|
||||
|
|
@ -36,26 +38,45 @@ internal fun VisaTxDetailsBottomSheet(config: TangemBottomSheetConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun VisaTxDetailsBottomSheetContent(config: VisaTxDetailsBottomSheetConfig, modifier: Modifier = Modifier) {
|
||||
ContentContainer(
|
||||
modifier = modifier,
|
||||
blocksCount = config.requests.size.inc(),
|
||||
title = {
|
||||
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,
|
||||
)
|
||||
},
|
||||
block = { index ->
|
||||
if (index == 0) {
|
||||
}
|
||||
|
||||
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)
|
||||
} else {
|
||||
BlockchainRequestBlock(config.requests[index - 1])
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
items(config.requests) { item ->
|
||||
BlockchainRequestBlock(item)
|
||||
}
|
||||
|
||||
item {
|
||||
DisputeButton(config.onDisputeClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -173,38 +194,14 @@ private fun BlockchainRequestBlock(request: VisaTxDetailsBottomSheetConfig.Reque
|
|||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun ContentContainer(
|
||||
blocksCount: Int,
|
||||
title: @Composable BoxScope.() -> Unit,
|
||||
block: @Composable ColumnScope.(Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier.background(TangemTheme.colors.background.secondary),
|
||||
contentPadding = PaddingValues(
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing12),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
stickyHeader {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size44)
|
||||
.background(TangemTheme.colors.background.secondary),
|
||||
contentAlignment = Alignment.Center,
|
||||
content = title,
|
||||
)
|
||||
}
|
||||
items(blocksCount) { index ->
|
||||
Column {
|
||||
block(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
|
|
@ -249,20 +246,8 @@ private class VisaTxDetailsBottomSheetParameterProvider :
|
|||
txStatus = "confirmed",
|
||||
onExploreClick = {},
|
||||
),
|
||||
VisaTxDetailsBottomSheetConfig.Request(
|
||||
id = "524582128501966799",
|
||||
type = "settlement",
|
||||
status = "accepted",
|
||||
blockchainAmount = "1.0614 USDT",
|
||||
transactionAmount = "0.99 €",
|
||||
currencyCode = "978",
|
||||
errorCode = 0,
|
||||
date = "2023-12-01 00:01:00.000 +0300",
|
||||
txHash = "0x635841d5fbdf1087cdd929019c863ee88a7165e4340bc17ddd0b1d04dfb11daa",
|
||||
txStatus = "confirmed",
|
||||
onExploreClick = {},
|
||||
),
|
||||
),
|
||||
onDisputeClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue