From 5891112ab16af5d1ab63e2129a7b067f59aace44 Mon Sep 17 00:00:00 2001 From: iMaks99 <45900939+iMaks99@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:01:05 +0500 Subject: [PATCH 1/2] AND-9530 Fixed Onramp: no status on single-currency cards. Preparing components --- common/ui/build.gradle.kts | 2 ++ .../ui/expressStatus}/ExpressEstimate.kt | 6 ++-- .../ui/expressStatus}/ExpressProvider.kt | 4 +-- .../expressStatus/ExpressStatusBottomSheet.kt | 24 ++++++++++++++ .../ui/expressStatus}/ExpressStatusItem.kt | 4 +-- .../ui/expressStatus}/ExpressStatusItems.kt | 10 +++--- .../OnrampStatusBottomSheetContent.kt | 10 ++---- .../state}/ExpressTransactionStateUM.kt | 31 ++++--------------- .../tokendetails/state/TokenDetailsState.kt | 2 +- .../tokendetails/state/express/ExchangeUM.kt | 21 +++++++++++++ ...nDetailsOnrampTransactionStateConverter.kt | 6 ++-- ...enDetailsSwapTransactionsStateConverter.kt | 20 ++++++------ .../factory/express/ExchangeStatusFactory.kt | 12 +++---- .../factory/express/ExpressStatusFactory.kt | 22 +++++++------ .../factory/express/OnrampStatusFactory.kt | 4 +-- .../tokendetails/ui/TokenDetailsScreen.kt | 4 +-- .../express/ExpressStatusBottomSheet.kt | 13 +++----- .../ExchangeStatusBottomSheetContent.kt | 8 ++--- .../viewmodels/TokenDetailsViewModel.kt | 5 ++- 19 files changed, 115 insertions(+), 93 deletions(-) rename {features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express => common/ui/src/main/java/com/tangem/common/ui/expressStatus}/ExpressEstimate.kt (93%) rename {features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express => common/ui/src/main/java/com/tangem/common/ui/expressStatus}/ExpressProvider.kt (97%) create mode 100644 common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusBottomSheet.kt rename {features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express => common/ui/src/main/java/com/tangem/common/ui/expressStatus}/ExpressStatusItem.kt (98%) rename {features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express => common/ui/src/main/java/com/tangem/common/ui/expressStatus}/ExpressStatusItems.kt (78%) rename {features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp => common/ui/src/main/java/com/tangem/common/ui/expressStatus}/OnrampStatusBottomSheetContent.kt (79%) rename {features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express => common/ui/src/main/java/com/tangem/common/ui/expressStatus/state}/ExpressTransactionStateUM.kt (53%) create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExchangeUM.kt diff --git a/common/ui/build.gradle.kts b/common/ui/build.gradle.kts index 315574334e..28e0387959 100644 --- a/common/ui/build.gradle.kts +++ b/common/ui/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { implementation(deps.compose.navigation) implementation(deps.compose.navigation.hilt) implementation(deps.compose.coil) + implementation(deps.compose.constraintLayout) /** Deps */ implementation(deps.kotlin.immutable.collections) @@ -34,6 +35,7 @@ dependencies { implementation(projects.domain.tokens.models) implementation(projects.domain.transaction.models) implementation(projects.domain.wallets.models) + implementation(projects.domain.onramp.models) implementation(deps.tangem.card.core) implementation(deps.tangem.blockchain) { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressEstimate.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressEstimate.kt similarity index 93% rename from features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressEstimate.kt rename to common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressEstimate.kt index 6f913e75fb..81164716b5 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressEstimate.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressEstimate.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express +package com.tangem.common.ui.expressStatus import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -8,16 +8,16 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource +import com.tangem.common.ui.R import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.inputrow.InputRowApprox import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme -import com.tangem.features.tokendetails.impl.R @Suppress("LongParameterList") @Composable -internal fun ExpressEstimate( +fun ExpressEstimate( timestamp: TextReference, fromTokenIconState: CurrencyIconState, toTokenIconState: CurrencyIconState, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressProvider.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressProvider.kt similarity index 97% rename from features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressProvider.kt rename to common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressProvider.kt index 9112d98458..1b82c1a7e7 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressProvider.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressProvider.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express +package com.tangem.common.ui.expressStatus import android.content.res.Configuration import android.widget.Toast @@ -29,7 +29,7 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview @Composable -internal fun ExpressProvider( +fun ExpressProvider( providerName: TextReference, providerType: TextReference, providerTxId: String?, diff --git a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusBottomSheet.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusBottomSheet.kt new file mode 100644 index 0000000000..92bd1822aa --- /dev/null +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusBottomSheet.kt @@ -0,0 +1,24 @@ +package com.tangem.common.ui.expressStatus + +import androidx.compose.runtime.Composable +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM +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 + +data class ExpressStatusBottomSheetConfig( + val value: ExpressTransactionStateUM, +) : TangemBottomSheetConfigContent + +@Composable +fun ExpressStatusBottomSheet(config: TangemBottomSheetConfig) { + TangemBottomSheet( + config = config, + containerColor = TangemTheme.colors.background.tertiary, + ) { content: ExpressStatusBottomSheetConfig -> + when (val state = content.value) { + is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(state) + } + } +} diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusItem.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItem.kt similarity index 98% rename from features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusItem.kt rename to common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItem.kt index 91cf9ed9be..050f76cd26 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusItem.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItem.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express +package com.tangem.common.ui.expressStatus import android.content.res.Configuration import androidx.annotation.DrawableRes @@ -18,6 +18,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.constraintlayout.compose.* +import com.tangem.common.ui.R import com.tangem.core.ui.components.atoms.text.EllipsisText import com.tangem.core.ui.components.atoms.text.TextEllipsis import com.tangem.core.ui.components.currency.icon.CurrencyIcon @@ -27,7 +28,6 @@ import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview -import com.tangem.features.tokendetails.impl.R @Suppress("DestructuringDeclarationWithTooManyEntries", "LongMethod", "LongParameterList") @Composable diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusItems.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItems.kt similarity index 78% rename from features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusItems.kt rename to common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItems.kt index a0bf44cbc2..6caa46c290 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusItems.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItems.kt @@ -1,14 +1,14 @@ -package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express +package com.tangem.common.ui.expressStatus import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier +import com.tangem.common.ui.R +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateIconUM -import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM -import com.tangem.features.tokendetails.impl.R import kotlinx.collections.immutable.PersistentList -internal fun LazyListScope.expressTransactionsItems( +fun LazyListScope.expressTransactionsItems( expressTxs: PersistentList, modifier: Modifier = Modifier, ) { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusBottomSheetContent.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/OnrampStatusBottomSheetContent.kt similarity index 79% rename from features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusBottomSheetContent.kt rename to common/ui/src/main/java/com/tangem/common/ui/expressStatus/OnrampStatusBottomSheetContent.kt index 9b528ddd06..0022d1a262 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/onramp/OnrampStatusBottomSheetContent.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/OnrampStatusBottomSheetContent.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.onramp +package com.tangem.common.ui.expressStatus import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -8,8 +8,7 @@ import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign -import com.tangem.common.ui.expressStatus.ExpressStatusBlock -import com.tangem.common.ui.expressStatus.ExpressStatusNotificationBlock +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.core.ui.R import com.tangem.core.ui.components.SpacerH10 import com.tangem.core.ui.components.SpacerH12 @@ -17,12 +16,9 @@ import com.tangem.core.ui.components.SpacerH16 import com.tangem.core.ui.components.SpacerH24 import com.tangem.core.ui.extensions.stringReference 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.ExpressEstimate -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressProvider @Composable -internal fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM) { +fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM) { Column(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16)) { SpacerH10() Text( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExpressTransactionStateUM.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/state/ExpressTransactionStateUM.kt similarity index 53% rename from features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExpressTransactionStateUM.kt rename to common/ui/src/main/java/com/tangem/common/ui/expressStatus/state/ExpressTransactionStateUM.kt index 928047ea55..7061fe2b27 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExpressTransactionStateUM.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/state/ExpressTransactionStateUM.kt @@ -1,31 +1,13 @@ -package com.tangem.feature.tokendetails.presentation.tokendetails.state.express +package com.tangem.common.ui.expressStatus.state -import com.tangem.common.ui.expressStatus.state.ExpressStatusUM 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.components.ExchangeStatusNotifications -import kotlinx.collections.immutable.ImmutableList -internal sealed class ExpressTransactionStateUM { +interface ExpressTransactionStateUM { - abstract val info: ExpressTransactionStateInfoUM - - data class ExchangeUM( - override val info: ExpressTransactionStateInfoUM, - val provider: SwapProvider, - val activeStatus: ExchangeStatus?, - val statuses: ImmutableList, - val notification: ExchangeStatusNotifications? = null, - val showProviderLink: Boolean, - val isRefundTerminalStatus: Boolean, - val fromCryptoCurrency: CryptoCurrency, - val toCryptoCurrency: CryptoCurrency, - ) : ExpressTransactionStateUM() + val info: ExpressTransactionStateInfoUM data class OnrampUM( override val info: ExpressTransactionStateInfoUM, @@ -34,10 +16,10 @@ internal sealed class ExpressTransactionStateUM { val providerType: String, // todo onramp fix after SwapProvider moved to own module val activeStatus: OnrampStatus.Status, val fromCurrencyCode: String, - ) : ExpressTransactionStateUM() + ) : ExpressTransactionStateUM } -internal data class ExpressTransactionStateInfoUM( +data class ExpressTransactionStateInfoUM( val title: TextReference, val status: ExpressStatusUM, val notification: NotificationUM?, @@ -49,7 +31,6 @@ internal data class ExpressTransactionStateInfoUM( val onGoToProviderClick: (String) -> Unit, val onClick: () -> Unit, val iconState: ExpressTransactionStateIconUM, - val toAmount: TextReference, val toFiatAmount: TextReference?, val toAmountSymbol: String, @@ -61,7 +42,7 @@ internal data class ExpressTransactionStateInfoUM( val fromCurrencyIcon: CurrencyIconState, ) -internal enum class ExpressTransactionStateIconUM { +enum class ExpressTransactionStateIconUM { Warning, Error, None, 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 368b693b50..f9923fedcc 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 @@ -9,7 +9,7 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification -import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExchangeUM.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExchangeUM.kt new file mode 100644 index 0000000000..1829bec66b --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExchangeUM.kt @@ -0,0 +1,21 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.state.express + +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM +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 ExchangeUM( + override val info: ExpressTransactionStateInfoUM, + val provider: SwapProvider, + val activeStatus: ExchangeStatus?, + val statuses: ImmutableList, + val notification: ExchangeStatusNotifications? = null, + val showProviderLink: Boolean, + val isRefundTerminalStatus: Boolean, + val fromCryptoCurrency: CryptoCurrency, + val toCryptoCurrency: CryptoCurrency, +) : ExpressTransactionStateUM 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 8a1677cfd6..c3eb82b653 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 @@ -23,9 +23,9 @@ 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.tokens.model.analytics.TokenOnrampAnalyticsEvent -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.common.ui.expressStatus.state.ExpressTransactionStateIconUM +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.features.tokendetails.impl.R import com.tangem.utils.Provider 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 771b967b95..94035e10e5 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 @@ -23,9 +23,9 @@ import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListMode 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.common.ui.expressStatus.state.ExpressTransactionStateIconUM +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.features.tokendetails.impl.R import com.tangem.utils.Provider @@ -45,20 +45,20 @@ internal class TokenDetailsSwapTransactionsStateConverter( private val cryptoCurrency: CryptoCurrency, private val analyticsEventsHandler: AnalyticsEventHandler, 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 -> @@ -84,7 +84,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( getNotification(transaction.status?.status, transaction.status?.txExternalUrl, null) val showProviderLink = getShowProviderLink(notifications, transaction.status) result.add( - ExpressTransactionStateUM.ExchangeUM( + ExchangeUM( provider = transaction.provider, statuses = getStatuses(transaction.status?.status), notification = notifications, @@ -108,11 +108,11 @@ internal class TokenDetailsSwapTransactionsStateConverter( } fun updateTxStatus( - tx: ExpressTransactionStateUM.ExchangeUM, + tx: ExchangeUM, statusModel: ExchangeStatusModel?, refundToken: CryptoCurrency?, isRefundTerminalStatus: Boolean, - ): ExpressTransactionStateUM.ExchangeUM { + ): ExchangeUM { if (statusModel == null || tx.activeStatus == statusModel.status) { Timber.e("UpdateTxStatus isn't required. Current status isn't changed") return tx 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 534cbc94b4..fd8bfca8da 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 @@ -1,5 +1,6 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express +import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore @@ -15,9 +16,8 @@ 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.TokenDetailsState -import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsSwapTransactionsStateConverter -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 dagger.assisted.Assisted @@ -55,7 +55,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( ) } - suspend operator fun invoke(): Flow> { + suspend operator fun invoke(): Flow> { val selectedWallet = getSelectedWalletSyncUseCase().fold( ifLeft = { return emptyFlow() }, ifRight = { it }, @@ -82,7 +82,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( suspend fun removeTransactionOnBottomSheetClosed(isForceTerminal: Boolean = false) { val state = currentStateProvider() val bottomSheetConfig = state.bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig ?: return - val selectedTx = bottomSheetConfig.value as? ExpressTransactionStateUM.ExchangeUM ?: return + val selectedTx = bottomSheetConfig.value as? ExchangeUM ?: return val shouldTerminate = selectedTx.activeStatus.isTerminal(selectedTx.isRefundTerminalStatus) || isForceTerminal if (shouldTerminate) { @@ -95,7 +95,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( } } - suspend fun updateSwapTxStatus(swapTx: ExpressTransactionStateUM.ExchangeUM): ExpressTransactionStateUM.ExchangeUM { + suspend fun updateSwapTxStatus(swapTx: ExchangeUM): ExchangeUM { return if (swapTx.activeStatus.isTerminal(swapTx.isRefundTerminalStatus)) { swapTx } else { @@ -163,7 +163,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( private fun getExchangeStatusState( savedTransactions: List?, quotes: Set, - ): PersistentList { + ): PersistentList { if (savedTransactions == null) { return persistentListOf() } 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 index 476e3a67e3..eb467e9cf4 100644 --- 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 @@ -1,5 +1,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express +import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.domain.appcurrency.model.AppCurrency @@ -10,8 +12,7 @@ import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent 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.state.express.ExchangeUM import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -79,11 +80,13 @@ internal class ExpressStatusFactory @AssistedInject constructor( expressTxs.map { tx -> async { when (tx) { - is ExpressTransactionStateUM.ExchangeUM -> exchangeStatusFactory.updateSwapTxStatus(tx) + is ExchangeUM -> exchangeStatusFactory.updateSwapTxStatus(tx) is ExpressTransactionStateUM.OnrampUM -> onrampStatusFactory.updateOnrmapTxStatus(tx) + else -> null } } }.awaitAll() + .filterNotNull() .toPersistentList() } @@ -95,13 +98,13 @@ internal class ExpressStatusFactory @AssistedInject constructor( 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) { + if (currentTx is ExchangeUM && currentTx.activeStatus == ExchangeStatus.Finished) { updateBalance(currentTx.toCryptoCurrency) } val expressTxsToDisplay = expressTxs.filterNot { when (it) { - is ExpressTransactionStateUM.ExchangeUM -> false - is ExpressTransactionStateUM.OnrampUM -> it.activeStatus.isHidden + is ExpressTransactionStateUM.OnrampUM -> false // it.activeStatus.isHidden + else -> false } }.toPersistentList() return state.copy( @@ -115,7 +118,7 @@ internal class ExpressStatusFactory @AssistedInject constructor( fun getStateWithExpressStatusBottomSheet(expressState: ExpressTransactionStateUM): TokenDetailsState { val analyticEvent = when (expressState) { - is ExpressTransactionStateUM.ExchangeUM -> TokenExchangeAnalyticsEvent.CexTxStatusOpened( + is ExchangeUM -> TokenExchangeAnalyticsEvent.CexTxStatusOpened( cryptoCurrency.symbol, ) is ExpressTransactionStateUM.OnrampUM -> TokenOnrampAnalyticsEvent.OnrampStatusOpened( @@ -123,6 +126,7 @@ internal class ExpressStatusFactory @AssistedInject constructor( provider = expressState.providerName, fiatCurrency = expressState.fromCurrencyCode, ) + else -> return currentStateProvider() } analyticsEventsHandler.send(analyticEvent) @@ -156,9 +160,7 @@ internal class ExpressStatusFactory @AssistedInject constructor( isForceTerminal: Boolean = false, ) { when (expressState) { - is ExpressTransactionStateUM.ExchangeUM -> exchangeStatusFactory.removeTransactionOnBottomSheetClosed( - isForceTerminal, - ) + is ExchangeUM -> exchangeStatusFactory.removeTransactionOnBottomSheetClosed(isForceTerminal) is ExpressTransactionStateUM.OnrampUM -> onrampStatusFactory.removeTransactionOnBottomSheetClosed() } } 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 15395783f0..ea8a1d95af 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,5 +1,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express +import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus import com.tangem.domain.appcurrency.model.AppCurrency @@ -14,9 +16,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent 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.ExpressStatusBottomSheetConfig import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.utils.Provider import dagger.assisted.Assisted 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 7037d01f45..9854c7c61e 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 @@ -22,6 +22,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.paging.compose.collectAsLazyPagingItems +import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig +import com.tangem.common.ui.expressStatus.expressTransactionsItems import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet @@ -49,8 +51,6 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.T 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.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 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 index c0378ede7c..9fb66bfb57 100644 --- 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 @@ -1,17 +1,14 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express import androidx.compose.runtime.Composable +import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig +import com.tangem.common.ui.expressStatus.OnrampStatusBottomSheetContent +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM 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.state.express.ExchangeUM 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) { @@ -21,7 +18,7 @@ internal fun ExpressStatusBottomSheet(config: TangemBottomSheetConfig) { ) { content: ExpressStatusBottomSheetConfig -> when (val state = content.value) { is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(state) - is ExpressTransactionStateUM.ExchangeUM -> ExchangeStatusBottomSheetContent(state) + is ExchangeUM -> ExchangeStatusBottomSheetContent(state) } } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheetContent.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheetContent.kt index 493c53c1e8..ab6bfb28b6 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheetContent.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheetContent.kt @@ -9,6 +9,8 @@ import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import com.tangem.common.ui.expressStatus.ExpressEstimate +import com.tangem.common.ui.expressStatus.ExpressProvider import com.tangem.core.ui.R import com.tangem.core.ui.components.SpacerH10 import com.tangem.core.ui.components.SpacerH12 @@ -19,12 +21,10 @@ 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.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 +import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM @Composable -internal fun ExchangeStatusBottomSheetContent(state: ExpressTransactionStateUM.ExchangeUM) { +internal fun ExchangeStatusBottomSheetContent(state: ExchangeUM) { Column(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16)) { SpacerH10() Text( 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 f3edf58432..acb4fb3417 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 @@ -5,7 +5,6 @@ import androidx.lifecycle.* import androidx.paging.cachedIn import arrow.core.getOrElse import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.core.TangemSdkError import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.common.routing.bundle.unbundle @@ -68,10 +67,10 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.Token import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsNotificationsAnalyticsSender 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.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory 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.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.features.onramp.OnrampFeatureToggles import com.tangem.features.tokendetails.impl.R import com.tangem.utils.Provider From 1754b930a3f589c7a70c7223705299715c1b6459 Mon Sep 17 00:00:00 2001 From: iMaks99 <45900939+iMaks99@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:04:38 +0500 Subject: [PATCH 2/2] AND-9530 Fixed Onramp: no status on single-currency cards --- .../factory/express/ExpressStatusFactory.kt | 2 +- features/wallet/impl/build.gradle.kts | 7 +- .../wallet/domain/OnrampStatusFactory.kt | 99 +++++++ .../implementors/SingleWalletContentLoader.kt | 14 + .../SingleWalletContentLoaderFactory.kt | 6 + .../wallet/state/model/WalletState.kt | 3 + .../SetExpressStatusesTransformer.kt | 76 +++++ .../SingleWalletOnrampTransactionConverter.kt | 279 ++++++++++++++++++ .../state/utils/WalletLoadingStateFactory.kt | 2 + .../SingleWalletExpressStatusesSubscriber.kt | 92 ++++++ .../presentation/wallet/ui/WalletScreen.kt | 10 + .../wallet/viewmodels/WalletViewModel.kt | 28 +- .../intents/WalletContentClickIntents.kt | 47 +++ 13 files changed, 659 insertions(+), 6 deletions(-) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetExpressStatusesTransformer.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletOnrampTransactionConverter.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletExpressStatusesSubscriber.kt 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 index eb467e9cf4..663e0fd70d 100644 --- 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 @@ -103,7 +103,7 @@ internal class ExpressStatusFactory @AssistedInject constructor( } val expressTxsToDisplay = expressTxs.filterNot { when (it) { - is ExpressTransactionStateUM.OnrampUM -> false // it.activeStatus.isHidden + is ExpressTransactionStateUM.OnrampUM -> it.activeStatus.isHidden else -> false } }.toPersistentList() diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 2048d4f108..a1f44712ae 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -52,18 +52,19 @@ dependencies { implementation(projects.core.utils) implementation(projects.core.analytics) implementation(projects.core.analytics.models) - implementation(projects.common.routing) implementation(projects.core.deepLinks) implementation(projects.core.deepLinks.global) implementation(projects.core.decompose) + implementation(projects.core.datasource) + implementation(projects.libs.crypto) + implementation(projects.libs.blockchainSdk) /** Domain modules */ implementation(projects.domain.card) implementation(projects.domain.demo) implementation(projects.domain.legacy) - implementation(projects.libs.blockchainSdk) implementation(projects.domain.models) implementation(projects.domain.settings) implementation(projects.domain.tokens) @@ -82,6 +83,7 @@ dependencies { implementation(projects.domain.markets.models) implementation(projects.domain.feedback) implementation(projects.domain.onramp.models) + implementation(projects.domain.onramp) //TODO: Create api/impl modules for onboarding https://tangem.atlassian.net/browse/AND-4841 implementation(projects.features.onboarding) @@ -100,6 +102,7 @@ dependencies { /** Common modules */ implementation(projects.common.ui) + implementation(projects.common.routing) /** Test libraries */ implementation(deps.test.junit) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt new file mode 100644 index 0000000000..457d123b1d --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt @@ -0,0 +1,99 @@ +package com.tangem.feature.wallet.presentation.wallet.domain + +import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus +import com.tangem.domain.onramp.GetOnrampStatusUseCase +import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase +import com.tangem.domain.onramp.OnrampUpdateTransactionStatusUseCase +import com.tangem.domain.onramp.model.OnrampStatus +import com.tangem.domain.onramp.model.OnrampStatus.Status.* +import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import dagger.hilt.android.scopes.ViewModelScoped +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.withContext +import timber.log.Timber +import javax.inject.Inject + +@ViewModelScoped +internal class OnrampStatusFactory @Inject constructor( + private val stateHolder: WalletStateController, + private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase, + private val getOnrampStatusUseCase: GetOnrampStatusUseCase, + private val onrampUpdateTransactionStatusUseCase: OnrampUpdateTransactionStatusUseCase, + private val analyticsEventHandler: AnalyticsEventHandler, + private val dispatchers: CoroutineDispatcherProvider, +) { + + suspend fun removeTransactionOnBottomSheetClosed() { + val state = stateHolder.getSelectedWallet() + val bottomSheetConfig = state.bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig ?: return + val selectedTx = bottomSheetConfig.value as? ExpressTransactionStateUM.OnrampUM ?: return + + if (selectedTx.activeStatus.isTerminal) { + onrampRemoveTransactionUseCase(externalTxId = selectedTx.info.txExternalId) + } + } + + suspend fun updateOnrmapTransactionStatuses() = withContext(dispatchers.io) { + val singleWalletState = stateHolder.getSelectedWallet() as? WalletState.SingleCurrency.Content + ?: return@withContext + + singleWalletState.expressTxs.map { tx -> + async { + if (tx is ExpressTransactionStateUM.OnrampUM) { + updateOnrampTxStatus(tx) + } + } + }.awaitAll() + } + + private suspend fun updateOnrampTxStatus(onrampTx: ExpressTransactionStateUM.OnrampUM) { + if (!onrampTx.activeStatus.isTerminal) { + getOnrampStatusUseCase(onrampTx.info.txId).fold( + ifLeft = { + Timber.e("Couldn't update onramp status. $it") + }, + ifRight = { statusModel -> + val externalTxId = statusModel.externalTxId + val status = toAnalyticStatus(statusModel.status) ?: return + + if (statusModel.status != onrampTx.activeStatus) { + analyticsEventHandler.send( + TokenOnrampAnalyticsEvent.OnrampStatusChanged( + tokenSymbol = onrampTx.info.toAmountSymbol, + status = status.name, + provider = onrampTx.providerName, + fiatCurrency = onrampTx.fromCurrencyCode, + ), + ) + onrampUpdateTransactionStatusUseCase(externalTxId = externalTxId, statusModel.status) + } + }, + ) + } + } + + private fun toAnalyticStatus(status: OnrampStatus.Status?): ExpressAnalyticsStatus? { + return when (status) { + Expired, + Paused, + -> ExpressAnalyticsStatus.Cancelled + Created, + WaitingForPayment, + PaymentProcessing, + Paid, + Sending, + -> ExpressAnalyticsStatus.InProgress + Verifying -> ExpressAnalyticsStatus.KYC + Failed -> ExpressAnalyticsStatus.Fail + Finished -> ExpressAnalyticsStatus.Done + null -> null + } + } +} diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoader.kt index 0145f41f99..a81e5b732f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoader.kt @@ -2,6 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.onramp.GetOnrampTransactionsUseCase +import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase @@ -27,6 +29,8 @@ internal class SingleWalletContentLoader( private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase, + private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase, private val analyticsEventHandler: AnalyticsEventHandler, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, ) : WalletContentLoader(id = userWallet.walletId) { @@ -55,6 +59,16 @@ internal class SingleWalletContentLoader( getSingleWalletWarningsFactory = getSingleWalletWarningsFactory, walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, ), + SingleWalletExpressStatusesSubscriber( + userWallet = userWallet, + stateHolder = stateHolder, + clickIntents = clickIntents, + getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase, + analyticsEventHandler = analyticsEventHandler, + getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase, + getOnrampTransactionsUseCase = getOnrampTransactionsUseCase, + onrampRemoveTransactionUseCase = onrampRemoveTransactionUseCase, + ), TxHistorySubscriber( userWallet = userWallet, isRefresh = isRefresh, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoaderFactory.kt index 5bdf92ac28..34d91fa2cb 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoaderFactory.kt @@ -2,6 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.onramp.GetOnrampTransactionsUseCase +import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase @@ -26,6 +28,8 @@ internal class SingleWalletContentLoaderFactory @Inject constructor( private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase, + private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase, private val analyticsEventHandler: AnalyticsEventHandler, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, ) { @@ -45,6 +49,8 @@ internal class SingleWalletContentLoaderFactory @Inject constructor( getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase, analyticsEventHandler = analyticsEventHandler, walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, + getOnrampTransactionsUseCase = getOnrampTransactionsUseCase, + onrampRemoveTransactionUseCase = onrampRemoveTransactionUseCase, ) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletState.kt index 3190d6f286..d09f324c26 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletState.kt @@ -1,6 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.compose.runtime.Immutable +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.state.TxHistoryState @@ -59,6 +60,8 @@ internal sealed interface WalletState : WalletStateHolder { override val buttons: PersistentList, override val marketPriceBlockState: MarketPriceBlockState, override val txHistoryState: TxHistoryState, + val expressTxsToDisplay: PersistentList, + val expressTxs: PersistentList, ) : SingleCurrency() data class Locked( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetExpressStatusesTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetExpressStatusesTransformer.kt new file mode 100644 index 0000000000..00a849fced --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetExpressStatusesTransformer.kt @@ -0,0 +1,76 @@ +package com.tangem.feature.wallet.presentation.wallet.state.transformers + +import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig +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.onramp.model.cache.OnrampTransaction +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletOnrampTransactionConverter +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents +import kotlinx.collections.immutable.toPersistentList +import timber.log.Timber + +internal class SetExpressStatusesTransformer( + userWalletId: UserWalletId, + private val onrampTxs: List, + private val clickIntents: WalletClickIntents, + private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val appCurrency: AppCurrency, + private val analyticsEventHandler: AnalyticsEventHandler, +) : WalletStateTransformer(userWalletId) { + override fun transform(prevState: WalletState): WalletState { + return when (prevState) { + is WalletState.SingleCurrency.Content -> { + val expressTxs = SingleWalletOnrampTransactionConverter( + clickIntents = clickIntents, + cryptoCurrencyStatus = cryptoCurrencyStatus, + appCurrency = appCurrency, + analyticsEventHandler = analyticsEventHandler, + ).convertList(onrampTxs).toPersistentList() + + val expressTxsToDisplay = expressTxs.filterNot { + it.activeStatus.isHidden + }.toPersistentList() + + val expressBottomSheet = prevState.bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig + val currentTx = expressTxs.firstOrNull { it.info.txId == expressBottomSheet?.value?.info?.txId } + + prevState.copy( + expressTxs = expressTxs, + expressTxsToDisplay = expressTxsToDisplay, + bottomSheetConfig = prevState.bottomSheetConfig?.updateStateWithExpressStatusBottomSheet(currentTx), + ) + } + is WalletState.SingleCurrency.Locked -> { + Timber.w("Impossible to load express statuses for locked wallet") + prevState + } + is WalletState.Visa -> { + Timber.w("Impossible to load express statuses for visa wallet") + prevState + } + is WalletState.MultiCurrency -> { + Timber.w("Impossible to load express statuses for multi-currency wallet") + prevState + } + } + } + + private fun TangemBottomSheetConfig.updateStateWithExpressStatusBottomSheet( + expressState: ExpressTransactionStateUM?, + ): TangemBottomSheetConfig { + val currentConfig = this.content as? ExpressStatusBottomSheetConfig ?: return this + if (expressState == null) return this + return copy( + content = if (currentConfig.value != expressState) { + ExpressStatusBottomSheetConfig(expressState) + } else { + currentConfig + }, + ) + } +} diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletOnrampTransactionConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletOnrampTransactionConverter.kt new file mode 100644 index 0000000000..b7b97ca7e7 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletOnrampTransactionConverter.kt @@ -0,0 +1,279 @@ +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter + +import com.tangem.common.ui.expressStatus.state.ExpressLinkUM +import com.tangem.common.ui.expressStatus.state.ExpressStatusItemState +import com.tangem.common.ui.expressStatus.state.ExpressStatusItemUM +import com.tangem.common.ui.expressStatus.state.ExpressStatusUM +import com.tangem.common.ui.notifications.ExpressNotificationsUM +import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter +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.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.CryptoCurrencyStatus +import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent +import com.tangem.feature.wallet.impl.R +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.persistentListOf + +internal class SingleWalletOnrampTransactionConverter( + private val clickIntents: WalletClickIntents, + cryptoCurrencyStatus: CryptoCurrencyStatus, + private val appCurrency: AppCurrency, + private val analyticsEventHandler: AnalyticsEventHandler, +) : Converter { + + private val iconStateConverter = CryptoCurrencyToIconStateConverter() + + private val currency = cryptoCurrencyStatus.currency + private val status = cryptoCurrencyStatus.value + + override fun convert(value: OnrampTransaction): ExpressTransactionStateUM.OnrampUM { + return ExpressTransactionStateUM.OnrampUM( + info = ExpressTransactionStateInfoUM( + title = resourceReference( + id = R.string.express_status_buying, + wrappedList(currency.name), + ), + status = convertStatuses(value.status, value.externalTxUrl), + notification = getNotification(value.status, value.externalTxUrl, value.providerName), + txId = value.txId, + txExternalId = value.externalTxId, + txExternalUrl = value.externalTxUrl, + timestamp = value.timestamp, + timestampFormatted = resourceReference( + R.string.send_date_format, + wrappedList( + value.timestamp.toDateFormatWithTodayYesterday(), + value.timestamp.toTimeFormat(), + ), + ), + toAmount = stringReference( + value.toAmount.format { crypto(currency) }, + ), + toFiatAmount = stringReference( + status.fiatRate?.multiply(value.toAmount).format { + fiat( + fiatCurrencyCode = appCurrency.code, + fiatCurrencySymbol = appCurrency.symbol, + ) + }, + ), + toAmountSymbol = currency.symbol, + toCurrencyIcon = iconStateConverter.convert(currency), + fromAmount = stringReference( + value.fromAmount.format { + fiat( + fiatCurrencyCode = value.fromCurrency.name, + fiatCurrencySymbol = value.fromCurrency.code, + ) + }, + ), + fromFiatAmount = null, + fromAmountSymbol = value.fromCurrency.code, + fromCurrencyIcon = CurrencyIconState.FiatIcon( + url = value.fromCurrency.image, + fallbackResId = R.drawable.ic_currency_24, + ), + iconState = getIconState(value.status), + onGoToProviderClick = { + analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider) + clickIntents.onGoToProviderClick(it) + }, + onClick = { + val analyticEvent = TokenOnrampAnalyticsEvent.OnrampStatusOpened( + tokenSymbol = currency.symbol, + provider = value.providerName, + fiatCurrency = value.fromCurrency.code, + ) + analyticsEventHandler.send(analyticEvent) + clickIntents.onExpressTransactionClick(value.txId) + }, + ), + providerName = value.providerName, + providerImageUrl = value.providerImageUrl, + providerType = value.providerType, + activeStatus = value.status, + fromCurrencyCode = value.fromCurrency.code, + ) + } + + private fun getNotification( + status: OnrampStatus.Status, + externalTxUrl: String?, + providerName: String, + ): NotificationUM? { + if (externalTxUrl == null) return null + return when (status) { + OnrampStatus.Status.Verifying -> { + analyticsEventHandler.send( + TokenOnrampAnalyticsEvent.NoticeKYC(currency.symbol, providerName), + ) + ExpressNotificationsUM.NeedVerification { + clickIntents.onGoToProviderClick(externalTxUrl) + } + } + OnrampStatus.Status.Failed -> { + ExpressNotificationsUM.FailedByProvider { + clickIntents.onGoToProviderClick(externalTxUrl) + } + } + else -> null + } + } + + 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( + getAwaitingDepositItem(), + getPaymentProcessingItem(), + getBuyingItem(), + getSendingItem(), + ) + } + + return ExpressStatusUM( + title = resourceReference(R.string.common_transaction_status), + link = getStatusLink(status, externalTxUrl), + statuses = statuses, + ) + } + + private fun OnrampStatus.Status.getAwaitingDepositItem() = ExpressStatusItemUM( + text = when { + order < OnrampStatus.Status.WaitingForPayment.order -> { + resourceReference(R.string.express_exchange_status_receiving) + } + this == OnrampStatus.Status.WaitingForPayment -> { + resourceReference(R.string.express_exchange_status_receiving_active) + } + else -> { + resourceReference(R.string.express_exchange_status_received) + } + }, + state = getStatusState(OnrampStatus.Status.WaitingForPayment), + ) + + private fun OnrampStatus.Status.getPaymentProcessingItem() = ExpressStatusItemUM( + text = when { + order < OnrampStatus.Status.PaymentProcessing.order -> { + resourceReference(R.string.express_exchange_status_confirming) + } + this == OnrampStatus.Status.PaymentProcessing -> { + resourceReference(R.string.express_exchange_status_confirming_active) + } + this == OnrampStatus.Status.Verifying -> { + resourceReference(R.string.express_exchange_status_verifying) + } + this == OnrampStatus.Status.Failed -> { + resourceReference(R.string.express_exchange_status_failed) + } + else -> resourceReference(R.string.express_exchange_status_confirmed) + }, + state = when { + order < OnrampStatus.Status.PaymentProcessing.order -> { + ExpressStatusItemState.Default + } + this == OnrampStatus.Status.PaymentProcessing -> { + ExpressStatusItemState.Active + } + this == OnrampStatus.Status.Verifying -> { + ExpressStatusItemState.Warning + } + this == OnrampStatus.Status.Failed -> { + ExpressStatusItemState.Error + } + else -> { + ExpressStatusItemState.Done + } + }, + ) + + private fun OnrampStatus.Status.getBuyingItem() = ExpressStatusItemUM( + text = when { + order < OnrampStatus.Status.Paid.order -> { + resourceReference(R.string.express_status_buying, wrappedList(currency.name)) + } + this == OnrampStatus.Status.Paid -> { + resourceReference( + R.string.express_status_buying_active, + wrappedList(currency.name), + ) + } + else -> { + resourceReference(R.string.express_status_bought, wrappedList(currency.name)) + } + }, + state = getStatusState(OnrampStatus.Status.Paid), + ) + + private fun OnrampStatus.Status.getSendingItem() = ExpressStatusItemUM( + text = when { + order < OnrampStatus.Status.Sending.order -> { + resourceReference( + R.string.express_exchange_status_sending, + wrappedList(currency.name), + ) + } + this == OnrampStatus.Status.Sending -> { + resourceReference( + R.string.express_exchange_status_sending_active, + wrappedList(currency.name), + ) + } + else -> { + resourceReference( + R.string.express_exchange_status_sent, + wrappedList(currency.name), + ) + } + }, + state = getStatusState(OnrampStatus.Status.Sending), + ) + + private fun getStatusLink(status: OnrampStatus.Status, externalTxUrl: String?): ExpressLinkUM { + if (externalTxUrl == null) return ExpressLinkUM.Empty + return when (status) { + OnrampStatus.Status.Verifying, + OnrampStatus.Status.Failed, + -> { + ExpressLinkUM.Content( + icon = R.drawable.ic_arrow_top_right_24, + text = resourceReference(R.string.common_go_to_provider), + onClick = { + clickIntents.onGoToProviderClick(externalTxUrl) + }, + ) + } + else -> ExpressLinkUM.Empty + } + } + + private fun OnrampStatus.Status.getStatusState(targetState: OnrampStatus.Status) = when { + order < targetState.order -> ExpressStatusItemState.Default + this == targetState -> ExpressStatusItemState.Active + else -> ExpressStatusItemState.Done + } +} diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt index 38dba7abb1..987b8c6bce 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt @@ -58,6 +58,8 @@ internal class WalletLoadingStateFactory( value = TxHistoryState.getDefaultLoadingTransactions(clickIntents::onExploreClick), ), ), + expressTxsToDisplay = persistentListOf(), + expressTxs = persistentListOf(), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletExpressStatusesSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletExpressStatusesSubscriber.kt new file mode 100644 index 0000000000..6c83d80975 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletExpressStatusesSubscriber.kt @@ -0,0 +1,92 @@ +package com.tangem.feature.wallet.presentation.wallet.subscribers + +import arrow.core.Either +import arrow.core.getOrElse +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.onramp.GetOnrampTransactionsUseCase +import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase +import com.tangem.domain.onramp.model.cache.OnrampTransaction +import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase +import com.tangem.domain.tokens.error.CurrencyStatusError +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.wallets.models.UserWallet +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetExpressStatusesTransformer +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.* +import timber.log.Timber + +@Suppress("LongParameterList") +internal class SingleWalletExpressStatusesSubscriber( + private val userWallet: UserWallet, + private val stateHolder: WalletStateController, + private val clickIntents: WalletClickIntents, + private val analyticsEventHandler: AnalyticsEventHandler, + private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, + private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase, + private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase, +) : WalletSubscriber() { + + override fun create( + coroutineScope: CoroutineScope, + ): Flow, AppCurrency>> { + return combine( + flow = getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = userWallet.walletId) + .conflate() + .distinctUntilChanged(), + flow2 = getSelectedAppCurrencyUseCase() + .conflate() + .distinctUntilChanged() + .map { maybeAppCurrency -> maybeAppCurrency.getOrElse { AppCurrency.Default } }, + transform = { maybeCurrencyStatus, appCurrency -> maybeCurrencyStatus to appCurrency }, + ).onEach { maybeCurrencyStatusAndAppCurrency -> + val status = maybeCurrencyStatusAndAppCurrency.first.getOrElse { + Timber.e("Unable to get primary currency status: $it") + return@onEach + } + + getOnrampTransactionsUseCase( + userWalletId = userWallet.walletId, + cryptoCurrencyId = status.currency.id, + ).onEach { maybeTransaction -> + maybeTransaction.fold( + ifRight = { onrampTxs -> + onrampTxs.clearHiddenTerminal() + stateHolder.update( + SetExpressStatusesTransformer( + userWalletId = userWallet.walletId, + onrampTxs = onrampTxs, + clickIntents = clickIntents, + cryptoCurrencyStatus = status, + appCurrency = maybeCurrencyStatusAndAppCurrency.second, + analyticsEventHandler = analyticsEventHandler, + ), + ) + }, + ifLeft = { + stateHolder.update( + SetExpressStatusesTransformer( + userWalletId = userWallet.walletId, + onrampTxs = listOf(), + clickIntents = clickIntents, + cryptoCurrencyStatus = status, + appCurrency = maybeCurrencyStatusAndAppCurrency.second, + analyticsEventHandler = analyticsEventHandler, + ), + ) + }, + ) + } + .launchIn(coroutineScope) + } + } + + private suspend fun List.clearHiddenTerminal() { + this.filter { it.status.isHidden && it.status.isTerminal } + .forEach { onrampRemoveTransactionUseCase(externalTxId = it.externalTxId) } + } +} diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 8a47b88c02..d6797374c5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -45,6 +45,9 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.paging.compose.collectAsLazyPagingItems import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheet +import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig +import com.tangem.common.ui.expressStatus.expressTransactionsItems import com.tangem.core.ui.components.atoms.Hand import com.tangem.core.ui.components.atoms.handComposableComponentHeight import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig @@ -216,6 +219,12 @@ private fun WalletContent( walletState.marketPriceBlockState?.let { marketPriceBlockState -> marketPriceBlock(state = marketPriceBlockState, modifier = itemModifier) } + if (walletState is WalletState.SingleCurrency.Content) { + expressTransactionsItems( + expressTxs = walletState.expressTxsToDisplay, + modifier = itemModifier, + ) + } } (selectedWallet as? WalletState.Visa.Content)?.let { @@ -716,6 +725,7 @@ private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) { is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig) is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig) is PushNotificationsBottomSheetConfig -> PushNotificationsBottomSheet(config = bottomSheetConfig) + is ExpressStatusBottomSheetConfig -> ExpressStatusBottomSheet(config = bottomSheetConfig) } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 84de9f0644..4697089b5e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -17,6 +17,7 @@ import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWalletAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore +import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader @@ -33,9 +34,7 @@ import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.utils.Provider -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import com.tangem.utils.coroutines.JobHolder -import com.tangem.utils.coroutines.saveIn +import com.tangem.utils.coroutines.* import com.tangem.utils.extensions.indexOfFirstOrNull import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay @@ -70,6 +69,7 @@ internal class WalletViewModel @Inject constructor( private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase, private val walletImageResolver: WalletImageResolver, private val tokenListStore: MultiWalletTokenListStore, + private val onrampStatusFactory: OnrampStatusFactory, private val walletFeatureToggles: WalletFeatureToggles, analyticsEventsHandler: AnalyticsEventHandler, ) : ViewModel() { @@ -81,6 +81,8 @@ internal class WalletViewModel @Inject constructor( private val refreshWalletJobHolder = JobHolder() private var needToRefreshWallet = false + private var expressTxStatusTaskScheduler = SingleTaskScheduler() + init { analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.ScreenOpened) @@ -93,6 +95,7 @@ internal class WalletViewModel @Inject constructor( subscribeOnSelectedWalletFlow() subscribeToScreenBackgroundState() subscribeOnPushNotificationsPermission() + subscribeOnExpressTransactionsUpdates() } private fun maybeMigrateNames() { @@ -239,6 +242,24 @@ internal class WalletViewModel @Inject constructor( .launchIn(viewModelScope) } + private fun subscribeOnExpressTransactionsUpdates() { + viewModelScope.launch(dispatchers.main) { + expressTxStatusTaskScheduler.cancelTask() + expressTxStatusTaskScheduler.scheduleTask( + viewModelScope, + PeriodicTask( + isDelayFirst = false, + delay = EXPRESS_STATUS_UPDATE_DELAY, + task = { + runCatching { onrampStatusFactory.updateOnrmapTransactionStatuses() } + }, + onSuccess = { /* no-op */ }, + onError = { /* no-op */ }, + ), + ) + } + } + private fun needToRefreshTimer() { viewModelScope.launch { delay(REFRESH_WALLET_BACKGROUND_TIMER_MILLIS) @@ -430,5 +451,6 @@ internal class WalletViewModel @Inject constructor( private companion object { const val REFRESH_WALLET_BACKGROUND_TIMER_MILLIS = 10000L + const val EXPRESS_STATUS_UPDATE_DELAY = 10000L } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt index e34a2aa9d6..81f767c6b0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt @@ -1,6 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents import arrow.core.getOrElse +import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase @@ -11,10 +12,14 @@ import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.usecase.GetUserWalletUseCase +import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory import com.tangem.feature.wallet.presentation.wallet.domain.unwrap import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped @@ -43,6 +48,12 @@ internal interface WalletContentClickIntents { fun onTokenItemLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus) fun onTransactionClick(txHash: String) + + fun onDissmissBottomSheet() + + fun onGoToProviderClick(externalTxId: String) + + fun onExpressTransactionClick(txId: String) } @Suppress("LongParameterList") @@ -51,6 +62,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor( private val stateHolder: WalletStateController, private val currencyActionsClickIntents: WalletCurrencyActionsClickIntentsImplementor, private val walletWarningsClickIntents: WalletWarningsClickIntentsImplementor, + private val onrampStatusFactory: OnrampStatusFactory, private val getUserWalletUseCase: GetUserWalletUseCase, private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase, @@ -164,4 +176,39 @@ internal class WalletContentClickIntentsImplementor @Inject constructor( ) } } + + override fun onGoToProviderClick(externalTxUrl: String) { + router.openUrl(externalTxUrl) + } + + override fun onDissmissBottomSheet() { + val userWalletId = stateHolder.getSelectedWalletId() + if (stateHolder.getSelectedWallet().bottomSheetConfig?.content is ExpressStatusBottomSheetConfig) { + viewModelScope.launch(dispatchers.main) { + onrampStatusFactory.removeTransactionOnBottomSheetClosed() + } + } + stateHolder.update(CloseBottomSheetTransformer(userWalletId)) + } + + override fun onExpressTransactionClick(txId: String) { + viewModelScope.launch { + val userWalletId = stateHolder.getSelectedWalletId() + val singleWalletState = stateHolder.getSelectedWallet() as? WalletState.SingleCurrency.Content + ?: return@launch + + val expressTransaction = singleWalletState.expressTxsToDisplay.firstOrNull { it.info.txId == txId } + ?: return@launch + + stateHolder.update( + OpenBottomSheetTransformer( + userWalletId = userWalletId, + content = ExpressStatusBottomSheetConfig( + value = expressTransaction, + ), + onDismissBottomSheet = ::onDissmissBottomSheet, + ), + ) + } + } }