From 460d7f6c4d25ec9499136b28f8131b3485851c5b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Nov 2024 21:23:19 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/OnrampDomainModule.kt | 7 +- .../ExpressStatusNotificationBlock.kt | 30 +-- .../DefaultOnrampTransactionRepository.kt | 7 + .../domain/onramp/GetOnrampStatusUseCase.kt | 6 +- .../onramp/GetOnrampTransactionsUseCase.kt | 21 +- .../OnrampTransactionRepository.kt | 3 + .../tokendetails/TokenDetailsPreviewData.kt | 6 +- .../state/SwapTransactionsState.kt | 43 ----- .../tokendetails/state/TokenDetailsState.kt | 3 +- .../state/express/ExchangeStatusState.kt | 14 ++ .../express/ExpressTransactionStateUM.kt | 20 +- ...nDetailsOnrampTransactionStateConverter.kt | 23 ++- .../TokenDetailsSkeletonStateConverter.kt | 3 +- .../state/factory/TokenDetailsStateFactory.kt | 27 --- ...enDetailsSwapTransactionsStateConverter.kt | 139 +++++++++----- .../factory/express/ExchangeStatusFactory.kt | 96 +++++----- .../factory/express/ExpressStatusFactory.kt | 162 ++++++++++++++++ .../factory/express/OnrampStatusFactory.kt | 133 +++++-------- .../tokendetails/ui/TokenDetailsScreen.kt | 25 +-- .../express/ExpressStatusBottomSheet.kt | 27 +++ ...mpStatusItems.kt => ExpressStatusItems.kt} | 35 ++-- .../express/exchange/ExchangeStatusBlock.kt | 2 +- ...kt => ExchangeStatusBottomSheetContent.kt} | 59 ++---- .../express/exchange/ExchangeStatusItems.kt | 47 ----- ...t.kt => OnrampStatusBottomSheetContent.kt} | 53 ++--- .../viewmodels/TokenDetailsClickIntents.kt | 4 +- .../viewmodels/TokenDetailsViewModel.kt | 181 ++++-------------- 27 files changed, 583 insertions(+), 593 deletions(-) delete mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExchangeStatusState.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheet.kt rename features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/{onramp/OnrampStatusItems.kt => ExpressStatusItems.kt} (53%) rename features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/{ExchangeStatusBottomSheet.kt => ExchangeStatusBottomSheetContent.kt} (65%) delete mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusItems.kt rename features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/{OnrampStatusBottomSheet.kt => OnrampStatusBottomSheetContent.kt} (56%) diff --git a/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt index 3db5dadfc6..8c5ddc6502 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt @@ -54,9 +54,14 @@ internal object OnrampDomainModule { @Singleton fun provideGetOnrampStatusUseCase( onrampRepository: OnrampRepository, + onrampTransactionRepository: OnrampTransactionRepository, onrampErrorResolver: OnrampErrorResolver, ): GetOnrampStatusUseCase { - return GetOnrampStatusUseCase(onrampRepository, onrampErrorResolver) + return GetOnrampStatusUseCase( + onrampRepository, + onrampTransactionRepository, + onrampErrorResolver, + ) } @Provides diff --git a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusNotificationBlock.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusNotificationBlock.kt index b93ad58334..58693f2e00 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusNotificationBlock.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusNotificationBlock.kt @@ -1,8 +1,9 @@ package com.tangem.common.ui.expressStatus -import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.tangem.common.ui.notifications.ExpressNotificationsUM @@ -12,21 +13,20 @@ import com.tangem.core.ui.res.TangemTheme @Composable fun ExpressStatusNotificationBlock(state: NotificationUM?) { - AnimatedContent( - targetState = state, + AnimatedVisibility( + visible = state?.config != null, modifier = Modifier.padding(top = 12.dp), label = "Express Status Notification Change", - ) { notification -> - if (notification?.config != null) { - Notification( - config = notification.config, - iconTint = when (state) { - is ExpressNotificationsUM.NeedVerification -> TangemTheme.colors.icon.attention - is ExpressNotificationsUM.FailedByProvider -> TangemTheme.colors.icon.warning - else -> null - }, - containerColor = TangemTheme.colors.background.action, - ) - } + ) { + val wrappedNotification = remember(this) { requireNotNull(state?.config) } + Notification( + config = wrappedNotification, + iconTint = when (state) { + is ExpressNotificationsUM.NeedVerification -> TangemTheme.colors.icon.attention + is ExpressNotificationsUM.FailedByProvider -> TangemTheme.colors.icon.warning + else -> null + }, + containerColor = TangemTheme.colors.background.action, + ) } } \ No newline at end of file diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampTransactionRepository.kt b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampTransactionRepository.kt index c31d07714c..5ca1572531 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampTransactionRepository.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampTransactionRepository.kt @@ -6,6 +6,7 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.getObjectSet import com.tangem.datasource.local.preferences.utils.getObjectSetSync +import com.tangem.domain.onramp.model.OnrampStatus import com.tangem.domain.onramp.model.cache.OnrampTransaction import com.tangem.domain.onramp.repositories.OnrampTransactionRepository import com.tangem.domain.tokens.model.CryptoCurrency @@ -62,6 +63,12 @@ internal class DefaultOnrampTransactionRepository( }.map(transactionConverter::convert) } + override suspend fun updateTransactionStatus(txId: String, status: OnrampStatus.Status) = + withContext(dispatchers.io) { + val updatedTx = getTransactionById(txId)?.copy(status = status) ?: return@withContext + storeTransaction(updatedTx) + } + override suspend fun removeTransaction(txId: String) { withContext(dispatchers.io) { appPreferencesStore.editData { mutablePreferences -> diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampStatusUseCase.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampStatusUseCase.kt index 4692c6d6b7..3853303b04 100644 --- a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampStatusUseCase.kt +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampStatusUseCase.kt @@ -5,15 +5,19 @@ import com.tangem.domain.onramp.model.OnrampError import com.tangem.domain.onramp.model.OnrampStatus import com.tangem.domain.onramp.repositories.OnrampErrorResolver import com.tangem.domain.onramp.repositories.OnrampRepository +import com.tangem.domain.onramp.repositories.OnrampTransactionRepository class GetOnrampStatusUseCase( private val onrampRepository: OnrampRepository, + private val onrampTransactionRepository: OnrampTransactionRepository, private val errorResolver: OnrampErrorResolver, ) { suspend operator fun invoke(txId: String): Either { return Either.catch { - onrampRepository.getStatus(txId) + val status = onrampRepository.getStatus(txId) + onrampTransactionRepository.updateTransactionStatus(txId = txId, status = status.status) + status }.mapLeft { errorResolver.resolve(it) } diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampTransactionsUseCase.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampTransactionsUseCase.kt index af51320013..4deb1ece79 100644 --- a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampTransactionsUseCase.kt +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampTransactionsUseCase.kt @@ -1,12 +1,17 @@ package com.tangem.domain.onramp import arrow.core.Either +import arrow.core.left +import arrow.core.right import com.tangem.domain.onramp.model.OnrampError import com.tangem.domain.onramp.model.cache.OnrampTransaction import com.tangem.domain.onramp.repositories.OnrampTransactionRepository import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEmpty class GetOnrampTransactionsUseCase( private val onrampTransactionRepository: OnrampTransactionRepository, @@ -15,14 +20,12 @@ class GetOnrampTransactionsUseCase( operator fun invoke( userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID, - ): Either>> { - return Either.catch { - onrampTransactionRepository.getTransactions( - userWalletId = userWalletId, - cryptoCurrencyId = cryptoCurrencyId, - ) - }.mapLeft { - OnrampError.UnknownError - } + ): Flow>> { + return onrampTransactionRepository.getTransactions( + userWalletId = userWalletId, + cryptoCurrencyId = cryptoCurrencyId, + ).map { it.right() } + .catch { OnrampError.UnknownError.left() } + .onEmpty { OnrampError.UnknownError.left() } } } \ No newline at end of file diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampTransactionRepository.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampTransactionRepository.kt index 24105314b1..deca8ceda9 100644 --- a/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampTransactionRepository.kt +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampTransactionRepository.kt @@ -1,5 +1,6 @@ package com.tangem.domain.onramp.repositories +import com.tangem.domain.onramp.model.OnrampStatus import com.tangem.domain.onramp.model.cache.OnrampTransaction import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId @@ -13,5 +14,7 @@ interface OnrampTransactionRepository { fun getTransactions(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID): Flow> + suspend fun updateTransactionStatus(txId: String, status: OnrampStatus.Status) + suspend fun removeTransaction(txId: String) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt index 3df712af86..625959ab86 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt @@ -307,8 +307,7 @@ internal object TokenDetailsPreviewData { ), dialogConfig = null, pendingTxs = persistentListOf(), - swapTxs = persistentListOf(), - onrampTxs = persistentListOf(), + expressTxs = persistentListOf(), pullToRefreshConfig = pullToRefreshConfig, bottomSheetConfig = null, isBalanceHidden = false, @@ -338,8 +337,7 @@ internal object TokenDetailsPreviewData { ), dialogConfig = null, pendingTxs = persistentListOf(), - swapTxs = persistentListOf(), - onrampTxs = persistentListOf(), + expressTxs = persistentListOf(), pullToRefreshConfig = pullToRefreshConfig, bottomSheetConfig = null, isBalanceHidden = false, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt deleted file mode 100644 index 88b4fbfae5..0000000000 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.tangem.feature.tokendetails.presentation.tokendetails.state - -import androidx.compose.runtime.Immutable -import com.tangem.core.ui.components.currency.icon.CurrencyIconState -import com.tangem.core.ui.extensions.TextReference -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.feature.swap.domain.models.domain.ExchangeStatus -import com.tangem.feature.swap.domain.models.domain.SwapProvider -import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications -import kotlinx.collections.immutable.ImmutableList - -internal data class SwapTransactionsState( - val txId: String, - val provider: SwapProvider, - val txUrl: String? = null, - val txExternalId: String? = null, - val timestamp: TextReference, - val fiatSymbol: String, - val activeStatus: ExchangeStatus?, - val hasFailed: Boolean, - val statuses: ImmutableList, - val notification: ExchangeStatusNotifications? = null, - val toCryptoCurrency: CryptoCurrency, - val toCryptoAmount: String, - val toFiatAmount: String, - val toCurrencyIcon: CurrencyIconState, - val fromCryptoCurrency: CryptoCurrency, - val fromCryptoAmount: TextReference, - val fromFiatAmount: String, - val fromCurrencyIcon: CurrencyIconState, - val showProviderLink: Boolean, - val isRefundTerminalStatus: Boolean = true, - val onClick: () -> Unit, - val onGoToProviderClick: (String) -> Unit, -) - -@Immutable -internal data class ExchangeStatusState( - val status: ExchangeStatus, - val text: TextReference, - val isActive: Boolean, - val isDone: Boolean, -) \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt index 4e2755339a..8ece0d02e9 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt @@ -21,8 +21,7 @@ internal data class TokenDetailsState( val stakingBlocksState: StakingBlockUM?, val notifications: ImmutableList, val pendingTxs: PersistentList, - val swapTxs: PersistentList, - val onrampTxs: PersistentList, + val expressTxs: PersistentList, val txHistoryState: TxHistoryState, val dialogConfig: TokenDetailsDialogConfig?, val pullToRefreshConfig: PullToRefreshConfig, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExchangeStatusState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExchangeStatusState.kt new file mode 100644 index 0000000000..c6d11b2540 --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExchangeStatusState.kt @@ -0,0 +1,14 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.state.express + +import com.tangem.core.ui.extensions.TextReference +import com.tangem.feature.swap.domain.models.domain.ExchangeStatus +import javax.annotation.concurrent.Immutable + +@Deprecated("Use ExpressStatusBlock from common") +@Immutable +internal data class ExchangeStatusState( + val status: ExchangeStatus, + val text: TextReference, + val isActive: Boolean, + val isDone: Boolean, +) \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExpressTransactionStateUM.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExpressTransactionStateUM.kt index c4f11dd934..fb777dc79d 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExpressTransactionStateUM.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExpressTransactionStateUM.kt @@ -5,9 +5,9 @@ import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.onramp.model.OnrampStatus +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.feature.swap.domain.models.domain.ExchangeStatus import com.tangem.feature.swap.domain.models.domain.SwapProvider -import com.tangem.feature.tokendetails.presentation.tokendetails.state.ExchangeStatusState import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications import kotlinx.collections.immutable.ImmutableList @@ -23,6 +23,8 @@ internal sealed class ExpressTransactionStateUM { val notification: ExchangeStatusNotifications? = null, val showProviderLink: Boolean, val isRefundTerminalStatus: Boolean, + val fromCryptoCurrency: CryptoCurrency, + val toCryptoCurrency: CryptoCurrency, ) : ExpressTransactionStateUM() data class OnrampUM( @@ -30,20 +32,22 @@ internal sealed class ExpressTransactionStateUM { val providerName: String, // todo onramp fix after SwapProvider moved to own module val providerImageUrl: String, // todo onramp fix after SwapProvider moved to own module val providerType: String, // todo onramp fix after SwapProvider moved to own module - val cryptoCurrencyName: String, val activeStatus: OnrampStatus.Status, ) : ExpressTransactionStateUM() } internal data class ExpressTransactionStateInfoUM( + val title: TextReference, val status: ExpressStatusUM, val notification: NotificationUM?, val txId: String, - val txUrl: String?, val txExternalId: String?, - val timestamp: TextReference, + val txExternalUrl: String?, + val timestamp: Long, + val timestampFormatted: TextReference, val onGoToProviderClick: (String) -> Unit, val onClick: () -> Unit, + val iconState: ExpressTransactionStateIconUM, val toAmount: TextReference, val toFiatAmount: TextReference?, @@ -54,4 +58,10 @@ internal data class ExpressTransactionStateInfoUM( val fromFiatAmount: TextReference?, val fromAmountSymbol: String, val fromCurrencyIcon: CurrencyIconState, -) \ No newline at end of file +) + +internal enum class ExpressTransactionStateIconUM { + Warning, + Error, + None, +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt index e50c7e3977..b0a556cbc9 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt @@ -14,13 +14,14 @@ import com.tangem.core.ui.extensions.wrappedList 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.core.ui.utils.toDateFormatWithTodayYesterday import com.tangem.core.ui.utils.toTimeFormat import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.onramp.model.OnrampStatus import com.tangem.domain.onramp.model.cache.OnrampTransaction import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateIconUM import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateInfoUM import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents @@ -43,15 +44,17 @@ internal class TokenDetailsOnrampTransactionStateConverter( val appCurrency = appCurrencyProvider() return ExpressTransactionStateUM.OnrampUM( info = ExpressTransactionStateInfoUM( + title = resourceReference(id = R.string.express_status_buying, wrappedList(cryptoCurrency.name)), status = convertStatuses(value.status, value.externalTxUrl), notification = getNotification(value.status, value.externalTxUrl), txId = value.txId, txExternalId = value.externalTxId, - txUrl = value.externalTxUrl, - timestamp = resourceReference( + txExternalUrl = value.externalTxUrl, + timestamp = value.timestamp, + timestampFormatted = resourceReference( R.string.send_date_format, wrappedList( - value.timestamp.toTimeFormat(DateTimeFormatters.dateFormatter), + value.timestamp.toDateFormatWithTodayYesterday(), value.timestamp.toTimeFormat(), ), ), @@ -82,13 +85,13 @@ internal class TokenDetailsOnrampTransactionStateConverter( url = value.fromCurrency.image, fallbackResId = R.drawable.ic_currency_24, ), + iconState = getIconState(value.status), onGoToProviderClick = clickIntents::onGoToProviderClick, - onClick = { clickIntents.onOnrampTransactionClick(value.txId) }, + onClick = { clickIntents.onExpressTransactionClick(value.txId) }, ), providerName = value.providerName, providerImageUrl = value.providerImageUrl, providerType = value.providerType, - cryptoCurrencyName = cryptoCurrency.name, activeStatus = value.status, ) } @@ -110,6 +113,14 @@ internal class TokenDetailsOnrampTransactionStateConverter( } } + private fun getIconState(status: OnrampStatus.Status): ExpressTransactionStateIconUM { + return when (status) { + OnrampStatus.Status.Verifying -> ExpressTransactionStateIconUM.Warning + OnrampStatus.Status.Failed -> ExpressTransactionStateIconUM.Error + else -> ExpressTransactionStateIconUM.None + } + } + private fun convertStatuses(status: OnrampStatus.Status, externalTxUrl: String?): ExpressStatusUM { val statuses = with(status) { persistentListOf( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt index 89df9ace33..07f98c6f10 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt @@ -66,8 +66,7 @@ internal class TokenDetailsSkeletonStateConverter( stakingBlocksState = StakingBlockUM.Loading(iconState).takeIf { isSupportedInMobileApp }, notifications = persistentListOf(), pendingTxs = persistentListOf(), - swapTxs = persistentListOf(), - onrampTxs = persistentListOf(), + expressTxs = persistentListOf(), txHistoryState = TxHistoryState.Content( contentItems = MutableStateFlow( value = TxHistoryState.getDefaultLoadingTransactions(clickIntents::onExploreClick), diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 4b31124dc3..64b4e3f0b7 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -28,7 +28,6 @@ import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetUserWalletUseCase -import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsAppBarMenuConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState @@ -36,7 +35,6 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.component import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadedTxHistoryConverter import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter.TokenDetailsLoadingTxHistoryModel -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.exchange.ExchangeStatusBottomSheetConfig import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.features.tokendetails.impl.R import com.tangem.utils.Provider @@ -301,31 +299,6 @@ internal class TokenDetailsStateFactory( return state.copy(notifications = notificationConverter.removeHederaAssociateWarning(state)) } - fun getStateWithExchangeStatusBottomSheet(swapTxState: SwapTransactionsState): TokenDetailsState { - return currentStateProvider().copy( - bottomSheetConfig = TangemBottomSheetConfig( - isShow = true, - onDismissRequest = clickIntents::onDismissBottomSheet, - content = ExchangeStatusBottomSheetConfig( - value = swapTxState, - ), - ), - ) - } - - fun updateStateWithExchangeStatusBottomSheet(swapTxState: SwapTransactionsState): TangemBottomSheetConfig? { - val state = currentStateProvider() - val bottomSheetConfig = state.bottomSheetConfig - val currentConfig = bottomSheetConfig?.content as? ExchangeStatusBottomSheetConfig ?: return bottomSheetConfig - return bottomSheetConfig.copy( - content = if (currentConfig.value != swapTxState) { - ExchangeStatusBottomSheetConfig(swapTxState) - } else { - currentConfig - }, - ) - } - fun getStateAndTriggerEvent( state: TokenDetailsState, errorMessage: TextReference, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt index 5f36f79e34..4ae19831d2 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt @@ -1,13 +1,16 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory +import com.tangem.common.ui.expressStatus.state.ExpressLinkUM +import com.tangem.common.ui.expressStatus.state.ExpressStatusUM import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.extensions.wrappedList 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.BigDecimalFormatter import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday import com.tangem.core.ui.utils.toTimeFormat import com.tangem.domain.appcurrency.model.AppCurrency @@ -17,9 +20,12 @@ import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent import com.tangem.feature.swap.domain.models.domain.ExchangeStatus import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel -import com.tangem.feature.tokendetails.presentation.tokendetails.state.ExchangeStatusState -import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState +import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionModel import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeStatusState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateIconUM +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateInfoUM +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.features.tokendetails.impl.R import com.tangem.utils.Provider @@ -32,25 +38,27 @@ import timber.log.Timber import java.math.BigDecimal import java.util.Locale +// Fixme [REDACTED_JIRA] +@Suppress("LargeClass") internal class TokenDetailsSwapTransactionsStateConverter( private val clickIntents: TokenDetailsClickIntents, private val cryptoCurrency: CryptoCurrency, private val analyticsEventsHandlerProvider: Provider, appCurrencyProvider: Provider, -) : Converter> { +) : Converter> { private val iconStateConverter = CryptoCurrencyToIconStateConverter() private val appCurrency = appCurrencyProvider() - override fun convert(value: Unit): PersistentList { + override fun convert(value: Unit): PersistentList { return persistentListOf() } fun convert( savedTransactions: List, quotes: Set, - ): PersistentList { - val result = mutableListOf() + ): PersistentList { + val result = mutableListOf() savedTransactions .forEach { swapTransaction -> @@ -72,42 +80,26 @@ internal class TokenDetailsSwapTransactionsStateConverter( fromFiatAmount = quote.fiatRate.multiply(fromAmount) } } - val timestamp = transaction.timestamp val notifications = getNotification(transaction.status?.status, transaction.status?.txExternalUrl, null) val showProviderLink = getShowProviderLink(notifications, transaction.status) result.add( - SwapTransactionsState( - txId = transaction.txId, + ExpressTransactionStateUM.ExchangeUM( provider = transaction.provider, - txUrl = transaction.status?.txExternalUrl, - txExternalId = transaction.status?.txExternalId, - timestamp = TextReference.Str( - "${timestamp.toDateFormatWithTodayYesterday()}, ${timestamp.toTimeFormat()}", - ), - fiatSymbol = appCurrency.symbol, statuses = getStatuses(transaction.status?.status), - hasFailed = transaction.status?.status == ExchangeStatus.Failed, - activeStatus = transaction.status?.status, notification = notifications, - toCryptoCurrency = toCryptoCurrency, - toCryptoAmount = toAmount.format { crypto(toCryptoCurrency) }, - toFiatAmount = getFiatAmount(toFiatAmount), - toCurrencyIcon = iconStateConverter.convert(toCryptoCurrency), - fromCryptoCurrency = fromCryptoCurrency, - fromCryptoAmount = stringReference( - fromAmount.format { crypto(fromCryptoCurrency) }, - ), - fromFiatAmount = getFiatAmount(fromFiatAmount), - fromCurrencyIcon = iconStateConverter.convert(fromCryptoCurrency), + activeStatus = transaction.status?.status, showProviderLink = showProviderLink, - onClick = { clickIntents.onSwapTransactionClick(transaction.txId) }, - onGoToProviderClick = { url -> - analyticsEventsHandlerProvider().send( - TokenExchangeAnalyticsEvent.GoToProviderStatus(cryptoCurrency.symbol), - ) - clickIntents.onGoToProviderClick(url = url) - }, + isRefundTerminalStatus = true, + fromCryptoCurrency = fromCryptoCurrency, + toCryptoCurrency = toCryptoCurrency, + info = createStateInfo( + transaction, + toCryptoCurrency, + fromCryptoCurrency, + toFiatAmount, + fromFiatAmount, + ), ), ) } @@ -116,37 +108,79 @@ internal class TokenDetailsSwapTransactionsStateConverter( } fun updateTxStatus( - tx: SwapTransactionsState, + tx: ExpressTransactionStateUM.ExchangeUM, statusModel: ExchangeStatusModel?, refundToken: CryptoCurrency?, isRefundTerminalStatus: Boolean, - ): SwapTransactionsState { + ): ExpressTransactionStateUM.ExchangeUM { if (statusModel == null || tx.activeStatus == statusModel.status) { Timber.e("UpdateTxStatus isn't required. Current status isn't changed") return tx } - val hasFailed = tx.hasFailed || statusModel.status == ExchangeStatus.Failed + val hasFailed = statusModel.status == ExchangeStatus.Failed val notifications = getNotification(statusModel.status, statusModel.txExternalUrl, refundToken) val showProviderLink = getShowProviderLink(notifications, statusModel) return tx.copy( activeStatus = statusModel.status, - hasFailed = hasFailed, notification = notifications, statuses = getStatuses(statusModel.status, hasFailed), - txUrl = statusModel.txExternalUrl, showProviderLink = showProviderLink, isRefundTerminalStatus = isRefundTerminalStatus, + info = tx.info.copy(txExternalUrl = statusModel.txExternalUrl), ) } - private fun getFiatAmount(toFiatAmount: BigDecimal?): String { - return BigDecimalFormatter.formatFiatAmount( - fiatAmount = toFiatAmount, - fiatCurrencyCode = appCurrency.code, - fiatCurrencySymbol = appCurrency.symbol, + private fun createStateInfo( + transaction: SavedSwapTransactionModel, + toCryptoCurrency: CryptoCurrency, + fromCryptoCurrency: CryptoCurrency, + toFiatAmount: BigDecimal?, + fromFiatAmount: BigDecimal?, + ): ExpressTransactionStateInfoUM { + val timestamp = transaction.timestamp + return ExpressTransactionStateInfoUM( + title = resourceReference(R.string.express_exchange_by, wrappedList(transaction.provider.name)), + txId = transaction.txId, + txExternalUrl = transaction.status?.txExternalUrl, + txExternalId = transaction.status?.txExternalId, + timestamp = timestamp, + timestampFormatted = stringReference( + "${timestamp.toDateFormatWithTodayYesterday()}, ${timestamp.toTimeFormat()}", + ), + toAmount = getCryptoAmount(transaction.toCryptoAmount, toCryptoCurrency), + toFiatAmount = getFiatAmount(toFiatAmount), + toCurrencyIcon = iconStateConverter.convert(toCryptoCurrency), + toAmountSymbol = toCryptoCurrency.symbol, + fromAmount = getCryptoAmount(transaction.fromCryptoAmount, fromCryptoCurrency), + fromFiatAmount = getFiatAmount(fromFiatAmount), + fromCurrencyIcon = iconStateConverter.convert(fromCryptoCurrency), + fromAmountSymbol = fromCryptoCurrency.symbol, + onClick = { clickIntents.onExpressTransactionClick(transaction.txId) }, + onGoToProviderClick = { url -> + analyticsEventsHandlerProvider().send( + TokenExchangeAnalyticsEvent.GoToProviderStatus(cryptoCurrency.symbol), + ) + clickIntents.onGoToProviderClick(url = url) + }, + iconState = getIconState(transaction.status?.status), + status = getStatusState(), + notification = null, // fixme [REDACTED_JIRA] ) } + private fun getCryptoAmount(amount: BigDecimal?, cryptoCurrency: CryptoCurrency) = stringReference( + amount.format { crypto(cryptoCurrency) }, + ) + + private fun getFiatAmount(toFiatAmount: BigDecimal?) = stringReference( + toFiatAmount.format { + fiat( + fiatCurrencyCode = appCurrency.code, + fiatCurrencySymbol = appCurrency.symbol, + ) + }, + ) + private fun getNotification( status: ExchangeStatus?, txUrl: String?, @@ -186,6 +220,21 @@ internal class TokenDetailsSwapTransactionsStateConverter( } } + private fun getIconState(status: ExchangeStatus?): ExpressTransactionStateIconUM { + return when (status) { + ExchangeStatus.Verifying -> ExpressTransactionStateIconUM.Warning + ExchangeStatus.Failed, ExchangeStatus.Cancelled -> ExpressTransactionStateIconUM.Error + else -> ExpressTransactionStateIconUM.None + } + } + + // Fixme [REDACTED_JIRA] + private fun getStatusState() = ExpressStatusUM( + title = resourceReference(R.string.express_exchange_status_title), + link = ExpressLinkUM.Empty, + statuses = persistentListOf(), + ) + private fun getShowProviderLink(notifications: ExchangeStatusNotifications?, status: ExchangeStatusModel?) = notifications == null && status?.txExternalUrl != null && status.status != ExchangeStatus.Cancelled diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt index 262493e39a..034bb903d3 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt @@ -14,39 +14,36 @@ import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.feature.swap.domain.SwapTransactionRepository import com.tangem.feature.swap.domain.api.SwapRepository import com.tangem.feature.swap.domain.models.domain.* -import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsSwapTransactionsStateConverter -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.exchange.ExchangeStatusBottomSheetConfig +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheetConfig import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.utils.Provider -import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.withContext @Suppress("LongParameterList") -internal class ExchangeStatusFactory( +internal class ExchangeStatusFactory @AssistedInject constructor( private val swapTransactionRepository: SwapTransactionRepository, private val swapRepository: SwapRepository, private val quotesRepository: QuotesRepository, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, private val swapTransactionStatusStore: SwapTransactionStatusStore, - private val dispatchers: CoroutineDispatcherProvider, - private val clickIntents: TokenDetailsClickIntents, - private val appCurrencyProvider: Provider, - private val analyticsEventsHandlerProvider: Provider, - private val currentStateProvider: Provider, - private val userWalletId: UserWalletId, - private val cryptoCurrency: CryptoCurrency, + @Assisted private val clickIntents: TokenDetailsClickIntents, + @Assisted private val appCurrencyProvider: Provider, + @Assisted private val analyticsEventsHandlerProvider: Provider, + @Assisted private val currentStateProvider: Provider, + @Assisted private val userWalletId: UserWalletId, + @Assisted private val cryptoCurrency: CryptoCurrency, ) { private val swapTransactionsStateConverter by lazy { @@ -58,7 +55,7 @@ internal class ExchangeStatusFactory( ) } - suspend operator fun invoke(): Flow> { + suspend operator fun invoke(): Flow> { val selectedWallet = getSelectedWalletSyncUseCase().fold( ifLeft = { return emptyFlow() }, ifRight = { it }, @@ -82,50 +79,39 @@ internal class ExchangeStatusFactory( } } - suspend fun removeTransactionOnBottomSheetClosed(isForceTerminal: Boolean = false): TokenDetailsState { + suspend fun removeTransactionOnBottomSheetClosed(isForceTerminal: Boolean = false) { val state = currentStateProvider() - val bottomSheetConfig = state.bottomSheetConfig?.content as? ExchangeStatusBottomSheetConfig ?: return state - val selectedTx = bottomSheetConfig.value + val bottomSheetConfig = state.bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig ?: return + val selectedTx = bottomSheetConfig.value as? ExpressTransactionStateUM.ExchangeUM ?: return val shouldTerminate = selectedTx.activeStatus.isTerminal(selectedTx.isRefundTerminalStatus) || isForceTerminal - return if (shouldTerminate) { + if (shouldTerminate) { swapTransactionRepository.removeTransaction( userWalletId = userWalletId, fromCryptoCurrency = selectedTx.fromCryptoCurrency, toCryptoCurrency = selectedTx.toCryptoCurrency, - txId = selectedTx.txId, + txId = selectedTx.info.txId, ) - val filteredTxs = state.swapTxs - .filterNot { it.txId == selectedTx.txId } - .toPersistentList() - state.copy(swapTxs = filteredTxs) - } else { - state } } - suspend fun updateSwapTxStatuses(swapTxList: PersistentList) = withContext(dispatchers.io) { - swapTxList.map { tx -> - async { - val statusModel = getExchangeStatus(tx.txId, tx.provider) - val isRefundTerminalStatus = statusModel?.refundNetwork == null && - statusModel?.refundContractAddress == null && - tx.provider.type != ExchangeProviderType.DEX_BRIDGE - if (tx.activeStatus.isTerminal(isRefundTerminalStatus)) { - tx - } else { - val addedRefundToken = addRefundCurrencyIfNeeded(statusModel, tx.provider.type) - swapTransactionsStateConverter.updateTxStatus( - tx = tx, - statusModel = statusModel, - refundToken = addedRefundToken, - isRefundTerminalStatus = isRefundTerminalStatus, - ) - } - } + suspend fun updateSwapTxStatus(swapTx: ExpressTransactionStateUM.ExchangeUM): ExpressTransactionStateUM.ExchangeUM { + return if (swapTx.activeStatus.isTerminal(swapTx.isRefundTerminalStatus)) { + swapTx + } else { + val statusModel = getExchangeStatus(swapTx.info.txId, swapTx.provider) + val isRefundTerminalStatus = statusModel?.refundNetwork == null && + statusModel?.refundContractAddress == null && + swapTx.provider.type != ExchangeProviderType.DEX_BRIDGE + + val addedRefundToken = addRefundCurrencyIfNeeded(statusModel, swapTx.provider.type) + swapTransactionsStateConverter.updateTxStatus( + tx = swapTx, + statusModel = statusModel, + refundToken = addedRefundToken, + isRefundTerminalStatus = isRefundTerminalStatus, + ) } - .awaitAll() - .toPersistentList() } private suspend fun getExchangeStatus(txId: String, provider: SwapProvider): ExchangeStatusModel? { @@ -177,7 +163,7 @@ internal class ExchangeStatusFactory( private fun getExchangeStatusState( savedTransactions: List?, quotes: Set, - ): PersistentList { + ): PersistentList { if (savedTransactions == null) { return persistentListOf() } @@ -224,4 +210,16 @@ internal class ExchangeStatusFactory( emptySet() } } + + @AssistedFactory + interface Factory { + fun create( + clickIntents: TokenDetailsClickIntents, + appCurrencyProvider: Provider, + analyticsEventsHandlerProvider: Provider, + currentStateProvider: Provider, + userWalletId: UserWalletId, + cryptoCurrency: CryptoCurrency, + ): ExchangeStatusFactory + } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt new file mode 100644 index 0000000000..7d14266a26 --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt @@ -0,0 +1,162 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express + +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.feature.swap.domain.models.domain.ExchangeStatus +import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheetConfig +import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents +import com.tangem.utils.Provider +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.withContext + +@Suppress("LongParameterList") +internal class ExpressStatusFactory @AssistedInject constructor( + @Assisted private val currentStateProvider: Provider, + @Assisted private val clickIntents: TokenDetailsClickIntents, + @Assisted private val cryptoCurrency: CryptoCurrency, + @Assisted appCurrencyProvider: Provider, + @Assisted analyticsEventsHandlerProvider: Provider, + @Assisted userWalletId: UserWalletId, + @Assisted cryptoCurrencyStatusProvider: Provider, + private val dispatchers: CoroutineDispatcherProvider, + private val analyticsEventsHandler: AnalyticsEventHandler, + onrampStatusFactory: OnrampStatusFactory.Factory, + exchangeStatusFactory: ExchangeStatusFactory.Factory, +) { + + private val exchangeStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) { + exchangeStatusFactory.create( + clickIntents = clickIntents, + appCurrencyProvider = appCurrencyProvider, + analyticsEventsHandlerProvider = analyticsEventsHandlerProvider, + currentStateProvider = currentStateProvider, + userWalletId = userWalletId, + cryptoCurrency = cryptoCurrency, + ) + } + + private val onrampStatusFactory by lazy(LazyThreadSafetyMode.NONE) { + onrampStatusFactory.create( + currentStateProvider = currentStateProvider, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + appCurrencyProvider = appCurrencyProvider, + clickIntents = clickIntents, + cryptoCurrency = cryptoCurrency, + userWalletId = userWalletId, + ) + } + + suspend fun getExpressStatuses(): Flow> = combine( + flow = exchangeStatusFactory(), + flow2 = onrampStatusFactory(), + ) { maybeExchange, maybeOnramp -> + persistentListOf( + maybeOnramp, + maybeExchange, + ).flatten() + .sortedByDescending { it.info.timestamp } + .toPersistentList() + } + + suspend fun getUpdatedExpressStatuses(expressTxs: PersistentList) = + withContext(dispatchers.io) { + expressTxs.map { tx -> + async { + when (tx) { + is ExpressTransactionStateUM.ExchangeUM -> exchangeStatusFactory.updateSwapTxStatus(tx) + is ExpressTransactionStateUM.OnrampUM -> onrampStatusFactory.updateOnrmapTxStatus(tx) + } + } + }.awaitAll() + .toPersistentList() + } + + fun getStateWithUpdatedExpressTxs( + expressTxs: PersistentList, + updateBalance: (CryptoCurrency) -> Unit, + ): TokenDetailsState { + val state = currentStateProvider() + val config = state.bottomSheetConfig + val expressBottomSheet = config?.content as? ExpressStatusBottomSheetConfig + val currentTx = expressTxs.firstOrNull { it.info.txId == expressBottomSheet?.value?.info?.txId } + if (currentTx is ExpressTransactionStateUM.ExchangeUM && currentTx.activeStatus == ExchangeStatus.Finished) { + updateBalance(currentTx.toCryptoCurrency) + } + return state.copy( + expressTxs = expressTxs, + bottomSheetConfig = currentTx?.let( + ::updateStateWithExpressStatusBottomSheet, + ) ?: config, + ) + } + + fun getStateWithExpressStatusBottomSheet(expressState: ExpressTransactionStateUM): TokenDetailsState { + analyticsEventsHandler.send(TokenExchangeAnalyticsEvent.CexTxStatusOpened(cryptoCurrency.symbol)) + + return currentStateProvider().copy( + bottomSheetConfig = TangemBottomSheetConfig( + isShow = true, + onDismissRequest = clickIntents::onDismissBottomSheet, + content = ExpressStatusBottomSheetConfig( + value = expressState, + ), + ), + ) + } + + fun updateStateWithExpressStatusBottomSheet(expressState: ExpressTransactionStateUM): TangemBottomSheetConfig? { + val state = currentStateProvider() + val bottomSheetConfig = state.bottomSheetConfig + val currentConfig = bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig ?: return bottomSheetConfig + return bottomSheetConfig.copy( + content = if (currentConfig.value != expressState) { + ExpressStatusBottomSheetConfig(expressState) + } else { + currentConfig + }, + ) + } + + suspend fun removeTransactionOnBottomSheetClosed( + expressState: ExpressTransactionStateUM, + isForceTerminal: Boolean = false, + ) { + when (expressState) { + is ExpressTransactionStateUM.ExchangeUM -> exchangeStatusFactory.removeTransactionOnBottomSheetClosed( + isForceTerminal, + ) + is ExpressTransactionStateUM.OnrampUM -> onrampStatusFactory.removeTransactionOnBottomSheetClosed() + } + } + + @AssistedFactory + interface Factory { + @Suppress("LongParameterList") + fun create( + clickIntents: TokenDetailsClickIntents, + appCurrencyProvider: Provider, + analyticsEventsHandlerProvider: Provider, + currentStateProvider: Provider, + userWalletId: UserWalletId, + cryptoCurrency: CryptoCurrency, + cryptoCurrencyStatusProvider: Provider, + ): ExpressStatusFactory + } +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt index 9d9f0cee0e..686932a3a8 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt @@ -1,43 +1,37 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.onramp.GetOnrampStatusUseCase import com.tangem.domain.onramp.GetOnrampTransactionsUseCase import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase -import com.tangem.domain.onramp.OnrampSaveTransactionUseCase -import com.tangem.domain.onramp.model.cache.OnrampTransaction import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsOnrampTransactionStateConverter -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.onramp.OnrampStatusBottomSheetConfig +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheetConfig import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.utils.Provider -import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.withContext +import kotlinx.coroutines.flow.map +import timber.log.Timber @Suppress("LongParameterList") -internal class OnrampStatusFactory( - private val stateProvider: Provider, - private val cryptoCurrencyStatusProvider: Provider, - private val appCurrencyProvider: Provider, - private val clickIntents: TokenDetailsClickIntents, - private val cryptoCurrency: CryptoCurrency, - private val userWalletId: UserWalletId, +internal class OnrampStatusFactory @AssistedInject constructor( private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase, - private val onrampSaveTransactionUseCase: OnrampSaveTransactionUseCase, private val getOnrampStatusUseCase: GetOnrampStatusUseCase, private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase, - private val dispatchers: CoroutineDispatcherProvider, + @Assisted private val currentStateProvider: Provider, + @Assisted private val cryptoCurrencyStatusProvider: Provider, + @Assisted private val appCurrencyProvider: Provider, + @Assisted private val clickIntents: TokenDetailsClickIntents, + @Assisted private val cryptoCurrency: CryptoCurrency, + @Assisted private val userWalletId: UserWalletId, ) { private val onrampTransactionStateConverter by lazy(LazyThreadSafetyMode.NONE) { @@ -49,80 +43,53 @@ internal class OnrampStatusFactory( ) } - operator fun invoke(): Flow> { + operator fun invoke(): Flow> { return getOnrampTransactionsUseCase( userWalletId = userWalletId, cryptoCurrencyId = cryptoCurrency.id, - ).fold( - ifRight = { savedTransactions -> - savedTransactions - }, - ifLeft = { flowOf(persistentListOf()) }, - ) + ).map { maybeTransaction -> + maybeTransaction.fold( + ifRight = onrampTransactionStateConverter::convertList, + ifLeft = { persistentListOf() }, + ) + } } - fun getStateWithOnrampStatusBottomSheet(onrampTxState: ExpressTransactionStateUM.OnrampUM): TokenDetailsState { - return stateProvider().copy( - bottomSheetConfig = TangemBottomSheetConfig( - isShow = true, - onDismissRequest = clickIntents::onDismissBottomSheet, - content = OnrampStatusBottomSheetConfig(onrampTxState), - ), - ) - } + suspend fun removeTransactionOnBottomSheetClosed() { + val state = currentStateProvider() + val bottomSheetConfig = state.bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig ?: return + val selectedTx = bottomSheetConfig.value as? ExpressTransactionStateUM.OnrampUM ?: return - fun updateOnrampStatusBottomSheet(onrampTxs: List): TokenDetailsState { - val state = stateProvider() - val bottomSheetConfig = state.bottomSheetConfig - val onrampBottomSheet = bottomSheetConfig?.content as? OnrampStatusBottomSheetConfig - - val updatedOnrampTxState = onrampTransactionStateConverter.convertList(onrampTxs).toPersistentList() - val currentOnrampTxState = - updatedOnrampTxState.firstOrNull { it.info.txId == onrampBottomSheet?.value?.info?.txId } - - return state.copy( - onrampTxs = updatedOnrampTxState, - bottomSheetConfig = bottomSheetConfig?.copy( - content = if (currentOnrampTxState != null && currentOnrampTxState != onrampBottomSheet?.value) { - OnrampStatusBottomSheetConfig(currentOnrampTxState) - } else { - onrampBottomSheet - } ?: bottomSheetConfig.content, - ), - ) - } - - suspend fun removeTransactionOnBottomSheetClosed(): TokenDetailsState { - val state = stateProvider() - val bottomSheetConfig = state.bottomSheetConfig?.content as? OnrampStatusBottomSheetConfig ?: return state - val selectedTx = bottomSheetConfig.value - - return if (selectedTx.activeStatus.isTerminal()) { + if (selectedTx.activeStatus.isTerminal()) { onrampRemoveTransactionUseCase(txId = selectedTx.info.txId) - val filteredTxs = state.onrampTxs - .filterNot { it.info.txId == selectedTx.info.txId } - .toPersistentList() - state.copy(onrampTxs = filteredTxs) - } else { - state } } - suspend fun updateOnrmapTxStatuses(onrampTxList: List) = withContext(dispatchers.io) { - onrampTxList.map { tx -> - async { - getOnrampStatusUseCase(tx.txId).fold( - ifLeft = { null }, - ifRight = { statusModel -> - val updatedStatus = tx.copy(status = statusModel.status) - onrampSaveTransactionUseCase(updatedStatus) - updatedStatus - }, - ) - } + suspend fun updateOnrmapTxStatus(onrampTx: ExpressTransactionStateUM.OnrampUM): ExpressTransactionStateUM.OnrampUM { + return if (onrampTx.activeStatus.isTerminal()) { + onrampTx + } else { + getOnrampStatusUseCase(onrampTx.info.txId).fold( + ifLeft = { + Timber.e("Couldn't update onramp status. $it") + onrampTx + }, + ifRight = { statusModel -> + onrampTx.copy(activeStatus = statusModel.status) + }, + ) } - .awaitAll() - .filterNotNull() - .toPersistentList() + } + + @AssistedFactory + interface Factory { + fun create( + currentStateProvider: Provider, + cryptoCurrencyStatusProvider: Provider, + appCurrencyProvider: Provider, + clickIntents: TokenDetailsClickIntents, + cryptoCurrency: CryptoCurrency, + userWalletId: UserWalletId, + ): OnrampStatusFactory } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt index 02a7c85aad..a22b97d642 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt @@ -48,12 +48,9 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.T import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsDialogs import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsTopAppBar import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenInfoBlock -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.exchange.ExchangeStatusBottomSheet -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.exchange.ExchangeStatusBottomSheetConfig -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.exchange.swapTransactionsItems -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.onramp.OnrampStatusBottomSheet -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.onramp.OnrampStatusBottomSheetConfig -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.onramp.onrampTransactionsItems +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheet +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheetConfig +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.expressTransactionsItems import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking.TokenStakingBlock import com.tangem.features.markets.token.block.TokenMarketBlockComponent @@ -180,13 +177,8 @@ internal fun TokenDetailsScreen(state: TokenDetailsState, tokenMarketBlockCompon ) } - swapTransactionsItems( - swapTxs = state.swapTxs, - modifier = itemModifier, - ) - - onrampTransactionsItems( - onrampTxs = state.onrampTxs, + expressTransactionsItems( + expressTxs = state.expressTxs, modifier = itemModifier, ) @@ -214,11 +206,8 @@ internal fun TokenDetailsScreen(state: TokenDetailsState, tokenMarketBlockCompon is ChooseAddressBottomSheetConfig -> { ChooseAddressBottomSheet(config = config) } - is ExchangeStatusBottomSheetConfig -> { - ExchangeStatusBottomSheet(config = config) - } - is OnrampStatusBottomSheetConfig -> { - OnrampStatusBottomSheet(config = config) + is ExpressStatusBottomSheetConfig -> { + ExpressStatusBottomSheet(config = config) } } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheet.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheet.kt new file mode 100644 index 0000000000..c37f0c020a --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheet.kt @@ -0,0 +1,27 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express + +import androidx.compose.runtime.Composable +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.exchange.ExchangeStatusBottomSheetContent +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.onramp.OnrampStatusBottomSheetContent + +internal data class ExpressStatusBottomSheetConfig( + val value: ExpressTransactionStateUM, +) : TangemBottomSheetConfigContent + +@Composable +internal fun ExpressStatusBottomSheet(config: TangemBottomSheetConfig) { + TangemBottomSheet( + config = config, + containerColor = TangemTheme.colors.background.tertiary, + ) { content: ExpressStatusBottomSheetConfig -> + when (val state = content.value) { + is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(state) + is ExpressTransactionStateUM.ExchangeUM -> ExchangeStatusBottomSheetContent(state) + } + } +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusItems.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusItems.kt similarity index 53% rename from features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusItems.kt rename to features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusItems.kt index 0a8a4b0b90..ffd74a9e24 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusItems.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusItems.kt @@ -1,45 +1,42 @@ -package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.onramp +package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.res.TangemTheme -import com.tangem.domain.onramp.model.OnrampStatus +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateIconUM import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusItem import com.tangem.features.tokendetails.impl.R import kotlinx.collections.immutable.PersistentList -internal fun LazyListScope.onrampTransactionsItems( - onrampTxs: PersistentList, +internal fun LazyListScope.expressTransactionsItems( + expressTxs: PersistentList, modifier: Modifier = Modifier, ) { items( - count = onrampTxs.size, - key = { onrampTxs[it].info.txId }, - contentType = { onrampTxs[it]::class.java }, + count = expressTxs.size, + key = { expressTxs[it].info.txId }, + contentType = { expressTxs[it]::class.java }, ) { - val item = onrampTxs[it] - val itemInfo = item.info - - val (iconRes, tint) = when (item.activeStatus) { - OnrampStatus.Status.Verifying -> R.drawable.ic_alert_triangle_20 to TangemTheme.colors.icon.attention - OnrampStatus.Status.Failed -> { + val itemInfo = expressTxs[it].info + val (iconRes, tint) = when (itemInfo.iconState) { + ExpressTransactionStateIconUM.Warning -> { + R.drawable.ic_alert_triangle_20 to TangemTheme.colors.icon.attention + } + ExpressTransactionStateIconUM.Error -> { R.drawable.ic_alert_circle_24 to TangemTheme.colors.icon.warning } - else -> null to null + ExpressTransactionStateIconUM.None -> null to null } ExpressStatusItem( - title = resourceReference(id = R.string.express_status_buying, wrappedList(item.cryptoCurrencyName)), + title = itemInfo.title, fromTokenIconState = itemInfo.fromCurrencyIcon, toTokenIconState = itemInfo.toCurrencyIcon, fromAmount = itemInfo.fromAmount, fromSymbol = itemInfo.fromAmountSymbol, toAmount = itemInfo.toAmount, toSymbol = itemInfo.toAmountSymbol, - onClick = item.info.onClick, + onClick = itemInfo.onClick, infoIconRes = iconRes, infoIconTint = tint, modifier = modifier.animateItem(), diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBlock.kt index 6e8205895c..7b5cd01dd1 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBlock.kt @@ -25,7 +25,7 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.feature.swap.domain.models.domain.ExchangeStatus -import com.tangem.feature.tokendetails.presentation.tokendetails.state.ExchangeStatusState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeStatusState import com.tangem.features.tokendetails.impl.R import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheet.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheetContent.kt similarity index 65% rename from features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheet.kt rename to features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheetContent.kt index feeb5ca411..305ac6bb39 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheet.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheetContent.kt @@ -14,30 +14,17 @@ import com.tangem.core.ui.components.SpacerH10 import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.components.SpacerH16 import com.tangem.core.ui.components.SpacerH24 -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.notifications.CurrencyNotification import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.swap.domain.models.domain.ExchangeStatus -import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressEstimate import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressProvider @Composable -internal fun ExchangeStatusBottomSheet(config: TangemBottomSheetConfig) { - TangemBottomSheet( - config = config, - containerColor = TangemTheme.colors.background.tertiary, - ) { content: ExchangeStatusBottomSheetConfig -> - ExchangeStatusBottomSheetContent(config = content.value) - } -} - -@Composable -private fun ExchangeStatusBottomSheetContent(config: SwapTransactionsState) { +internal fun ExchangeStatusBottomSheetContent(state: ExpressTransactionStateUM.ExchangeUM) { Column(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16)) { SpacerH10() Text( @@ -57,31 +44,31 @@ private fun ExchangeStatusBottomSheetContent(config: SwapTransactionsState) { ) SpacerH16() ExpressEstimate( - timestamp = config.timestamp, - fromTokenIconState = config.fromCurrencyIcon, - toTokenIconState = config.toCurrencyIcon, - fromCryptoAmount = config.fromCryptoAmount, - fromCryptoSymbol = config.fromCryptoCurrency.symbol, - toCryptoAmount = TextReference.Str(config.toCryptoAmount), - toCryptoSymbol = config.toCryptoCurrency.symbol, - fromFiatAmount = TextReference.Str(config.fromFiatAmount), - toFiatAmount = TextReference.Str(config.toFiatAmount), + timestamp = state.info.timestampFormatted, + fromTokenIconState = state.info.fromCurrencyIcon, + toTokenIconState = state.info.toCurrencyIcon, + fromCryptoAmount = state.info.fromAmount, + fromCryptoSymbol = state.info.fromAmountSymbol, + toCryptoAmount = state.info.toAmount, + toCryptoSymbol = state.info.toAmountSymbol, + fromFiatAmount = state.info.fromFiatAmount, + toFiatAmount = state.info.toFiatAmount, ) SpacerH12() ExpressProvider( - providerName = TextReference.Str(config.provider.name), - providerType = TextReference.Str(config.provider.type.providerName), - providerTxId = config.txExternalId, - imageUrl = config.provider.imageLarge, + providerName = TextReference.Str(state.provider.name), + providerType = TextReference.Str(state.provider.type.providerName), + providerTxId = state.info.txExternalId, + imageUrl = state.provider.imageLarge, ) SpacerH12() ExchangeStatusBlock( - statuses = config.statuses, - showLink = config.showProviderLink, - onClick = { config.onGoToProviderClick(config.txUrl.orEmpty()) }, + statuses = state.statuses, + showLink = state.showProviderLink, + onClick = { state.info.onGoToProviderClick(state.info.txExternalUrl.orEmpty()) }, ) - if (config.notification != null) { - Notification(state = config.notification, activeStatus = config.activeStatus) + if (state.notification != null) { + Notification(state = state.notification, activeStatus = state.activeStatus) } SpacerH24() } @@ -114,8 +101,4 @@ private fun Notification(state: ExchangeStatusNotifications, activeStatus: Excha } } } -} - -internal data class ExchangeStatusBottomSheetConfig( - val value: SwapTransactionsState, -) : TangemBottomSheetConfigContent \ No newline at end of file +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusItems.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusItems.kt deleted file mode 100644 index c18925e716..0000000000 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusItems.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.exchange - -import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.ui.Modifier -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.wrappedList -import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.swap.domain.models.domain.ExchangeStatus -import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusItem -import com.tangem.features.tokendetails.impl.R -import kotlinx.collections.immutable.PersistentList - -internal fun LazyListScope.swapTransactionsItems( - swapTxs: PersistentList, - modifier: Modifier = Modifier, -) { - if (swapTxs.isNotEmpty()) { - items( - count = swapTxs.size, - key = { swapTxs[it].txId }, - contentType = { swapTxs[it]::class.java }, - ) { - val item = swapTxs[it] - val (iconRes, tint) = when (item.activeStatus) { - ExchangeStatus.Verifying -> R.drawable.ic_alert_triangle_20 to TangemTheme.colors.icon.attention - ExchangeStatus.Failed, ExchangeStatus.Cancelled -> { - R.drawable.ic_alert_circle_24 to TangemTheme.colors.icon.warning - } - else -> null to null - } - - ExpressStatusItem( - title = resourceReference(id = R.string.express_exchange_by, wrappedList(item.provider.name)), - fromTokenIconState = item.fromCurrencyIcon, - toTokenIconState = item.toCurrencyIcon, - fromAmount = item.fromCryptoAmount, - fromSymbol = item.fromCryptoCurrency.symbol, - toSymbol = item.toCryptoCurrency.symbol, - onClick = item.onClick, - infoIconRes = iconRes, - infoIconTint = tint, - modifier = modifier.animateItem(), - ) - } - } -} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusBottomSheet.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusBottomSheetContent.kt similarity index 56% rename from features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusBottomSheet.kt rename to features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusBottomSheetContent.kt index 13cfa71096..bf278974e7 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusBottomSheet.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusBottomSheetContent.kt @@ -15,9 +15,6 @@ import com.tangem.core.ui.components.SpacerH10 import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.components.SpacerH16 import com.tangem.core.ui.components.SpacerH24 -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM @@ -25,17 +22,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.e import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressProvider @Composable -internal fun OnrampStatusBottomSheet(config: TangemBottomSheetConfig) { - TangemBottomSheet( - config = config, - containerColor = TangemTheme.colors.background.tertiary, - ) { content: OnrampStatusBottomSheetConfig -> - OnrampStatusBottomSheetContent(config = content.value) - } -} - -@Composable -private fun OnrampStatusBottomSheetContent(config: ExpressTransactionStateUM.OnrampUM) { +internal fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM) { Column(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16)) { SpacerH10() Text( @@ -55,33 +42,27 @@ private fun OnrampStatusBottomSheetContent(config: ExpressTransactionStateUM.Onr ) SpacerH16() ExpressEstimate( - timestamp = config.info.timestamp, - fromTokenIconState = config.info.fromCurrencyIcon, - toTokenIconState = config.info.toCurrencyIcon, - fromCryptoAmount = config.info.fromAmount, - fromCryptoSymbol = config.info.fromAmountSymbol, - toCryptoAmount = config.info.toAmount, - toCryptoSymbol = config.info.toAmountSymbol, - fromFiatAmount = config.info.fromFiatAmount, - toFiatAmount = config.info.toFiatAmount, + timestamp = state.info.timestampFormatted, + fromTokenIconState = state.info.fromCurrencyIcon, + toTokenIconState = state.info.toCurrencyIcon, + fromCryptoAmount = state.info.fromAmount, + fromCryptoSymbol = state.info.fromAmountSymbol, + toCryptoAmount = state.info.toAmount, + toCryptoSymbol = state.info.toAmountSymbol, + fromFiatAmount = state.info.fromFiatAmount, + toFiatAmount = state.info.toFiatAmount, ) SpacerH12() ExpressProvider( - providerName = stringReference(config.providerName), - providerType = stringReference(config.providerType), - providerTxId = config.info.txExternalId, - imageUrl = config.providerImageUrl, + providerName = stringReference(state.providerName), + providerType = stringReference(state.providerType), + providerTxId = state.info.txExternalId, + imageUrl = state.providerImageUrl, ) SpacerH12() - ExpressStatusBlock(state = config.info.status) - if (config.info.notification != null) { - ExpressStatusNotificationBlock(state = config.info.notification) - } + ExpressStatusBlock(state = state.info.status) + ExpressStatusNotificationBlock(state = state.info.notification) SpacerH24() } -} - -internal data class OnrampStatusBottomSheetConfig( - val value: ExpressTransactionStateUM.OnrampUM, -) : TangemBottomSheetConfigContent \ No newline at end of file +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt index 9058c501e5..9f150beb95 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt @@ -45,9 +45,7 @@ interface TokenDetailsClickIntents { fun onCloseRentInfoNotification() - fun onSwapTransactionClick(txId: String) - - fun onOnrampTransactionClick(txId: String) + fun onExpressTransactionClick(txId: String) fun onGoToProviderClick(url: String) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 63f8ad3395..782f95ba25 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -22,7 +22,6 @@ import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.haptic.TangemHapticEffect import com.tangem.core.ui.haptic.VibratorHapticManager -import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase @@ -30,11 +29,6 @@ import com.tangem.domain.card.GetExtendedPublicKeyForCurrencyUseCase import com.tangem.domain.card.NetworkHasDerivationUseCase import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase -import com.tangem.domain.onramp.GetOnrampStatusUseCase -import com.tangem.domain.onramp.GetOnrampTransactionsUseCase -import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase -import com.tangem.domain.onramp.OnrampSaveTransactionUseCase -import com.tangem.domain.onramp.model.cache.OnrampTransaction import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase import com.tangem.domain.staking.GetStakingAvailabilityUseCase @@ -50,12 +44,10 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkAddress import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason -import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent -import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.transaction.error.AssociateAssetError import com.tangem.domain.transaction.usecase.AssociateAssetUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase @@ -64,22 +56,16 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase -import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase -import com.tangem.feature.swap.domain.SwapTransactionRepository -import com.tangem.feature.swap.domain.api.SwapRepository -import com.tangem.feature.swap.domain.models.domain.ExchangeStatus import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsCurrencyStatusAnalyticsSender import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsNotificationsAnalyticsSender -import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory -import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express.ExchangeStatusFactory -import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express.OnrampStatusFactory -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.exchange.ExchangeStatusBottomSheetConfig -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.onramp.OnrampStatusBottomSheetConfig +import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express.ExpressStatusFactory +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheetConfig import com.tangem.features.tokendetails.impl.R import com.tangem.utils.Provider import com.tangem.utils.coroutines.* @@ -111,8 +97,6 @@ internal class TokenDetailsViewModel @Inject constructor( private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val getCurrencyWarningsUseCase: GetCurrencyWarningsUseCase, private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, - private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, - private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, private val shouldShowSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase, private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase, private val getExtendedPublicKeyForCurrencyUseCase: GetExtendedPublicKeyForCurrencyUseCase, @@ -120,20 +104,13 @@ internal class TokenDetailsViewModel @Inject constructor( private val getStakingAvailabilityUseCase: GetStakingAvailabilityUseCase, private val getYieldUseCase: GetYieldUseCase, private val networkHasDerivationUseCase: NetworkHasDerivationUseCase, - private val swapRepository: SwapRepository, - private val swapTransactionRepository: SwapTransactionRepository, - private val quotesRepository: QuotesRepository, - private val swapTransactionStatusStore: SwapTransactionStatusStore, private val isDemoCardUseCase: IsDemoCardUseCase, private val associateAssetUseCase: AssociateAssetUseCase, private val reduxStateHolder: ReduxStateHolder, private val analyticsEventsHandler: AnalyticsEventHandler, private val vibratorHapticManager: VibratorHapticManager, private val clipboardManager: ClipboardManager, - private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase, - private val onrampSaveTransactionUseCase: OnrampSaveTransactionUseCase, - private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase, - private val getOnrampStatusUseCase: GetOnrampStatusUseCase, + expressStatusFactory: ExpressStatusFactory.Factory, getUserWalletUseCase: GetUserWalletUseCase, getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase, deepLinksRegistry: DeepLinksRegistry, @@ -157,15 +134,13 @@ internal class TokenDetailsViewModel @Inject constructor( private val marketPriceJobHolder = JobHolder() private val refreshStateJobHolder = JobHolder() private val warningsJobHolder = JobHolder() - private val swapTxJobHolder = JobHolder() - private val onrampTxJobHolder = JobHolder() + private val expressTxJobHolder = JobHolder() private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null private var stakingEntryInfo: StakingEntryInfo? = null private var stakingAvailability: StakingAvailability = StakingAvailability.Unavailable - private var swapTxStatusTaskScheduler = SingleTaskScheduler>() - private var onrampTxStatusTaskScheduler = SingleTaskScheduler>() + private var expressTxStatusTaskScheduler = SingleTaskScheduler>() private val stateFactory = TokenDetailsStateFactory( currentStateProvider = Provider { uiState.value }, @@ -182,37 +157,15 @@ internal class TokenDetailsViewModel @Inject constructor( decimals = cryptoCurrency.decimals, ) - private val exchangeStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) { - ExchangeStatusFactory( - swapTransactionRepository = swapTransactionRepository, - swapRepository = swapRepository, - quotesRepository = quotesRepository, - getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, - addCryptoCurrenciesUseCase = addCryptoCurrenciesUseCase, - swapTransactionStatusStore = swapTransactionStatusStore, - dispatchers = dispatchers, + private val expressStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) { + expressStatusFactory.create( clickIntents = this, appCurrencyProvider = Provider { selectedAppCurrencyFlow.value }, analyticsEventsHandlerProvider = Provider { analyticsEventsHandler }, currentStateProvider = Provider { uiState.value }, - userWalletId = userWalletId, - cryptoCurrency = cryptoCurrency, - ) - } - - private val onrampStatusFactory by lazy(LazyThreadSafetyMode.NONE) { - OnrampStatusFactory( - stateProvider = Provider { uiState.value }, - appCurrencyProvider = Provider { selectedAppCurrencyFlow.value }, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, userWalletId = userWalletId, cryptoCurrency = cryptoCurrency, - clickIntents = this, - getOnrampTransactionsUseCase = getOnrampTransactionsUseCase, - onrampSaveTransactionUseCase = onrampSaveTransactionUseCase, - onrampRemoveTransactionUseCase = onrampRemoveTransactionUseCase, - getOnrampStatusUseCase = getOnrampStatusUseCase, - dispatchers = dispatchers, ) } @@ -255,16 +208,14 @@ internal class TokenDetailsViewModel @Inject constructor( } override fun onCleared() { - swapTxStatusTaskScheduler.cancelTask() - onrampTxStatusTaskScheduler.cancelTask() - onrampTxJobHolder.cancel() + expressTxStatusTaskScheduler.cancelTask() + expressTxJobHolder.cancel() super.onCleared() } private fun updateContent() { subscribeOnCurrencyStatusUpdates() - subscribeOnExchangeTransactionsUpdates() - subscribeOnOnrampTransactionsUpdates() + subscribeOnExpressTransactionsUpdates() updateTxHistory(refresh = false, showItemsLoading = true) updateStakingInfo() @@ -337,55 +288,31 @@ internal class TokenDetailsViewModel @Inject constructor( } } - private fun subscribeOnExchangeTransactionsUpdates() { + private fun subscribeOnExpressTransactionsUpdates() { viewModelScope.launch(dispatchers.main) { - swapTxStatusTaskScheduler.cancelTask() - exchangeStatusFactory.invoke() + expressTxStatusTaskScheduler.cancelTask() + expressStatusFactory + .getExpressStatuses() .distinctUntilChanged() - .filterNot { it.isEmpty() } - .onEach { swapTxs -> - updateSwapTx(swapTxs) - swapTxStatusTaskScheduler.scheduleTask( - viewModelScope, - PeriodicTask( - delay = EXCHANGE_STATUS_UPDATE_DELAY, - task = { - runCatching { - exchangeStatusFactory.updateSwapTxStatuses(internalUiState.value.swapTxs) - } - }, - onSuccess = ::updateSwapTx, - onError = { /* no-op */ }, - ), + .onEach { expressTxs -> + internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs( + expressTxs, + ::updateNetworkToSwapBalance, ) - } - .flowOn(dispatchers.main) - .launchIn(viewModelScope) - .saveIn(swapTxJobHolder) - } - } - - private fun subscribeOnOnrampTransactionsUpdates() { - viewModelScope.launch(dispatchers.main) { - onrampTxStatusTaskScheduler.cancelTask() - onrampStatusFactory.invoke().distinctUntilChanged() - .filterNot { it.isEmpty() } - .onEach { onrampTxs -> - internalUiState.value = onrampStatusFactory.updateOnrampStatusBottomSheet(onrampTxs) - onrampTxStatusTaskScheduler.scheduleTask( + expressTxStatusTaskScheduler.scheduleTask( viewModelScope, PeriodicTask( - delay = EXCHANGE_STATUS_UPDATE_DELAY, + delay = EXPRESS_STATUS_UPDATE_DELAY, task = { runCatching { - val onrampTxsToUpdate = onrampTxs.filter { onrampTx -> - internalUiState.value.onrampTxs.any { it.info.txId == onrampTx.txId } - } - onrampStatusFactory.updateOnrmapTxStatuses(onrampTxsToUpdate) + expressStatusFactory.getUpdatedExpressStatuses(internalUiState.value.expressTxs) } }, - onSuccess = { - internalUiState.value = onrampStatusFactory.updateOnrampStatusBottomSheet(onrampTxs) + onSuccess = { updatedTxs -> + internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs( + updatedTxs, + ::updateNetworkToSwapBalance, + ) }, onError = { /* no-op */ }, ), @@ -393,25 +320,10 @@ internal class TokenDetailsViewModel @Inject constructor( } .flowOn(dispatchers.main) .launchIn(viewModelScope) - .saveIn(onrampTxJobHolder) + .saveIn(expressTxJobHolder) } } - private fun updateSwapTx(swapTxs: PersistentList) { - val config = internalUiState.value.bottomSheetConfig - val exchangeBottomSheet = config?.content as? ExchangeStatusBottomSheetConfig - val currentTx = swapTxs.firstOrNull { it.txId == exchangeBottomSheet?.value?.txId } - if (currentTx?.activeStatus == ExchangeStatus.Finished) { - updateNetworkToSwapBalance(currentTx.toCryptoCurrency) - } - internalUiState.value = internalUiState.value.copy( - swapTxs = swapTxs, - bottomSheetConfig = currentTx?.let( - stateFactory::updateStateWithExchangeStatusBottomSheet, - ) ?: config, - ) - } - private fun updateNetworkToSwapBalance(toCryptoCurrency: CryptoCurrency) { viewModelScope.launch { updateDelayedCurrencyStatusUseCase( @@ -835,8 +747,7 @@ internal class TokenDetailsViewModel @Inject constructor( refresh = true, showItemsLoading = internalUiState.value.txHistoryState !is TxHistoryState.Content, ) - subscribeOnExchangeTransactionsUpdates() - subscribeOnOnrampTransactionsUpdates() + subscribeOnExpressTransactionsUpdates() }, ).awaitAll() internalUiState.value = stateFactory.getRefreshedState() @@ -844,16 +755,10 @@ internal class TokenDetailsViewModel @Inject constructor( } override fun onDismissBottomSheet() { - val bsContent = internalUiState.value.bottomSheetConfig?.content - when (bsContent) { - is ExchangeStatusBottomSheetConfig -> { + when (val bsContent = internalUiState.value.bottomSheetConfig?.content) { + is ExpressStatusBottomSheetConfig -> { viewModelScope.launch(dispatchers.main) { - internalUiState.value = exchangeStatusFactory.removeTransactionOnBottomSheetClosed() - } - } - is OnrampStatusBottomSheetConfig -> { - viewModelScope.launch(dispatchers.main) { - internalUiState.value = onrampStatusFactory.removeTransactionOnBottomSheetClosed() + expressStatusFactory.removeTransactionOnBottomSheetClosed(bsContent.value) } } } @@ -864,15 +769,9 @@ internal class TokenDetailsViewModel @Inject constructor( internalUiState.value = stateFactory.getStateWithRemovedRentNotification() } - override fun onSwapTransactionClick(txId: String) { - val swapTxState = internalUiState.value.swapTxs.first { it.txId == txId } - analyticsEventsHandler.send(TokenExchangeAnalyticsEvent.CexTxStatusOpened(cryptoCurrency.symbol)) - internalUiState.value = stateFactory.getStateWithExchangeStatusBottomSheet(swapTxState) - } - - override fun onOnrampTransactionClick(txId: String) { - val onrampTxState = internalUiState.value.onrampTxs.first { it.info.txId == txId } - internalUiState.value = onrampStatusFactory.getStateWithOnrampStatusBottomSheet(onrampTxState) + override fun onExpressTransactionClick(txId: String) { + val expressTxState = internalUiState.value.expressTxs.first { it.info.txId == txId } + internalUiState.value = expressStatusFactory.getStateWithExpressStatusBottomSheet(expressTxState) } override fun onGoToProviderClick(url: String) { @@ -880,9 +779,13 @@ internal class TokenDetailsViewModel @Inject constructor( } override fun onGoToRefundedTokenClick(cryptoCurrency: CryptoCurrency) { - if (internalUiState.value.bottomSheetConfig?.content is ExchangeStatusBottomSheetConfig) { + val bottomSheetState = internalUiState.value.bottomSheetConfig?.content + if (bottomSheetState is ExpressStatusBottomSheetConfig) { viewModelScope.launch { - internalUiState.value = exchangeStatusFactory.removeTransactionOnBottomSheetClosed(true) + expressStatusFactory.removeTransactionOnBottomSheetClosed( + expressState = bottomSheetState.value, + isForceTerminal = true, + ) } } internalUiState.value = stateFactory.getStateWithClosedBottomSheet() @@ -1005,6 +908,6 @@ internal class TokenDetailsViewModel @Inject constructor( } private companion object { - const val EXCHANGE_STATUS_UPDATE_DELAY = 10_000L + const val EXPRESS_STATUS_UPDATE_DELAY = 10_000L } } \ No newline at end of file