Updated on 2026-08-14

This commit is contained in:
Tangem 2024-12-19 17:01:05 +05:00
parent 5c8102a75a
commit 47f9a36fb4
19 changed files with 115 additions and 93 deletions

View file

@ -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

View file

@ -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<ExchangeStatusState>,
val notification: ExchangeStatusNotifications? = null,
val showProviderLink: Boolean,
val isRefundTerminalStatus: Boolean,
val fromCryptoCurrency: CryptoCurrency,
val toCryptoCurrency: CryptoCurrency,
) : ExpressTransactionStateUM

View file

@ -1,68 +0,0 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.express
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 {
abstract val info: ExpressTransactionStateInfoUM
data class ExchangeUM(
override val info: ExpressTransactionStateInfoUM,
val provider: SwapProvider,
val activeStatus: ExchangeStatus?,
val statuses: ImmutableList<ExchangeStatusState>,
val notification: ExchangeStatusNotifications? = null,
val showProviderLink: Boolean,
val isRefundTerminalStatus: Boolean,
val fromCryptoCurrency: CryptoCurrency,
val toCryptoCurrency: CryptoCurrency,
) : ExpressTransactionStateUM()
data class OnrampUM(
override val info: ExpressTransactionStateInfoUM,
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 activeStatus: OnrampStatus.Status,
val fromCurrencyCode: String,
) : ExpressTransactionStateUM()
}
internal data class ExpressTransactionStateInfoUM(
val title: TextReference,
val status: ExpressStatusUM,
val notification: NotificationUM?,
val txId: String,
val txExternalId: String?,
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?,
val toAmountSymbol: String,
val toCurrencyIcon: CurrencyIconState,
val fromAmount: TextReference,
val fromFiatAmount: TextReference?,
val fromAmountSymbol: String,
val fromCurrencyIcon: CurrencyIconState,
)
internal enum class ExpressTransactionStateIconUM {
Warning,
Error,
None,
}

View file

@ -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

View file

@ -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<AppCurrency>,
) : Converter<Unit, PersistentList<ExpressTransactionStateUM.ExchangeUM>> {
) : Converter<Unit, PersistentList<ExchangeUM>> {
private val iconStateConverter = CryptoCurrencyToIconStateConverter()
private val appCurrency = appCurrencyProvider()
override fun convert(value: Unit): PersistentList<ExpressTransactionStateUM.ExchangeUM> {
override fun convert(value: Unit): PersistentList<ExchangeUM> {
return persistentListOf()
}
fun convert(
savedTransactions: List<SavedSwapTransactionListModel>,
quotes: Set<Quote>,
): PersistentList<ExpressTransactionStateUM.ExchangeUM> {
val result = mutableListOf<ExpressTransactionStateUM.ExchangeUM>()
): PersistentList<ExchangeUM> {
val result = mutableListOf<ExchangeUM>()
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

View file

@ -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<PersistentList<ExpressTransactionStateUM.ExchangeUM>> {
suspend operator fun invoke(): Flow<PersistentList<ExchangeUM>> {
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<SavedSwapTransactionListModel>?,
quotes: Set<Quote>,
): PersistentList<ExpressTransactionStateUM.ExchangeUM> {
): PersistentList<ExchangeUM> {
if (savedTransactions == null) {
return persistentListOf()
}

View file

@ -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()
}
}

View file

@ -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

View file

@ -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

View file

@ -1,71 +0,0 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
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(
timestamp: TextReference,
fromTokenIconState: CurrencyIconState,
toTokenIconState: CurrencyIconState,
fromCryptoAmount: TextReference,
fromCryptoSymbol: String,
toCryptoAmount: TextReference,
toCryptoSymbol: String,
fromFiatAmount: TextReference?,
toFiatAmount: TextReference?,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.action),
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(
start = TangemTheme.dimens.spacing12,
end = TangemTheme.dimens.spacing12,
top = TangemTheme.dimens.spacing14,
bottom = TangemTheme.dimens.spacing2,
),
) {
Text(
text = stringResource(id = R.string.express_estimated_amount),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
Text(
text = timestamp.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
}
InputRowApprox(
leftIcon = fromTokenIconState,
leftTitle = fromCryptoAmount,
leftSubtitle = fromFiatAmount,
leftTitleEllipsisOffset = fromCryptoSymbol.length,
rightIcon = toTokenIconState,
rightTitle = toCryptoAmount,
rightSubtitle = toFiatAmount,
rightTitleEllipsisOffset = toCryptoSymbol.length,
)
}
}

View file

@ -1,109 +0,0 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express
import android.content.res.Configuration
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.inputrow.InputRowBestRate
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@Composable
internal fun ExpressProvider(
providerName: TextReference,
providerType: TextReference,
providerTxId: String?,
imageUrl: String,
) {
val clipboardManager = LocalClipboardManager.current
val hapticFeedback = LocalHapticFeedback.current
val context = LocalContext.current
Column(
modifier = Modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.action),
) {
Row(
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing12)
.padding(horizontal = TangemTheme.dimens.spacing12)
.fillMaxWidth(),
) {
Text(
text = stringResource(id = R.string.express_provider),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
SpacerWMax()
if (!providerTxId.isNullOrEmpty()) {
Row(
modifier = Modifier
.padding(start = 8.dp)
.clickable {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
clipboardManager.setText(AnnotatedString(providerTxId))
Toast.makeText(context, R.string.express_transaction_id_copied, Toast.LENGTH_SHORT).show()
},
) {
Icon(
modifier = Modifier
.size(TangemTheme.dimens.size20)
.padding(end = TangemTheme.dimens.spacing4)
.align(Alignment.CenterVertically),
painter = painterResource(id = R.drawable.ic_copy_24),
contentDescription = null,
tint = TangemTheme.colors.text.tertiary,
)
Text(
modifier = Modifier.align(Alignment.CenterVertically),
text = stringResource(R.string.express_transaction_id, providerTxId),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
}
}
}
InputRowBestRate(
imageUrl = imageUrl,
title = providerName,
titleExtra = providerType,
subtitle = TextReference.Res(R.string.express_floating_rate),
)
}
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun ExpressProvider_Preview() {
TangemThemePreview {
ExpressProvider(
providerName = TextReference.Str("Changelly"),
providerType = TextReference.Str("CEX"),
providerTxId = "hjsbajcqbhjsbajcqbhjsbajcqbhjsbajcqbhjsbajcqb",
imageUrl = "",
)
}
}

View file

@ -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)
}
}
}

View file

@ -1,191 +0,0 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express
import android.content.res.Configuration
import androidx.annotation.DrawableRes
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.constraintlayout.compose.*
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
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.extensions.TextReference
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
internal fun ExpressStatusItem(
title: TextReference,
fromTokenIconState: CurrencyIconState,
toTokenIconState: CurrencyIconState,
fromAmount: TextReference,
fromSymbol: String,
toSymbol: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
toAmount: TextReference = TextReference.EMPTY,
@DrawableRes infoIconRes: Int? = null,
infoIconTint: Color? = null,
) {
ConstraintLayout(
modifier = modifier
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.primary)
.clickable { onClick() }
.padding(TangemTheme.dimens.spacing12),
) {
val (titleRef, iconRef, infoIconRef, swapIconRef, fromRef, toRef, fromIconRef, toIconRef) = createRefs()
val padding6 = TangemTheme.dimens.spacing6
Text(
text = title.resolveReference(),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier.constrainAs(titleRef) {
start.linkTo(parent.start)
top.linkTo(parent.top)
},
)
CurrencyIcon(
state = fromTokenIconState,
shouldDisplayNetwork = false,
modifier = Modifier
.size(TangemTheme.dimens.size20)
.constrainAs(fromIconRef) {
start.linkTo(parent.start)
top.linkTo(titleRef.bottom, padding6)
bottom.linkTo(parent.bottom)
},
)
EllipsisText(
text = fromAmount.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
ellipsis = TextEllipsis.OffsetEnd(fromSymbol.length),
modifier = Modifier.constrainAs(fromRef) {
start.linkTo(fromIconRef.end, padding6)
top.linkTo(titleRef.bottom, padding6)
end.linkTo(swapIconRef.start)
bottom.linkTo(parent.bottom)
width = Dimension.fillToConstraints.atMostWrapContent
},
)
Icon(
painter = painterResource(id = R.drawable.ic_forward_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
modifier = Modifier
.size(TangemTheme.dimens.size12)
.constrainAs(swapIconRef) {
start.linkTo(fromRef.end, padding6)
top.linkTo(titleRef.bottom, padding6)
end.linkTo(toIconRef.start)
bottom.linkTo(parent.bottom)
},
)
CurrencyIcon(
state = toTokenIconState,
shouldDisplayNetwork = false,
modifier = Modifier
.size(TangemTheme.dimens.size20)
.constrainAs(toIconRef) {
start.linkTo(swapIconRef.end, padding6)
top.linkTo(titleRef.bottom, padding6)
end.linkTo(toRef.start)
bottom.linkTo(parent.bottom)
},
)
EllipsisText(
text = toAmount.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
ellipsis = TextEllipsis.OffsetEnd(toSymbol.length),
modifier = Modifier.constrainAs(toRef) {
start.linkTo(toIconRef.end, padding6)
top.linkTo(titleRef.bottom, padding6)
end.linkTo(infoIconRef.start, padding6, padding6)
bottom.linkTo(parent.bottom)
width = Dimension.fillToConstraints.atLeastWrapContent
},
)
Icon(
painter = painterResource(id = infoIconRes ?: R.drawable.ic_alert_triangle_20),
contentDescription = null,
tint = infoIconTint ?: TangemTheme.colors.icon.informative,
modifier = Modifier
.size(TangemTheme.dimens.size20)
.constrainAs(infoIconRef) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
end.linkTo(iconRef.start)
visibility = if (infoIconRes == null) {
Visibility.Gone
} else {
Visibility.Visible
}
},
)
Icon(
painter = painterResource(id = R.drawable.ic_chevron_right_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
modifier = Modifier
.size(TangemTheme.dimens.size24)
.constrainAs(iconRef) {
end.linkTo(parent.end)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
},
)
}
}
//region Preview
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun ExpressStatusItemPreview(
@PreviewParameter(ExpressStatusItemPreviewParameterProvider::class) amount: String,
) {
TangemThemePreview {
ExpressStatusItem(
title = stringReference("ChangeNow"),
fromTokenIconState = CurrencyIconState.Loading,
toTokenIconState = CurrencyIconState.Loading,
fromAmount = stringReference(amount),
fromSymbol = "USDT",
toAmount = stringReference(amount),
toSymbol = "USDT",
onClick = {},
infoIconRes = null,
infoIconTint = null,
)
}
}
private class ExpressStatusItemPreviewParameterProvider : PreviewParameterProvider<String> {
override val values: Sequence<String>
get() = sequenceOf(
"1111111111111111111111111111 USDT",
"11111 USDT",
)
}
//endregion

View file

@ -1,45 +0,0 @@
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.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(
expressTxs: PersistentList<ExpressTransactionStateUM>,
modifier: Modifier = Modifier,
) {
items(
count = expressTxs.size,
key = { expressTxs[it].info.txId },
contentType = { expressTxs[it]::class.java },
) {
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
}
ExpressTransactionStateIconUM.None -> null to null
}
ExpressStatusItem(
title = itemInfo.title,
fromTokenIconState = itemInfo.fromCurrencyIcon,
toTokenIconState = itemInfo.toCurrencyIcon,
fromAmount = itemInfo.fromAmount,
fromSymbol = itemInfo.fromAmountSymbol,
toAmount = itemInfo.toAmount,
toSymbol = itemInfo.toAmountSymbol,
onClick = itemInfo.onClick,
infoIconRes = iconRes,
infoIconTint = tint,
modifier = modifier.animateItem(),
)
}
}

View file

@ -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(

View file

@ -1,68 +0,0 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.onramp
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.core.ui.R
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.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) {
Column(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16)) {
SpacerH10()
Text(
text = stringResource(id = R.string.common_transaction_status),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.align(CenterHorizontally),
)
SpacerH10()
Text(
text = stringResource(id = R.string.express_exchange_status_subtitle),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
modifier = Modifier
.align(CenterHorizontally),
)
SpacerH16()
ExpressEstimate(
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(state.providerName),
providerType = stringReference(state.providerType),
providerTxId = state.info.txExternalId,
imageUrl = state.providerImageUrl,
)
SpacerH12()
ExpressStatusBlock(state = state.info.status)
ExpressStatusNotificationBlock(state = state.info.notification)
SpacerH24()
}
}

View file

@ -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