diff --git a/app/src/main/java/com/tangem/tap/common/log/TangemLoggingInitializer.kt b/app/src/main/java/com/tangem/tap/common/log/TangemLoggingInitializer.kt index a2496c3b32..a103b11bb0 100644 --- a/app/src/main/java/com/tangem/tap/common/log/TangemLoggingInitializer.kt +++ b/app/src/main/java/com/tangem/tap/common/log/TangemLoggingInitializer.kt @@ -98,7 +98,7 @@ class TangemLoggingInitializer( BlockchainSdkConfig.serializer(), environmentConfig.blockchainSdkConfig, ) - // Drop URL-shaped values (e.g. public endpoint URLs from BlockchainSdkConfig like + // Drop URL-shaped drawable (e.g. public endpoint URLs from BlockchainSdkConfig like // kaspaSecondaryApiUrl); they are not secrets and would obscure unrelated requests in logs. val values = JsonStringValuesExtractor.extract(json) .filter { it.isNotBlank() && !it.startsWith("http", ignoreCase = true) } diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml index 1a33b59980..db7d46a133 100644 --- a/app/src/main/res/xml/provider_paths.xml +++ b/app/src/main/res/xml/provider_paths.xml @@ -1,4 +1,5 @@ + \ No newline at end of file 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 index 2dc8a4cf7a..22f48c9bef 100644 --- 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 @@ -2,9 +2,9 @@ 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.sheet.TangemBottomSheet import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet import com.tangem.core.ui.res.TangemTheme data class ExpressStatusBottomSheetConfig( @@ -18,7 +18,7 @@ fun ExpressStatusBottomSheet(config: TangemBottomSheetConfig) { containerColor = TangemTheme.colors.background.tertiary, ) { content: ExpressStatusBottomSheetConfig -> when (val state = content.value) { - is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(state) + is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(state, false) } } } \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItems.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItems.kt index 875515992a..c52bf8b905 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItems.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItems.kt @@ -3,13 +3,7 @@ package com.tangem.common.ui.expressStatus import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -22,11 +16,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import com.tangem.common.ui.R -import com.tangem.common.ui.expressStatus.state.ExpressLinkUM -import com.tangem.common.ui.expressStatus.state.ExpressStatusUM -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.common.ui.expressStatus.state.* 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 @@ -221,13 +211,17 @@ private val PreviewExpressTransactionState: ExpressTransactionStateUM = object : onDisposeExpressStatus = {}, iconState = ExpressTransactionStateIconUM.None, toAmount = stringReference("0,11441958 BTC"), + toAmountValue = "0.11441958".toBigDecimal(), toFiatAmount = null, toAmountSymbol = "BTC", toCurrencyIcon = CurrencyIconState.Loading, + toAddress = "0x", fromAmount = stringReference("100 SOL"), + fromAmountValue = "100".toBigDecimal(), fromFiatAmount = null, fromAmountSymbol = "SOL", fromCurrencyIcon = CurrencyIconState.Loading, + fromAddress = "0x", ) } // endregion \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/OnrampStatusBottomSheetContent.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/OnrampStatusBottomSheetContent.kt index 2da3bcf5a8..6099a25b61 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/OnrampStatusBottomSheetContent.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/OnrampStatusBottomSheetContent.kt @@ -9,18 +9,16 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp 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 -import com.tangem.core.ui.components.SpacerH16 -import com.tangem.core.ui.components.SpacerH24 +import com.tangem.core.ui.components.* import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme @Composable -fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM) { +fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM, isExpressShareButtonEnabled: Boolean) { Column( modifier = Modifier .padding(horizontal = TangemTheme.dimens.spacing16) @@ -70,6 +68,10 @@ fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM) { isAutoDisposable = state.activeStatus.isAutoDisposable, onClick = state.info.onDisposeExpressStatus, ) - SpacerH24() + if (isExpressShareButtonEnabled) { + SpacerH(80.dp) + } else { + SpacerH24() + } } } \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/state/ExpressTransactionStateUM.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/state/ExpressTransactionStateUM.kt index 2b7035b44d..f2c1d7ff28 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/state/ExpressTransactionStateUM.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/state/ExpressTransactionStateUM.kt @@ -1,10 +1,13 @@ package com.tangem.common.ui.expressStatus.state +import androidx.compose.runtime.Immutable 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 java.math.BigDecimal +@Immutable interface ExpressTransactionStateUM { val info: ExpressTransactionStateInfoUM @@ -35,13 +38,17 @@ data class ExpressTransactionStateInfoUM( val onDisposeExpressStatus: () -> Unit, val iconState: ExpressTransactionStateIconUM, val toAmount: TextReference, + val toAmountValue: BigDecimal, val toFiatAmount: TextReference?, val toAmountSymbol: String, val toCurrencyIcon: CurrencyIconState, + val toAddress: String, val fromAmount: TextReference, + val fromAmountValue: BigDecimal, val fromFiatAmount: TextReference?, val fromAmountSymbol: String, val fromCurrencyIcon: CurrencyIconState, + val fromAddress: String?, ) { val subtitle: TextReference get() = buildExpressStatusSubtitle(activeStatus = activeStatus, date = timestampAgoFormatted) diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json index 722e694df5..f6bd556683 100644 --- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json @@ -138,5 +138,9 @@ { "name": "TWI_83_ADDRESS_BOOK_ENABLED", "version": "undefined" + }, + { + "name": "AND_15489_EXPRESS_SHARE_BUTTON_ENABLED", + "version": "undefined" } ] diff --git a/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapFeatureToggles.kt b/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapFeatureToggles.kt index 2b975af32f..f3ea6a28c3 100644 --- a/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapFeatureToggles.kt +++ b/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapFeatureToggles.kt @@ -8,4 +8,5 @@ interface SwapFeatureToggles { val isSwapProviderFilterEnabled: Boolean val isSwapRateExperienceEnabled: Boolean val isSwapPredefinedButtonsEnabled: Boolean + val isExpressShareButtonEnabled: Boolean } \ No newline at end of file diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapFeatureToggles.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapFeatureToggles.kt index fc49fe82dc..79cfd4ab71 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapFeatureToggles.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapFeatureToggles.kt @@ -6,34 +6,45 @@ import com.tangem.features.swap.SwapFeatureToggles import javax.inject.Inject internal class DefaultSwapFeatureToggles @Inject constructor( - featureTogglesManager: FeatureTogglesManager, + private val featureTogglesManager: FeatureTogglesManager, ) : SwapFeatureToggles { - override val isYieldSwapEnabled: Boolean = featureTogglesManager.isFeatureEnabled( - toggle = FeatureToggles.TWI_1326_YIELD_MODE_SWAP_ENABLED, - ) + override val isYieldSwapEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled( + toggle = FeatureToggles.TWI_1326_YIELD_MODE_SWAP_ENABLED, + ) - override val isSwapSwitchToTransferEnabled: Boolean = featureTogglesManager.isFeatureEnabled( - toggle = FeatureToggles.AND_15207_SWAP_SWITCH_TO_TRANSFER_ENABLED, - ) + override val isSwapSwitchToTransferEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled( + toggle = FeatureToggles.AND_15207_SWAP_SWITCH_TO_TRANSFER_ENABLED, + ) - override val isSwapIntegratedApproveEnabled: Boolean = featureTogglesManager.isFeatureEnabled( - toggle = FeatureToggles.AND_15120_SWAP_INTEGRATED_APPROVE, - ) + override val isSwapIntegratedApproveEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled( + toggle = FeatureToggles.AND_15120_SWAP_INTEGRATED_APPROVE, + ) - override val isSwapAbEnabled: Boolean = featureTogglesManager.isFeatureEnabled( - toggle = FeatureToggles.SWAP_AB_ENABLED, - ) + override val isSwapAbEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled( + toggle = FeatureToggles.SWAP_AB_ENABLED, + ) - override val isSwapProviderFilterEnabled: Boolean = featureTogglesManager.isFeatureEnabled( - toggle = FeatureToggles.AND_15009_SWAP_PROVIDER_FILTER_ENABLED, - ) + override val isSwapProviderFilterEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled( + toggle = FeatureToggles.AND_15009_SWAP_PROVIDER_FILTER_ENABLED, + ) - override val isSwapRateExperienceEnabled: Boolean = featureTogglesManager.isFeatureEnabled( - toggle = FeatureToggles.AND_15103_SWAP_RATE_EXPERIENCE_ENABLED, - ) + override val isSwapRateExperienceEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled( + toggle = FeatureToggles.AND_15103_SWAP_RATE_EXPERIENCE_ENABLED, + ) - override val isSwapPredefinedButtonsEnabled: Boolean = featureTogglesManager.isFeatureEnabled( - toggle = FeatureToggles.AND_15122_SWAP_PREDEFINED_BUTTONS_ENABLED, - ) + override val isSwapPredefinedButtonsEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled( + toggle = FeatureToggles.AND_15122_SWAP_PREDEFINED_BUTTONS_ENABLED, + ) + override val isExpressShareButtonEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled( + toggle = FeatureToggles.AND_15489_EXPRESS_SHARE_BUTTON_ENABLED, + ) } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/express/PreviewEmptyExpressTransactionsComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/express/PreviewEmptyExpressTransactionsComponent.kt index 78d0ad625f..dbba5c5cf0 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/express/PreviewEmptyExpressTransactionsComponent.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/express/PreviewEmptyExpressTransactionsComponent.kt @@ -3,14 +3,7 @@ package com.tangem.features.tangempay.components.express import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier import com.tangem.common.ui.expressStatus.expressTransactionsItemsLegacy -import com.tangem.common.ui.expressStatus.state.ExpressLinkUM -import com.tangem.common.ui.expressStatus.state.ExpressStatusItemState -import com.tangem.common.ui.expressStatus.state.ExpressStatusItemUM -import com.tangem.common.ui.expressStatus.state.ExpressStatusUM -import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM -import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM -import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM -import com.tangem.common.ui.expressStatus.state.ExpressTransactionsBlockState +import com.tangem.common.ui.expressStatus.state.* import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.onramp.model.OnrampStatus @@ -122,13 +115,17 @@ internal class PreviewEmptyExpressTransactionsComponent : ExpressTransactionsCom onDisposeExpressStatus = {}, iconState = iconState, toAmount = TextReference.Str(toAmount), + toAmountValue = toAmount.toBigDecimal(), toFiatAmount = null, toAmountSymbol = toSymbol, toCurrencyIcon = CurrencyIconState.Empty(), + toAddress = "", fromAmount = TextReference.Str(fromAmount), + fromAmountValue = fromAmount.toBigDecimal(), fromFiatAmount = null, fromAmountSymbol = fromSymbol, fromCurrencyIcon = CurrencyIconState.Empty(), + fromAddress = "", ), providerName = "Preview Provider", providerImageUrl = "", 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 e2e0af401f..89fd4d64b8 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 @@ -1,5 +1,6 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory +import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.common.ui.expressStatus.state.* import com.tangem.common.ui.expressStatus.toActiveStatusText import com.tangem.common.ui.expressStatus.toIconState @@ -7,7 +8,6 @@ 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.common.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 @@ -41,6 +41,7 @@ internal class TokenDetailsOnrampTransactionStateConverter( override fun convert(value: OnrampTransaction): ExpressTransactionStateUM.OnrampUM { val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() + val statusValue = cryptoCurrencyStatus?.value val appCurrency = appCurrencyProvider() return ExpressTransactionStateUM.OnrampUM( info = ExpressTransactionStateInfoUM( @@ -63,8 +64,9 @@ internal class TokenDetailsOnrampTransactionStateConverter( toAmount = stringReference( value.toAmount.format { crypto(cryptoCurrency) }, ), + toAmountValue = value.toAmount, toFiatAmount = stringReference( - cryptoCurrencyStatus?.value?.fiatRate?.multiply(value.toAmount).format { + statusValue?.fiatRate?.multiply(value.toAmount).format { fiat( fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol, @@ -73,6 +75,7 @@ internal class TokenDetailsOnrampTransactionStateConverter( ), toAmountSymbol = cryptoCurrency.symbol, toCurrencyIcon = iconStateConverter.convert(cryptoCurrency), + toAddress = statusValue?.networkAddress?.defaultAddress?.value.orEmpty(), fromAmount = stringReference( value.fromAmount.format { fiat( @@ -81,12 +84,14 @@ internal class TokenDetailsOnrampTransactionStateConverter( ) }, ), + fromAmountValue = value.fromAmount, fromFiatAmount = null, fromAmountSymbol = value.fromCurrency.code, fromCurrencyIcon = CurrencyIconState.FiatIcon( url = value.fromCurrency.image, fallbackResId = R.drawable.ic_currency_24, ), + fromAddress = null, iconState = value.status.toIconState(), onGoToProviderClick = { url -> analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider()) 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 6cee351764..78b32deba5 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt @@ -1,11 +1,11 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory +import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.common.ui.expressStatus.state.ExpressLinkUM import com.tangem.common.ui.expressStatus.state.ExpressStatusUM import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference @@ -17,9 +17,9 @@ import com.tangem.core.ui.utils.mapFormattedDate 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.models.account.Account import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.quote.QuoteStatus -import com.tangem.domain.models.quote.mapData +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent import com.tangem.feature.swap.domain.models.domain.ExchangeStatus @@ -34,11 +34,11 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.E import com.tangem.features.tokendetails.impl.R import com.tangem.utils.Provider import com.tangem.utils.converter.Converter +import com.tangem.utils.logging.TangemLogger import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList -import com.tangem.utils.logging.TangemLogger import java.math.BigDecimal // Fixme [REDACTED_JIRA] @@ -59,7 +59,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( fun convert( savedTransactions: List, - quoteStatuses: Set, + accountStatuses: Map, ): PersistentList { val result = mutableListOf() @@ -69,23 +69,25 @@ internal class TokenDetailsSwapTransactionsStateConverter( val fromCryptoCurrency = swapTransaction.fromCryptoCurrency val toCryptoCurrencyRawId = swapTransaction.toCryptoCurrency.id.rawCurrencyId val fromCryptoCurrencyRawId = swapTransaction.fromCryptoCurrency.id.rawCurrencyId - + var fromCryptoCurrencyStatus: CryptoCurrencyStatus? = null + var toCryptoCurrencyStatus: CryptoCurrencyStatus? = null swapTransaction.transactions.forEach { transaction -> val toAmount = transaction.toCryptoAmount val fromAmount = transaction.fromCryptoAmount var toFiatAmount: BigDecimal? = null var fromFiatAmount: BigDecimal? = null - quoteStatuses.forEach { quote -> - quote.mapData { - if (quote.rawCurrencyId == toCryptoCurrencyRawId) { - toFiatAmount = fiatRate.multiply(toAmount) - } + accountStatuses.forEach { (account, cryptoCurrencyStatus) -> + if (cryptoCurrencyStatus.currency.id.rawCurrencyId == fromCryptoCurrencyRawId && + account.userWalletId.stringValue == swapTransaction.fromUserWalletId + ) { + fromFiatAmount = cryptoCurrencyStatus.value.fiatRate?.multiply(fromAmount) + fromCryptoCurrencyStatus = cryptoCurrencyStatus } - - quote.mapData { - if (quote.rawCurrencyId == fromCryptoCurrencyRawId) { - fromFiatAmount = fiatRate.multiply(fromAmount) - } + if (cryptoCurrencyStatus.currency.id.rawCurrencyId == toCryptoCurrencyRawId && + account.userWalletId.stringValue == swapTransaction.toUserWalletId + ) { + toFiatAmount = cryptoCurrencyStatus.value.fiatRate?.multiply(toAmount) + toCryptoCurrencyStatus = cryptoCurrencyStatus } } val statusModel = transaction.status @@ -111,6 +113,8 @@ internal class TokenDetailsSwapTransactionsStateConverter( transaction = transaction, toCryptoCurrency = toCryptoCurrency, fromCryptoCurrency = fromCryptoCurrency, + fromCryptoCurrencyStatus = fromCryptoCurrencyStatus, + toCryptoCurrencyStatus = toCryptoCurrencyStatus, toFiatAmount = toFiatAmount, fromFiatAmount = fromFiatAmount, ), @@ -149,14 +153,19 @@ internal class TokenDetailsSwapTransactionsStateConverter( ) } + @Suppress("LongParameterList") private fun createStateInfo( transaction: SavedSwapTransactionModel, toCryptoCurrency: CryptoCurrency, fromCryptoCurrency: CryptoCurrency, + fromCryptoCurrencyStatus: CryptoCurrencyStatus?, + toCryptoCurrencyStatus: CryptoCurrencyStatus?, toFiatAmount: BigDecimal?, fromFiatAmount: BigDecimal?, ): ExpressTransactionStateInfoUM { val timestamp = transaction.timestamp + val fromStatusValue = fromCryptoCurrencyStatus?.value + val toStatusValue = toCryptoCurrencyStatus?.value return ExpressTransactionStateInfoUM( title = resourceReference(R.string.express_exchange_by, wrappedList(transaction.provider.name)), txId = transaction.txId, @@ -169,13 +178,17 @@ internal class TokenDetailsSwapTransactionsStateConverter( timestampAgoFormatted = mapFormattedDate(timestamp), activeStatus = getActiveStatusText(transaction.status?.status), toAmount = getCryptoAmount(transaction.toCryptoAmount, toCryptoCurrency), + toAmountValue = transaction.toCryptoAmount, toFiatAmount = getFiatAmount(toFiatAmount), toCurrencyIcon = iconStateConverter.convert(toCryptoCurrency), toAmountSymbol = toCryptoCurrency.symbol, + toAddress = toStatusValue?.networkAddress?.defaultAddress?.value.orEmpty(), fromAmount = getCryptoAmount(transaction.fromCryptoAmount, fromCryptoCurrency), + fromAmountValue = transaction.fromCryptoAmount, fromFiatAmount = getFiatAmount(fromFiatAmount), fromCurrencyIcon = iconStateConverter.convert(fromCryptoCurrency), fromAmountSymbol = fromCryptoCurrency.symbol, + fromAddress = fromStatusValue?.networkAddress?.defaultAddress?.value.orEmpty(), onClick = { clickIntents.onExpressTransactionClick(transaction.txId) }, onGoToProviderClick = { url -> analyticsEventsHandler.send( 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 e2465a65bd..924b9619b7 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 @@ -8,12 +8,13 @@ import com.tangem.datasource.local.swap.SwapTransactionStatusStore import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.account.Account import com.tangem.domain.models.account.AccountId import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.quote.QuoteStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.quotes.QuotesRepository +import com.tangem.domain.pay.usecase.GetPaymentAccountCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.feature.swap.domain.SwapTransactionRepository @@ -37,8 +38,8 @@ import kotlinx.coroutines.flow.map internal class ExchangeStatusFactory @AssistedInject constructor( private val swapTransactionRepository: SwapTransactionRepository, private val swapRepository: SwapRepository, - private val quotesRepository: QuotesRepository, private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase, + private val getPaymentAccountCryptoCurrencyStatusUseCase: GetPaymentAccountCryptoCurrencyStatusUseCase, private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase, private val swapTransactionStatusStore: SwapTransactionStatusStore, private val analyticsEventsHandler: AnalyticsEventHandler, @@ -62,19 +63,23 @@ internal class ExchangeStatusFactory @AssistedInject constructor( operator fun invoke(): Flow> { return swapTransactionRepository.getTransactions( userWallet = userWallet, - cryptoCurrencyId = cryptoCurrency.id, ).conflate() .map { savedTransactions -> - val quotes = savedTransactions - ?.flatMap { setOf(it.fromCryptoCurrency.id, it.toCryptoCurrency.id) } - ?.toSet() - ?.getQuotesOrEmpty() - ?: emptySet() + val accountStatuses = savedTransactions + ?.flatMap { swapTransaction -> + setOf( + swapTransaction.fromAccount to swapTransaction.fromCryptoCurrency, + swapTransaction.toAccount to swapTransaction.toCryptoCurrency, + ) + } + ?.toMap() + ?.getStatuses() + .orEmpty() getExchangeStatusState( savedTransactions = savedTransactions, - quoteStatuses = quotes, + accountStatuses = accountStatuses, ) } } @@ -194,7 +199,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( private fun getExchangeStatusState( savedTransactions: List?, - quoteStatuses: Set, + accountStatuses: Map, ): PersistentList { if (savedTransactions == null) { return persistentListOf() @@ -202,7 +207,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( return swapTransactionsStateConverter.convert( savedTransactions = savedTransactions, - quoteStatuses = quoteStatuses, + accountStatuses = accountStatuses, ) } @@ -226,12 +231,24 @@ internal class ExchangeStatusFactory @AssistedInject constructor( } } - private suspend fun Set.getQuotesOrEmpty(): Set { - val rawIds = mapNotNull { it.rawCurrencyId }.toSet() + private suspend fun Map.getStatuses(): Map { + return mapNotNull { (account, cryptoCurrency) -> + when (account) { + is Account.CryptoPortfolio -> { + val (cryptoPortfolioAccount, currencyStatus) = getAccountCurrencyStatusUseCase.invokeSync( + userWalletId = account.userWalletId, + currency = cryptoCurrency, + ).getOrNull() ?: return@mapNotNull null - return runCatching { quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = rawIds) } - .getOrNull() - .orEmpty() + cryptoPortfolioAccount to currencyStatus + } + is Account.Payment -> getPaymentAccountCryptoCurrencyStatusUseCase.invokeSync( + userWalletId = account.userWalletId, + cryptoCurrency = cryptoCurrency, + ).getOrNull() + else -> null + } + }.toMap() } @AssistedFactory 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 84897cd1c0..087e57b6d9 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 @@ -19,6 +19,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.model.ExpressTr import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotification import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheet +import com.tangem.features.swap.SwapFeatureToggles import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.assisted.Assisted @@ -45,6 +46,7 @@ internal class ExpressStatusFactory @AssistedInject constructor( private val analyticsEventsHandler: AnalyticsEventHandler, onrampStatusFactory: OnrampStatusFactory.Factory, exchangeStatusFactory: ExchangeStatusFactory.Factory, + private val swapFeatureToggles: SwapFeatureToggles, ) { private val exchangeStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) { @@ -212,6 +214,7 @@ internal class ExpressStatusFactory @AssistedInject constructor( is ExpressStatusBottomSheetConfig -> ExpressStatusBottomSheet( config = this, extraContent = extraContent, + isExpressShareButtonEnabled = swapFeatureToggles.isExpressShareButtonEnabled, ) } } 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 f334816546..4378035f1f 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,14 +1,18 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express import android.content.res.Configuration +import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.common.ui.expressStatus.OnrampStatusBottomSheetContent -import com.tangem.common.ui.expressStatus.state.* -import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM +import com.tangem.core.ui.components.BottomFade import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM @@ -17,15 +21,44 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.e @Composable internal fun ExpressStatusBottomSheet( config: TangemBottomSheetConfig, + isExpressShareButtonEnabled: Boolean, extraContent: (@Composable () -> Unit)? = null, ) { TangemBottomSheet( config = config, containerColor = TangemTheme.colors.background.tertiary, ) { content: ExpressStatusBottomSheetConfig -> - when (val state = content.value) { - is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(state) - is ExchangeUM -> ExchangeStatusBottomSheetContent(state = state, extraContent = extraContent) + if (isExpressShareButtonEnabled) { + Box { + when (val state = content.value) { + is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent( + state = state, + isExpressShareButtonEnabled = true, + ) + is ExchangeUM -> ExchangeStatusBottomSheetContent( + state = state, + extraContent = extraContent, + isExpressShareButtonEnabled = true, + ) + } + BottomFade( + backgroundColor = TangemTheme.colors.background.tertiary, + modifier = Modifier.align(Alignment.BottomCenter), + ) + ExpressShareContent(state = content.value) + } + } else { + when (val state = content.value) { + is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent( + state = state, + isExpressShareButtonEnabled = false, + ) + is ExchangeUM -> ExchangeStatusBottomSheetContent( + state = state, + extraContent = extraContent, + isExpressShareButtonEnabled = false, + ) + } } } } @@ -43,6 +76,7 @@ private fun PreviewExpressStatusBottomSheet( onDismissRequest = {}, content = param, ), + isExpressShareButtonEnabled = false, ) } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheetStateProvider.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheetStateProvider.kt index 1a0a125cb4..cc256b1136 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheetStateProvider.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheetStateProvider.kt @@ -24,7 +24,7 @@ class ExpressStatusBottomSheetStateProvider : PreviewParameterProvider 0 && graphicsLayer.size.height > 0) { + val uri = graphicsLayer.saveAsShareableFile(context) + + val shareIntent = if (uri != null) { + Intent().apply { + action = Intent.ACTION_SEND + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // Temporary external read grant + putExtra(Intent.EXTRA_TEXT, shareText) + putExtra(Intent.EXTRA_STREAM, uri) + clipData = ClipData.newRawUri(null, uri) + + type = "image/png" + } + } else { + Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_TEXT, shareText) + type = "text/plain" + } + } + context.startActivity(Intent.createChooser(shareIntent, null)) + } + } + }, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + .align(Alignment.BottomCenter), + ) + ExpressShareImageContent( + state = state, + onGraphicsLayer = { layer -> graphicsLayer = layer }, + modifier = Modifier.size(0.dp), // size 0 so that no space is used in the UI + ) + } +} + +@Composable +private fun ExpressShareImageContent( + state: ExpressTransactionStateUM, + onGraphicsLayer: (GraphicsLayer) -> Unit, + modifier: Modifier = Modifier, +) { + Box(modifier = modifier.drawForShare(onGraphicsLayer)) { + Box( + // override the parent size with desired size of the recording + modifier = Modifier + .wrapContentHeight(unbounded = true, align = Alignment.Top) + .wrapContentWidth(unbounded = true, align = Alignment.Start) + .requiredSize(525.dp, 580.dp), + ) { + Column( + modifier = Modifier + .matchParentSize() + .background(TangemColorPalette.Black), + ) { + Column( + modifier = Modifier.padding( + start = 40.dp, + end = 40.dp, + top = 40.dp, + bottom = 20.dp, + ), + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.img_tangem_logo_90_24), + contentDescription = null, + tint = TangemColorPalette.White, + ) + SpacerH(36.dp) + + ExpressShareImageAmount( + prefix = stringResourceSafe(R.string.common_send), + amountValue = state.info.fromAmountValue, + currencyIconState = state.info.fromCurrencyIcon, + currencySymbol = state.info.fromAmountSymbol, + ) + ExpressShareImageAddress( + prefix = stringResourceSafe(R.string.common_from), + address = state.info.fromAddress, + ) + + SpacerH(24.dp) + ExpressShareImageSeparator() + SpacerH(24.dp) + + ExpressShareImageAmount( + prefix = stringResourceSafe(R.string.common_receive), + amountValue = state.info.toAmountValue, + currencyIconState = state.info.toCurrencyIcon, + currencySymbol = state.info.toAmountSymbol, + ) + ExpressShareImageAddress( + prefix = stringResourceSafe(R.string.common_to), + address = state.info.toAddress, + ) + SpacerH(24.dp) + + ExpressShareImageProvider(state) + Text( + text = stringResourceSafe(R.string.express_transaction_id, state.info.txExternalId.orEmpty()), + style = TangemTheme.typography2.bodySemibold16, + color = TangemTheme.colors3.text.staticDark.secondary, + ) + } + Image( + painter = painterResource(R.drawable.img_share_express_background), + contentDescription = null, + modifier = Modifier, + contentScale = ContentScale.Crop, + ) + } + } + } +} + +@Composable +private fun ExpressShareImageAmount( + prefix: String, + amountValue: BigDecimal, + currencyIconState: CurrencyIconState, + currencySymbol: String, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = prefix, + color = TangemTheme.colors3.text.staticDark.primary, + style = TangemTheme.typography2.bodySemibold16, + ) + Text( + text = amountValue.stripZeroPlainString(), + color = TangemTheme.colors3.text.staticDark.primary, + style = TangemTheme.typography2.bodySemibold16, + ) + CurrencyIcon( + state = currencyIconState, + iconSize = 20.dp, + shouldDisplayNetwork = false, + ) + Text( + text = currencySymbol, + color = TangemTheme.colors3.text.staticDark.primary, + style = TangemTheme.typography2.bodySemibold16, + ) + } +} + +@Composable +private fun ExpressShareImageAddress(prefix: String, address: String?) { + if (address != null) { + Text( + text = "$prefix:", + color = TangemTheme.colors3.text.staticDark.secondary, + ) + Text( + text = address, + color = TangemTheme.colors3.text.staticDark.secondary, + ) + } +} + +@Composable +private fun ExpressShareImageSeparator() { + val width = 1.dp + val height = 39.dp + val dashOnInterval = (width * 2).toPx() + val dashOffInterval = (width * 2).toPx() + + val pathEffect = PathEffect.dashPathEffect( + intervals = floatArrayOf(dashOnInterval, dashOffInterval), + ) + Canvas(modifier = Modifier.size(width = width, height = height)) { + drawLine( + color = TangemColorPalette.Dark2, + start = Offset(0f, 0f), + end = Offset(size.width, size.height), + strokeWidth = width.toPx(), + cap = StrokeCap.Round, + pathEffect = pathEffect, + ) + } +} + +@Composable +private fun ExpressShareImageProvider(state: ExpressTransactionStateUM) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = stringResourceSafe(R.string.express_by_provider), + style = TangemTheme.typography2.bodySemibold16, + color = TangemTheme.colors3.text.staticDark.primary, + ) + val providerImageUrl = when (state) { + is ExchangeUM -> state.provider.imageLarge + is ExpressTransactionStateUM.OnrampUM -> state.providerImageUrl + else -> null + } + val providerName = when (state) { + is ExchangeUM -> state.provider.name + is ExpressTransactionStateUM.OnrampUM -> state.providerName + else -> null + } + val providerType = when (state) { + is ExchangeUM -> state.provider.type.providerName + is ExpressTransactionStateUM.OnrampUM -> state.providerType + else -> null + } + if (providerImageUrl != null) { + TangemIcon( + tangemIconUM = TangemIconUM.Url(providerImageUrl, fallbackRes = R.drawable.ic_empty_64), + modifier = Modifier.size(20.dp), + ) + } + Text( + text = providerName.orEmpty(), + style = TangemTheme.typography2.bodySemibold16, + color = TangemTheme.colors3.text.staticDark.primary, + ) + Text( + text = providerType.orEmpty(), + style = TangemTheme.typography2.bodySemibold16, + color = TangemTheme.colors3.text.staticDark.secondary, + ) + } +} + +@Composable +private fun Modifier.drawForShare(onGraphicsLayer: (GraphicsLayer) -> Unit): Modifier { + val isInspectionMode = LocalInspectionMode.current + + return drawWithCache { + // draw to graphics layer + onGraphicsLayer( + obtainGraphicsLayer().apply { + record( + size = IntSize( + width = 525.dp.toPx().toInt(), + height = 580.dp.toPx().toInt(), + ), + ) { + drawContent() + } + }, + ) + + if (isInspectionMode) { + // draw only for preview mode + onDrawWithContent { drawContent() } + } else { + // leave blank to skip drawing on the screen + onDrawWithContent { } + } + } +} + +@Suppress("MagicNumber") +private suspend fun GraphicsLayer.saveAsShareableFile(context: Context): Uri? { + // convert to bitmap + val bitmap = this.toImageBitmap().asAndroidBitmap() + + // create file + val cachePath = File(context.cacheDir, "images") + cachePath.mkdir() + + val file = File(cachePath, "shared_image.png") + + // write bitmap to file as PNG + file.outputStream().use { out -> + bitmap.compress(/* format = */ Bitmap.CompressFormat.PNG, /* quality = */ 100, /* stream = */ out) + out.flush() + } + + // Generate secure Content URI using the registered authority + return FileProvider.getUriForFile( + /* context = */ context, + /* authority = */ "${context.packageName}.provider", + /* file = */ file, + ) +} + +@ReadOnlyComposable +@Composable +internal fun makeExpressShareContent(state: ExpressTransactionStateUM): String? { + val txExternalId = state.info.txExternalId + val txId = if (txExternalId != null) { + stringResourceSafe(R.string.express_transaction_id, txExternalId) + } else { + "" + } + + val (providerName, providerType) = when (state) { + is ExchangeUM -> state.provider.name to state.provider.type + is ExpressTransactionStateUM.OnrampUM -> state.providerName to state.providerType + else -> return null + } + val providerInfo = "${stringResourceSafe(R.string.express_by_provider)} $providerName $providerType" + val fromAddress = if (state.info.fromAddress != null) { + "${stringResourceSafe(R.string.common_from)}: ${state.info.fromAddress}" + } else { + "" + } + val text = """ + ${stringResourceSafe(R.string.common_tangem)} + + ${stringResourceSafe(R.string.common_send)} ${state.info.fromAmount.resolveReference()} + $fromAddress + + ${stringResourceSafe(R.string.common_receive)} ${state.info.toAmount.resolveReference()} + ${stringResourceSafe(R.string.common_to)}: ${state.info.toAddress} + + $providerInfo + $txId + """.trimIndent() + + return text +} + +// region Preview +@Composable +@Preview(showBackground = true, widthDp = 360, device = Devices.PIXEL_7_PRO) +@Preview(showBackground = true, widthDp = 360, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun ExpressShareImageContent_Preview( + @PreviewParameter(ExpressStatusBottomSheetStateProvider::class) param: ExpressStatusBottomSheetConfig, +) { + TangemThemePreviewRedesign { + ExpressShareImageContent(param.value, {}, modifier = Modifier) + } +} + +// endregion \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheetContent.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/exchange/ExchangeStatusBottomSheetContent.kt index 6ce01c5bd9..3f4557962e 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 @@ -10,10 +10,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp import com.tangem.common.ui.expressStatus.ExpressEstimate import com.tangem.common.ui.expressStatus.ExpressHideButton import com.tangem.common.ui.expressStatus.ExpressProvider import com.tangem.core.ui.R +import com.tangem.core.ui.components.SpacerH import com.tangem.core.ui.components.SpacerH10 import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.components.SpacerH16 @@ -28,7 +30,11 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.component import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM @Composable -internal fun ExchangeStatusBottomSheetContent(state: ExchangeUM, extraContent: (@Composable () -> Unit)? = null) { +internal fun ExchangeStatusBottomSheetContent( + state: ExchangeUM, + isExpressShareButtonEnabled: Boolean, + extraContent: (@Composable () -> Unit)? = null, +) { Column( modifier = Modifier .padding(horizontal = TangemTheme.dimens.spacing16) @@ -87,7 +93,11 @@ internal fun ExchangeStatusBottomSheetContent(state: ExchangeUM, extraContent: ( isAutoDisposable = state.activeStatus?.isAutoDisposable == true, onClick = state.info.onDisposeExpressStatus, ) - SpacerH24() + if (isExpressShareButtonEnabled) { + SpacerH(80.dp) + } else { + SpacerH24() + } } } diff --git a/features/tokendetails/impl/src/main/res/drawable/img_share_express_background.webp b/features/tokendetails/impl/src/main/res/drawable/img_share_express_background.webp new file mode 100644 index 0000000000..5fafc633ee Binary files /dev/null and b/features/tokendetails/impl/src/main/res/drawable/img_share_express_background.webp differ 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 index 597e1b1837..727cfd9ca5 100644 --- 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 @@ -63,6 +63,7 @@ internal class SingleWalletOnrampTransactionConverter( timestampAgoFormatted = mapFormattedDate(value.timestamp), activeStatus = value.status.toActiveStatusText(currency.name), toAmount = stringReference(value.toAmount.format { crypto(currency) }), + toAmountValue = value.toAmount, toFiatAmount = stringReference( status.fiatRate?.multiply(value.toAmount).format { fiat( @@ -73,6 +74,7 @@ internal class SingleWalletOnrampTransactionConverter( ), toAmountSymbol = currency.symbol, toCurrencyIcon = iconStateConverter.convert(currency), + toAddress = status.networkAddress?.defaultAddress?.value.orEmpty(), fromAmount = stringReference( value.fromAmount.format { fiat( @@ -81,12 +83,14 @@ internal class SingleWalletOnrampTransactionConverter( ) }, ), + fromAmountValue = value.fromAmount, fromFiatAmount = null, fromAmountSymbol = value.fromCurrency.code, fromCurrencyIcon = CurrencyIconState.FiatIcon( url = value.fromCurrency.image, fallbackResId = R.drawable.ic_currency_24, ), + fromAddress = null, iconState = value.status.toIconState(), onGoToProviderClick = { url -> analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider())