diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt index dc27c1641a..ce8b600e55 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt @@ -55,6 +55,10 @@ object DateTimeFormatters { .withLocale(Locale.getDefault()) } + val dateTimeFormatter: DateTimeFormatter by lazy { + DateTimeFormat.forPattern("dd.MM.yyyy HH:mm") + } + fun formatTime(formatter: DateTimeFormatter = timeFormatter, time: DateTime): String { return formatter.print(time) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBottomSheetConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBottomSheetConfig.kt index 5945197384..bfabae7770 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBottomSheetConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBottomSheetConfig.kt @@ -3,11 +3,8 @@ package com.tangem.feature.wallet.presentation.wallet.state.model import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent internal data class BalancesAndLimitsBottomSheetConfig( - val currency: String, val balance: Balance, val limit: Limit, - val onBalanceInfoClick: () -> Unit, - val onLimitInfoClick: () -> Unit, ) : TangemBottomSheetConfigContent { data class Balance( @@ -17,6 +14,7 @@ internal data class BalancesAndLimitsBottomSheetConfig( val debit: String, val pending: String, val amlVerified: String, + val onInfoClick: () -> Unit, ) data class Limit( @@ -24,5 +22,6 @@ internal data class BalancesAndLimitsBottomSheetConfig( val inStore: String, val other: String, val singleTransaction: String, + val onInfoClick: () -> Unit, ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/VisaTxDetailsBottomSheetConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/VisaTxDetailsBottomSheetConfig.kt new file mode 100644 index 0000000000..6389eb3a0f --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/VisaTxDetailsBottomSheetConfig.kt @@ -0,0 +1,39 @@ +package com.tangem.feature.wallet.presentation.wallet.state.model + +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import kotlinx.collections.immutable.ImmutableList + +internal data class VisaTxDetailsBottomSheetConfig( + val transaction: Transaction, + val requests: ImmutableList, +) : TangemBottomSheetConfigContent { + + data class Transaction( + val id: String, + val type: String, + val status: String, + val blockchainAmount: String, + val blockchainFee: String, + val transactionAmount: String, + val transactionCurrencyCode: String, + val merchantName: String, + val merchantCity: String, + val merchantCountryCode: String, + val merchantCategoryCode: String, + ) + + data class Request( + val id: String, + val type: String, + val status: String, + val blockchainAmount: String, + val blockchainFee: String, + val transactionAmount: String, + val currencyCode: String, + val errorCode: Int, + val date: String, + val txHash: String, + val txStatus: String, + val onExploreClick: (() -> Unit)?, + ) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt index 14f9493984..f43ec73629 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt @@ -15,8 +15,13 @@ internal class BalancesAndLimitsBottomSheetConverter( ) : Converter { override fun convert(value: VisaCurrency): BalancesAndLimitsBottomSheetConfig { + fun formatAmount(amount: BigDecimal): String = BigDecimalFormatter.formatCryptoAmount( + amount, + cryptoCurrency = value.symbol, + decimals = value.decimals, + ) + return BalancesAndLimitsBottomSheetConfig( - currency = value.symbol, balance = BalancesAndLimitsBottomSheetConfig.Balance( totalBalance = value.balances.total.let(::formatAmount), availableBalance = value.balances.available.let(::formatAmount), @@ -24,24 +29,18 @@ internal class BalancesAndLimitsBottomSheetConverter( debit = value.balances.debt.let(::formatAmount), pending = value.balances.pendingRefund.let(::formatAmount), amlVerified = value.balances.verified.let(::formatAmount), + onInfoClick = this::showBalanceInfo, ), limit = BalancesAndLimitsBottomSheetConfig.Limit( availableBy = DateTimeFormatters.formatDate(date = value.limits.expirationDate), inStore = value.limits.remainingOtp.let(::formatAmount), other = value.limits.remainingNoOtp.let(::formatAmount), singleTransaction = value.limits.singleTransaction.let(::formatAmount), + onInfoClick = this::showLimitInfo, ), - onBalanceInfoClick = this::showBalanceInfo, - onLimitInfoClick = this::showLimitInfo, ) } - private fun formatAmount(amount: BigDecimal): String = BigDecimalFormatter.formatCryptoAmount( - amount, - cryptoCurrency = "", - decimals = 2, - ) - private fun showBalanceInfo() { eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaBalancesInfo)) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxDetailsBottomSheetConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxDetailsBottomSheetConverter.kt new file mode 100644 index 0000000000..f431a74a5e --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxDetailsBottomSheetConverter.kt @@ -0,0 +1,86 @@ +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter + +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.core.ui.utils.DateTimeFormatters +import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.visa.model.VisaTxDetails +import com.tangem.feature.wallet.presentation.wallet.state.model.VisaTxDetailsBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.VisaWalletIntents +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.toImmutableList +import org.joda.time.DateTimeZone +import java.math.BigDecimal +import java.util.Currency + +internal class VisaTxDetailsBottomSheetConverter( + private val visaCurrency: VisaCurrency, + private val clickIntents: VisaWalletIntents, +) : Converter { + + override fun convert(value: VisaTxDetails): VisaTxDetailsBottomSheetConfig { + return VisaTxDetailsBottomSheetConfig( + transaction = createTransaction(value), + requests = value.requests.map(::createRequest).toImmutableList(), + ) + } + + private fun createTransaction(details: VisaTxDetails): VisaTxDetailsBottomSheetConfig.Transaction { + return VisaTxDetailsBottomSheetConfig.Transaction( + id = details.id, + type = details.type, + status = details.status, + blockchainAmount = formatNetworkAmount(details.blockchainAmount), + blockchainFee = formatNetworkAmount(details.blockchainFee), + transactionAmount = formatFiatAmount(details.transactionAmount, details.fiatCurrency), + transactionCurrencyCode = details.transactionCurrencyCode.toString(), + merchantName = details.merchantName ?: UNKNOWN, + merchantCity = details.merchantCity ?: UNKNOWN, + merchantCountryCode = details.merchantCountryCode ?: UNKNOWN, + merchantCategoryCode = details.merchantCategoryCode ?: UNKNOWN, + ) + } + + private fun createRequest(request: VisaTxDetails.Request): VisaTxDetailsBottomSheetConfig.Request { + val localDate = request.requestDate.withZone(DateTimeZone.getDefault()) + val exploreUrl = request.exploreUrl + + return VisaTxDetailsBottomSheetConfig.Request( + id = request.id, + type = request.requestType, + status = request.requestStatus, + blockchainAmount = formatNetworkAmount(request.blockchainAmount), + blockchainFee = formatNetworkAmount(request.blockchainFee), + transactionAmount = formatFiatAmount(request.transactionAmount, request.fiatCurrency), + currencyCode = request.billingCurrencyCode.toString(), + errorCode = request.errorCode, + date = DateTimeFormatters.formatDate(DateTimeFormatters.dateTimeFormatter, date = localDate), + txHash = request.txHash ?: UNKNOWN, + txStatus = request.txStatus ?: UNKNOWN, + onExploreClick = if (exploreUrl != null) { + { clickIntents.onExploreClick(exploreUrl) } + } else { + null + }, + ) + } + + private fun formatNetworkAmount(amount: BigDecimal): String { + return BigDecimalFormatter.formatCryptoAmount( + cryptoAmount = amount, + cryptoCurrency = visaCurrency.symbol, + decimals = visaCurrency.decimals, + ) + } + + private fun formatFiatAmount(amount: BigDecimal, fiatCurrency: Currency): String { + return BigDecimalFormatter.formatFiatAmount( + fiatAmount = amount, + fiatCurrencyCode = fiatCurrency.currencyCode, + fiatCurrencySymbol = fiatCurrency.symbol, + ) + } + + private companion object { + const val UNKNOWN = "Unknown" + } +} \ No newline at end of file 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 3dcd34d73e..e491dcb364 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 @@ -35,6 +35,7 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.controlButtons import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.marketPriceBlock import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.BalancesAndLimitsBottomSheet +import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.VisaTxDetailsBottomSheet import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.balancesAndLimitsBlock import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.depositButton import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator @@ -169,6 +170,7 @@ private fun WalletContent( is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig) is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig) is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig) + is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt index fce6384945..c2e6a39b4d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt @@ -13,11 +13,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import com.tangem.core.ui.components.SpacerH16 -import com.tangem.core.ui.components.SpacerWMax import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -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.feature.wallet.impl.R @@ -45,136 +42,92 @@ private fun BalancesAndLimitsContent(config: BalancesAndLimitsBottomSheetConfig, ) }, firstBlock = { - BlockContent( - title = stringReference("Balance, ${config.currency}"), - content = { - BlockItem( - title = stringReference("Total"), - value = config.balance.totalBalance, - ) - BlockItem( - title = stringReference("AML Verified"), - value = config.balance.amlVerified, - ) - BlockItem( - title = stringReference("Available"), - value = config.balance.availableBalance, - ) - BlockItem( - title = stringReference("Blocked"), - value = config.balance.blockedBalance, - ) - BlockItem( - title = stringReference("Debit"), - value = config.balance.debit, - ) - BlockItem( - title = stringReference("Pending refund"), - value = config.balance.pending, - ) - }, - onInfoIconClick = config.onBalanceInfoClick, - ) + BalancesBlock(balances = config.balance) }, secondBlock = { - BlockContent( - title = stringReference("Limits, ${config.currency}"), - description = stringReference("Available by ${config.limit.availableBy}"), - content = { - BlockItem( - title = stringReference("In-store (otp)"), - value = config.limit.inStore, - ) - BlockItem( - title = stringReference("Other (no-otp)"), - value = config.limit.other, - ) - BlockItem( - title = stringReference("Single transaction"), - value = config.limit.singleTransaction, - ) - }, - onInfoIconClick = config.onBalanceInfoClick, - ) + LimitsBlock(limits = config.limit) }, ) } @Composable -private inline fun BlockContent( - title: TextReference, - content: @Composable ColumnScope.() -> Unit, - noinline onInfoIconClick: () -> Unit, - modifier: Modifier = Modifier, - description: TextReference? = null, -) { - Column( - modifier = modifier - .padding(horizontal = TangemTheme.dimens.spacing16) - .fillMaxWidth() - .background( - color = TangemTheme.colors.background.primary, - shape = TangemTheme.shapes.roundedCornersXMedium, +private fun BalancesBlock(balances: BalancesAndLimitsBottomSheetConfig.Balance, modifier: Modifier = Modifier) { + BlockContent( + modifier = modifier, + title = stringReference("Balance"), + content = { + BlockItem( + title = stringReference("Total"), + value = balances.totalBalance, ) - .padding(vertical = TangemTheme.dimens.spacing8), - ) { - Row( - modifier = Modifier - .padding( - start = TangemTheme.dimens.spacing12, - end = TangemTheme.dimens.spacing4, - ) - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = title.resolveReference(), - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.tertiary, + BlockItem( + title = stringReference("AML Verified"), + value = balances.amlVerified, ) - SpacerWMax() - if (description != null) { - Text( - text = description.resolveReference(), - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.tertiary, - ) - } - IconButton( - modifier = Modifier.size(TangemTheme.dimens.size32), - onClick = onInfoIconClick, - ) { - Icon( - modifier = Modifier.size(TangemTheme.dimens.size16), - painter = painterResource(id = R.drawable.ic_information_24), - tint = TangemTheme.colors.icon.informative, - contentDescription = null, - ) - } - } - content() - } + BlockItem( + title = stringReference("Available"), + value = balances.availableBalance, + ) + BlockItem( + title = stringReference("Blocked"), + value = balances.blockedBalance, + ) + BlockItem( + title = stringReference("Debit"), + value = balances.debit, + ) + BlockItem( + title = stringReference("Pending refund"), + value = balances.pending, + ) + }, + description = { + InfoButton(onClick = balances.onInfoClick) + }, + ) } @Composable -private fun BlockItem(title: TextReference, value: String, modifier: Modifier = Modifier) { - Row( - modifier = modifier - .padding(horizontal = TangemTheme.dimens.spacing12) - .fillMaxWidth() - .heightIn(min = TangemTheme.dimens.size32), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, +private fun LimitsBlock(limits: BalancesAndLimitsBottomSheetConfig.Limit, modifier: Modifier = Modifier) { + BlockContent( + modifier = modifier, + title = stringReference("Limits"), + content = { + BlockItem( + title = stringReference("In-store (otp)"), + value = limits.inStore, + ) + BlockItem( + title = stringReference("Other (no-otp)"), + value = limits.other, + ) + BlockItem( + title = stringReference("Single transaction"), + value = limits.singleTransaction, + ) + }, + description = { + Text( + text = "Available till ${limits.availableBy}", + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.tertiary, + ) + InfoButton(onClick = limits.onInfoClick) + }, + ) +} + +@Composable +private fun InfoButton(onClick: () -> Unit, modifier: Modifier = Modifier) { + IconButton( + modifier = modifier.size(TangemTheme.dimens.size32), + onClick = onClick, ) { - Text( - text = title.resolveReference(), - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.tertiary, - ) - Text( - text = value, - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.primary1, + Icon( + modifier = Modifier.size(TangemTheme.dimens.size16), + painter = painterResource(id = R.drawable.ic_information_24), + tint = TangemTheme.colors.icon.informative, + contentDescription = null, ) } } @@ -229,23 +182,22 @@ private class BalancesAndLimitsBottomSheetParameterProvider : CollectionPreviewParameterProvider( collection = listOf( BalancesAndLimitsBottomSheetConfig( - currency = "USDT", balance = BalancesAndLimitsBottomSheetConfig.Balance( - totalBalance = "492.45", - availableBalance = "392.45", - blockedBalance = "36.00", - debit = "00.00", - pending = "20.99", - amlVerified = "356.45", + totalBalance = "492.45 USDT", + availableBalance = "392.45 USDT", + blockedBalance = "36.00 USDT", + debit = "00.00 USDT", + pending = "20.99 USDT", + amlVerified = "356.45 USDT", + onInfoClick = {}, ), limit = BalancesAndLimitsBottomSheetConfig.Limit( - availableBy = "Nov, 11", - inStore = "563.00", - other = "100.00", - singleTransaction = "100.00", + availableBy = "Nov, 11 USDT", + inStore = "563.00 USDT", + other = "100.00 USDT", + singleTransaction = "100.00 USDT", + onInfoClick = {}, ), - onBalanceInfoClick = {}, - onLimitInfoClick = {}, ), ), ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/Blocks.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/Blocks.kt new file mode 100644 index 0000000000..723fabf3bc --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/Blocks.kt @@ -0,0 +1,83 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components.visa + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import com.tangem.core.ui.components.SpacerH8 +import com.tangem.core.ui.components.SpacerWMax +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme + +private const val BLOCK_ITEM_NAME_WEIGHT = .45f +private const val BLOCK_ITEM_VALUE_WEIGHT = .55f + +@Composable +internal inline fun BlockContent( + title: TextReference, + content: @Composable ColumnScope.() -> Unit, + modifier: Modifier = Modifier, + description: @Composable RowScope.() -> Unit = {}, +) { + Column( + modifier = modifier + .padding(horizontal = TangemTheme.dimens.spacing16) + .fillMaxWidth() + .background( + color = TangemTheme.colors.background.primary, + shape = TangemTheme.shapes.roundedCornersXMedium, + ), + ) { + Row( + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing12) + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size42), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = title.resolveReference(), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + SpacerWMax() + description() + } + content() + SpacerH8() + } +} + +@Composable +internal fun BlockItem(title: TextReference, value: String, modifier: Modifier = Modifier) { + Row( + modifier = modifier + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size32) + .padding( + vertical = TangemTheme.dimens.spacing8, + horizontal = TangemTheme.dimens.spacing12, + ), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.Top, + ) { + Text( + modifier = Modifier.weight(BLOCK_ITEM_NAME_WEIGHT), + text = title.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Start, + ) + Text( + modifier = Modifier.weight(BLOCK_ITEM_VALUE_WEIGHT), + text = value, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.primary1, + textAlign = TextAlign.End, + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/VisaTxDetailsBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/VisaTxDetailsBottomSheet.kt new file mode 100644 index 0000000000..eafdaf3801 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/VisaTxDetailsBottomSheet.kt @@ -0,0 +1,286 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components.visa + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import com.tangem.core.ui.components.SpacerW12 +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.wallet.state.model.VisaTxDetailsBottomSheetConfig +import kotlinx.collections.immutable.persistentListOf + +@Composable +internal fun VisaTxDetailsBottomSheet(config: TangemBottomSheetConfig) { + TangemBottomSheet( + config = config, + containerColor = TangemTheme.colors.background.secondary, + ) { content: VisaTxDetailsBottomSheetConfig -> + VisaTxDetailsBottomSheetContent(content) + } +} + +@Composable +private fun VisaTxDetailsBottomSheetContent(config: VisaTxDetailsBottomSheetConfig, modifier: Modifier = Modifier) { + ContentContainer( + modifier = modifier, + blocksCount = config.requests.size.inc(), + title = { + Text( + text = "Transaction Details", + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + }, + block = { index -> + if (index == 0) { + TransactionBlock(config.transaction) + } else { + BlockchainRequestBlock(config.requests[index - 1]) + } + }, + ) +} + +@Composable +private fun TransactionBlock(transaction: VisaTxDetailsBottomSheetConfig.Transaction, modifier: Modifier = Modifier) { + BlockContent( + modifier = modifier, + title = stringReference(value = "Transaction"), + content = { + BlockItem( + title = stringReference(value = "Type"), + value = transaction.type, + ) + BlockItem( + title = stringReference(value = "Status"), + value = transaction.status, + ) + BlockItem( + title = stringReference(value = "Blockchain Amount"), + value = transaction.blockchainAmount, + ) + BlockItem( + title = stringReference(value = "Blockchain Fee"), + value = transaction.blockchainFee, + ) + BlockItem( + title = stringReference(value = "Transaction Amount"), + value = transaction.transactionAmount, + ) + BlockItem( + title = stringReference(value = "Currency Code"), + value = transaction.transactionCurrencyCode, + ) + BlockItem( + title = stringReference(value = "Merchant Name"), + value = transaction.merchantName, + ) + BlockItem( + title = stringReference(value = "Merchant City"), + value = transaction.merchantCity, + ) + BlockItem( + title = stringReference(value = "Merchant Country Code"), + value = transaction.merchantCountryCode, + ) + BlockItem( + title = stringReference(value = "Merchant Category Code"), + value = transaction.merchantCategoryCode, + ) + }, + ) +} + +@Composable +private fun BlockchainRequestBlock(request: VisaTxDetailsBottomSheetConfig.Request, modifier: Modifier = Modifier) { + BlockContent( + modifier = modifier, + title = stringReference(value = "Blockchain request"), + description = { + if (request.onExploreClick == null) return + + Row( + modifier = Modifier.clickable(onClick = request.onExploreClick), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4), + ) { + Icon( + painter = painterResource(id = R.drawable.ic_compass_24), + contentDescription = null, + modifier = Modifier.size(size = TangemTheme.dimens.size18), + tint = TangemTheme.colors.icon.informative, + ) + Text( + text = "Explore", + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.caption1, + ) + } + SpacerW12() + }, + content = { + BlockItem( + title = stringReference(value = "Type"), + value = request.type, + ) + BlockItem( + title = stringReference(value = "Status"), + value = request.status, + ) + BlockItem( + title = stringReference(value = "Blockchain Amount"), + value = request.blockchainAmount, + ) + BlockItem( + title = stringReference(value = "Blockchain Fee"), + value = request.blockchainFee, + ) + BlockItem( + title = stringReference(value = "Transaction Amount"), + value = request.transactionAmount, + ) + BlockItem( + title = stringReference(value = "Currency Code"), + value = request.currencyCode, + ) + BlockItem( + title = stringReference(value = "Error Code"), + value = request.errorCode.toString(), + ) + BlockItem( + title = stringReference(value = "Date"), + value = request.date, + ) + BlockItem( + title = stringReference(value = "Tx Hash"), + value = request.txHash, + ) + BlockItem( + title = stringReference(value = "Tx Status"), + value = request.txStatus, + ) + }, + ) +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun ContentContainer( + blocksCount: Int, + title: @Composable BoxScope.() -> Unit, + block: @Composable ColumnScope.(Int) -> Unit, + modifier: Modifier = Modifier, +) { + LazyColumn( + modifier = modifier.background(TangemTheme.colors.background.secondary), + contentPadding = PaddingValues( + bottom = TangemTheme.dimens.spacing16, + ), + verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing12), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + stickyHeader { + Box( + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size44) + .background(TangemTheme.colors.background.secondary), + contentAlignment = Alignment.Center, + content = title, + ) + } + items(blocksCount) { index -> + Column { + block(index) + } + } + } +} + +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun VisaTxDetailsBottomSheetPreview_Light( + @PreviewParameter(VisaTxDetailsBottomSheetParameterProvider::class) state: VisaTxDetailsBottomSheetConfig, +) { + TangemTheme { + VisaTxDetailsBottomSheetContent(state) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun VisaTxDetailsBottomSheetPreview_Dark( + @PreviewParameter(VisaTxDetailsBottomSheetParameterProvider::class) state: VisaTxDetailsBottomSheetConfig, +) { + TangemTheme(isDark = true) { + VisaTxDetailsBottomSheetContent(state) + } +} + +private class VisaTxDetailsBottomSheetParameterProvider : + CollectionPreviewParameterProvider( + collection = listOf( + VisaTxDetailsBottomSheetConfig( + transaction = VisaTxDetailsBottomSheetConfig.Transaction( + id = "518385816101345408", + type = "payment", + status = "authorized", + blockchainAmount = "1.0614 USDT", + blockchainFee = "0.12", + transactionAmount = "0.99 €", + transactionCurrencyCode = "978", + merchantName = "SQ *FORMATIVE", + merchantCity = "London", + merchantCountryCode = "GB", + merchantCategoryCode = "5814", + ), + requests = persistentListOf( + VisaTxDetailsBottomSheetConfig.Request( + id = "524582128501966718", + type = "authorize_payment", + status = "accepted", + blockchainAmount = "1.0593 USDT", + blockchainFee = "0.10", + transactionAmount = "0.99 €", + currencyCode = "978", + errorCode = 0, + date = "2023-12-01 14:20:09.230 +0300", + txHash = "0xc458f0204fe43b82c775004baabb38435b5595f4307d8c3ac74625c827be7c29", + txStatus = "confirmed", + onExploreClick = {}, + ), + VisaTxDetailsBottomSheetConfig.Request( + id = "524582128501966799", + type = "settlement", + status = "accepted", + blockchainAmount = "1.0614 USDT", + blockchainFee = "0.12", + transactionAmount = "0.99 €", + currencyCode = "978", + errorCode = 0, + date = "2023-12-01 00:01:00.000 +0300", + txHash = "0x635841d5fbdf1087cdd929019c863ee88a7165e4340bc17ddd0b1d04dfb11daa", + txStatus = "confirmed", + onExploreClick = {}, + ), + ), + ), + ), + ) + +// endregion Preview \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt index f2f28ea189..b857c555b4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt @@ -7,9 +7,11 @@ import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModel import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.visa.GetVisaCurrencyUseCase +import com.tangem.domain.visa.GetVisaTxDetailsUseCase import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.BalancesAndLimitsBottomSheetConverter +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.VisaTxDetailsBottomSheetConverter import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped @@ -25,6 +27,8 @@ internal interface VisaWalletIntents { fun onBalancesAndLimitsClick() fun onVisaTransactionClick(id: String) + + fun onExploreClick(exploreUrl: String) } @ViewModelScoped @@ -33,6 +37,7 @@ internal class VisaWalletIntentsImplementor @Inject constructor( private val eventSender: WalletEventSender, private val getCurrencyStatusUseCase: GetCryptoCurrencyStatusSyncUseCase, private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase, + private val getVisaTxDetailsUseCase: GetVisaTxDetailsUseCase, private val dispatchers: CoroutineDispatcherProvider, ) : BaseWalletClickIntents(), VisaWalletIntents { @@ -97,6 +102,29 @@ internal class VisaWalletIntentsImplementor @Inject constructor( } override fun onVisaTransactionClick(id: String) { - // TODO: Implement [REDACTED_JIRA] + viewModelScope.launch(dispatchers.main) { + val userWalletId = stateController.getSelectedWalletId() + val visaCurrency = getVisaCurrencyUseCase(userWalletId) + .getOrElse { + Timber.e(it, "Failed to get visa currency") + return@launch + } + val transactionDetails = getVisaTxDetailsUseCase(userWalletId, id) + .getOrElse { + Timber.e(it, "Failed to get transaction details") + return@launch + } + + val converter = VisaTxDetailsBottomSheetConverter( + visaCurrency, + clickIntents = this@VisaWalletIntentsImplementor, + ) + + stateController.showBottomSheet(content = converter.convert(transactionDetails)) + } + } + + override fun onExploreClick(exploreUrl: String) { + router.openUrl(exploreUrl) } } \ No newline at end of file