Updated on 2026-08-14
This commit is contained in:
parent
28f550e633
commit
4754f0677d
28 changed files with 479 additions and 52 deletions
|
|
@ -16,6 +16,9 @@ dependencies {
|
|||
/** Project - API */
|
||||
api(projects.features.commonFeatures.api)
|
||||
api(projects.features.onramp.api)
|
||||
implementation(projects.features.marketing.api)
|
||||
implementation(projects.domain.marketing.models)
|
||||
implementation(projects.domain.quotes)
|
||||
|
||||
/** Project - Core */
|
||||
api(projects.core.analytics)
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ internal class AllOffersStateFactory(
|
|||
category = OnrampOfferCategoryUM.Recommended,
|
||||
advantages = mapOfferAdvantagesDTOtoUM(offer.advantages),
|
||||
paymentMethod = quote.paymentMethod,
|
||||
providerId = quote.provider.id,
|
||||
providerName = quote.provider.info.name,
|
||||
rate = formatCryptoAmount(quote.toAmount),
|
||||
diff = formatRateDiff(offer.rateDif),
|
||||
|
|
@ -185,6 +186,7 @@ internal class AllOffersStateFactory(
|
|||
category = OnrampOfferCategoryUM.Recommended,
|
||||
advantages = advantages,
|
||||
paymentMethod = quote.paymentMethod,
|
||||
providerId = quote.provider.id,
|
||||
providerName = quote.provider.info.name,
|
||||
rate = formatRequiredAmount(quote, currencyCode),
|
||||
diff = formatRateDiff(offer.rateDif),
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ private fun AllOffersContentSheetPaymentPreview() {
|
|||
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
|
||||
type = PaymentMethodType.CARD,
|
||||
),
|
||||
providerId = "simplex",
|
||||
providerName = "Simplex",
|
||||
rate = "0,0245334 BTC",
|
||||
diff = null,
|
||||
|
|
@ -219,6 +220,7 @@ private fun AllOffersContentSheetPaymentPreview() {
|
|||
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
|
||||
type = PaymentMethodType.CARD,
|
||||
),
|
||||
providerId = "simplex",
|
||||
providerName = "Simplex",
|
||||
rate = "0,00145334 BTC",
|
||||
diff = stringReference("–0.07%"),
|
||||
|
|
@ -270,6 +272,7 @@ private fun AllOffersContentSheetOffersPreview() {
|
|||
"https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
|
||||
type = PaymentMethodType.CARD,
|
||||
),
|
||||
providerId = "simplex",
|
||||
providerName = "Simplex",
|
||||
rate = "0,0245334 BTC",
|
||||
diff = null,
|
||||
|
|
@ -285,6 +288,7 @@ private fun AllOffersContentSheetOffersPreview() {
|
|||
"https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
|
||||
type = PaymentMethodType.CARD,
|
||||
),
|
||||
providerId = "simplex",
|
||||
providerName = "Simplex",
|
||||
rate = "0,00145334 BTC",
|
||||
diff = stringReference("–0.07%"),
|
||||
|
|
|
|||
|
|
@ -273,6 +273,7 @@ private fun PaymentMethodsContentPreview() {
|
|||
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
|
||||
type = PaymentMethodType.CARD,
|
||||
),
|
||||
providerId = "simplex",
|
||||
providerName = "Simplex",
|
||||
rate = "0,0245334 BTC",
|
||||
diff = null,
|
||||
|
|
@ -287,6 +288,7 @@ private fun PaymentMethodsContentPreview() {
|
|||
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
|
||||
type = PaymentMethodType.CARD,
|
||||
),
|
||||
providerId = "simplex",
|
||||
providerName = "Simplex",
|
||||
rate = "0,00145334 BTC",
|
||||
diff = stringReference("–0.07%"),
|
||||
|
|
|
|||
|
|
@ -10,9 +10,11 @@ import com.arkivanov.decompose.router.slot.childSlot
|
|||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.arkivanov.essenty.lifecycle.subscribe
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.features.marketing.api.MarketingBannerComponent
|
||||
import com.tangem.features.onramp.alloffers.AllOffersComponent
|
||||
import com.tangem.features.onramp.confirmresidency.ConfirmResidencyComponent
|
||||
import com.tangem.features.onramp.main.entity.OnrampMainBottomSheetConfig
|
||||
|
|
@ -29,10 +31,24 @@ internal class DefaultOnrampMainComponent @AssistedInject constructor(
|
|||
private val confirmResidencyComponentFactory: ConfirmResidencyComponent.Factory,
|
||||
private val selectCurrencyComponentFactory: SelectCurrencyComponent.Factory,
|
||||
private val allOffersComponentFactory: AllOffersComponent.Factory,
|
||||
private val marketingBannerComponentFactory: MarketingBannerComponent.Factory,
|
||||
) : OnrampMainComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: OnrampMainComponentModel = getOrCreateModel(params)
|
||||
|
||||
private val marketingBannerComponent: MarketingBannerComponent = marketingBannerComponentFactory.create(
|
||||
context = child("marketing_banner"),
|
||||
params = MarketingBannerComponent.Params.Standalone(
|
||||
requestFlow = model.marketingRequest,
|
||||
onDeeplinkClick = model::onMarketingBannerDeeplink,
|
||||
),
|
||||
)
|
||||
|
||||
private val linkedMarketingBannerComponent: MarketingBannerComponent = marketingBannerComponentFactory.create(
|
||||
context = child("marketing_banner_linked"),
|
||||
params = MarketingBannerComponent.Params.Linked(requestFlow = model.linkedMarketingRequest),
|
||||
)
|
||||
|
||||
init {
|
||||
lifecycle.subscribe(onStop = model::onStop)
|
||||
}
|
||||
|
|
@ -49,7 +65,12 @@ internal class DefaultOnrampMainComponent @AssistedInject constructor(
|
|||
val state by model.state.collectAsState()
|
||||
val bottomSheet by bottomSheetSlot.subscribeAsState()
|
||||
|
||||
OnrampMainScreen(modifier = modifier, state = state)
|
||||
OnrampMainScreen(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
marketingBannerComponent = marketingBannerComponent,
|
||||
linkedMarketingBannerComponent = linkedMarketingBannerComponent,
|
||||
)
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ internal data class OnrampOfferUM(
|
|||
val category: OnrampOfferCategoryUM,
|
||||
val advantages: OnrampOfferAdvantagesUM,
|
||||
val paymentMethod: OnrampPaymentMethod,
|
||||
val providerId: String,
|
||||
val providerName: String,
|
||||
val rate: String,
|
||||
val diff: TextReference?,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ internal class OnrampOffersStateFactory(
|
|||
category = category,
|
||||
advantages = advantages,
|
||||
paymentMethod = currentQuote.paymentMethod,
|
||||
providerId = currentQuote.provider.id,
|
||||
providerName = currentQuote.provider.info.name,
|
||||
rate = currentQuote.toAmount.value.format {
|
||||
crypto(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ package com.tangem.features.onramp.main.model
|
|||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.common.routing.deeplink.resolveMarketingDeeplink
|
||||
import com.tangem.common.routing.deeplink.toContextualRoute
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
|
|
@ -11,6 +14,8 @@ import com.tangem.core.decompose.ui.UiMessageSender
|
|||
import com.tangem.core.ui.components.fields.InputManager
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.marketing.models.MarketingScreen
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.onramp.*
|
||||
import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
|
||||
import com.tangem.domain.onramp.model.OnrampAvailability
|
||||
|
|
@ -18,8 +23,11 @@ import com.tangem.domain.onramp.model.OnrampCountry
|
|||
import com.tangem.domain.onramp.model.OnrampProviderWithQuote
|
||||
import com.tangem.domain.onramp.model.OnrampQuote
|
||||
import com.tangem.domain.onramp.model.error.OnrampError
|
||||
import com.tangem.domain.quotes.GetCurrencyUSDQuoteUseCase
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.marketing.api.LinkedBannerRequest
|
||||
import com.tangem.features.marketing.api.MarketingBannerRequest
|
||||
import com.tangem.features.onramp.main.OnrampMainComponent
|
||||
import com.tangem.features.onramp.main.entity.*
|
||||
import com.tangem.features.onramp.main.entity.factory.OnrampAmountButtonUMStateFactory
|
||||
|
|
@ -38,6 +46,7 @@ import com.tangem.utils.isNullOrZero
|
|||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
|
|
@ -56,6 +65,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
private val getOnrampOffersUseCase: GetOnrampOffersUseCase,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val messageSender: UiMessageSender,
|
||||
private val getCurrencyUSDQuoteUseCase: GetCurrencyUSDQuoteUseCase,
|
||||
paramsContainer: ParamsContainer,
|
||||
getWalletsUseCase: GetWalletsUseCase,
|
||||
) : Model(), OnrampIntents {
|
||||
|
|
@ -85,6 +95,68 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Expected received crypto amount, taken from the first [OnrampQuote.Data] quote's [OnrampQuote.Data.toAmount].
|
||||
* Used to derive [amountUsd][MarketingBannerRequest.amountUsd] for the marketing banner request flows below,
|
||||
* since the campaign min/max amount gating is expressed in USD while the user only enters a fiat amount here.
|
||||
*
|
||||
* Seeded with an initial `null` via [onStart] so the downstream [combine] can emit immediately on cold start
|
||||
* (before any quote is available, e.g. when the user has not entered an amount yet). Without this seed the
|
||||
* underlying quotes flow stays silent until a quote is stored, which would keep the banner requests from
|
||||
* emitting at all.
|
||||
*/
|
||||
private val expectedCryptoAmount: Flow<BigDecimal?> = getOnrampQuotesUseCase.invoke()
|
||||
.map { either ->
|
||||
either.getOrNull()
|
||||
?.filterIsInstance<OnrampQuote.Data>()
|
||||
?.firstOrNull()
|
||||
?.toAmount?.value
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.onStart { emit(null) }
|
||||
|
||||
/**
|
||||
* Request flow for the standalone marketing banner.
|
||||
* [fromFiat] is the fiat currency code the user is paying in (only available once the screen is in Content state).
|
||||
* [toNetwork] is the backend network id of the target crypto currency.
|
||||
* [toContractAddress] is the contract address of the target token (empty string for coins).
|
||||
* [amountUsd] is derived from the expected received crypto amount (see [expectedCryptoAmount]) converted to USD.
|
||||
* On cold start (no quote yet, e.g. the user has not entered an amount) it is null, so the request emits
|
||||
* immediately with `amountUsd = null` and the domain shows the banner ungated; once a quote arrives the request
|
||||
* re-emits with the real USD amount so the min/max filter applies. It is also null if the target currency has no
|
||||
* USD rate, again skipping the amount filter.
|
||||
*/
|
||||
val marketingRequest: Flow<MarketingBannerRequest?> = combine(state, expectedCryptoAmount) { s, crypto ->
|
||||
val contentState = s as? OnrampMainComponentUM.Content ?: return@combine null
|
||||
MarketingBannerRequest(
|
||||
screen = MarketingScreen.Onramp(
|
||||
fromFiat = contentState.amountBlockState.currencyUM.code,
|
||||
toNetwork = params.cryptoCurrency.network.rawId,
|
||||
toContractAddress = (params.cryptoCurrency as? CryptoCurrency.Token)?.contractAddress.orEmpty(),
|
||||
),
|
||||
amountUsd = computeAmountUsd(crypto),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Request flow for the LINKED_TO_PROVIDER marketing banner shown inline next to onramp provider offers.
|
||||
* Carries no provider id: provider matching is done per offer at render time (each offer row asks for its
|
||||
* banner via [MarketingBannerComponent.LinkedContent]), mirroring iOS.
|
||||
* [amountUsd] follows the same rules as in [marketingRequest]: null on cold start (banner shown ungated) or when
|
||||
* the target currency has no USD rate, and the real USD amount once a quote is available.
|
||||
*/
|
||||
val linkedMarketingRequest: Flow<LinkedBannerRequest?> = combine(state, expectedCryptoAmount) { s, crypto ->
|
||||
val contentState = s as? OnrampMainComponentUM.Content ?: return@combine null
|
||||
LinkedBannerRequest(
|
||||
screen = MarketingScreen.Onramp(
|
||||
fromFiat = contentState.amountBlockState.currencyUM.code,
|
||||
toNetwork = params.cryptoCurrency.network.rawId,
|
||||
toContractAddress = (params.cryptoCurrency as? CryptoCurrency.Token)?.contractAddress.orEmpty(),
|
||||
),
|
||||
amountUsd = computeAmountUsd(crypto),
|
||||
)
|
||||
}
|
||||
|
||||
private val amountStateFactory: OnrampAmountStateFactory by lazy(LazyThreadSafetyMode.NONE) {
|
||||
OnrampAmountStateFactory(
|
||||
currentStateProvider = Provider { state.value },
|
||||
|
|
@ -125,6 +197,16 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
super.onDestroy()
|
||||
}
|
||||
|
||||
fun onMarketingBannerDeeplink(deeplink: String): Boolean {
|
||||
val route = resolveMarketingDeeplink(deeplink).toContextualRoute(
|
||||
userWalletId = params.userWalletId,
|
||||
currency = params.cryptoCurrency,
|
||||
screenSource = AnalyticsParam.ScreensSources.Buy,
|
||||
) ?: return false
|
||||
router.push(route)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onAmountValueChanged(value: String) {
|
||||
state.update { amountStateFactory.getOnAmountValueChange(value) }
|
||||
modelScope.launch { amountInputManager.update(value) }
|
||||
|
|
@ -412,6 +494,14 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
/** Converts the expected received [crypto] amount into its USD value using the target currency's USD rate. */
|
||||
private suspend fun computeAmountUsd(crypto: BigDecimal?): BigDecimal? {
|
||||
val amount = crypto ?: return null
|
||||
val rawCurrencyId = params.cryptoCurrency.id.rawCurrencyId ?: return null
|
||||
val rate = getCurrencyUSDQuoteUseCase(rawCurrencyId) ?: return null
|
||||
return amount * rate
|
||||
}
|
||||
|
||||
private fun sendOnrampQuotesErrorAnalytic(quotes: List<OnrampQuote>) {
|
||||
quotes.forEach { errorState ->
|
||||
when (errorState) {
|
||||
|
|
|
|||
|
|
@ -17,11 +17,18 @@ import com.tangem.core.ui.components.notifications.Notification
|
|||
import com.tangem.core.ui.ds.message.TangemMessage
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemeRedesign
|
||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||
import com.tangem.features.marketing.api.MarketingBannerComponent
|
||||
import com.tangem.features.onramp.main.entity.OnrampMainComponentUM
|
||||
|
||||
@Composable
|
||||
internal fun OnrampMainScreen(state: OnrampMainComponentUM, modifier: Modifier = Modifier) {
|
||||
internal fun OnrampMainScreen(
|
||||
state: OnrampMainComponentUM,
|
||||
marketingBannerComponent: MarketingBannerComponent,
|
||||
linkedMarketingBannerComponent: MarketingBannerComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier.systemBarsPadding(),
|
||||
topBar = {
|
||||
|
|
@ -36,13 +43,20 @@ internal fun OnrampMainScreen(state: OnrampMainComponentUM, modifier: Modifier =
|
|||
) { scaffoldPaddings ->
|
||||
OnrampMainComponentContent(
|
||||
state = state,
|
||||
marketingBannerComponent = marketingBannerComponent,
|
||||
linkedMarketingBannerComponent = linkedMarketingBannerComponent,
|
||||
modifier = Modifier.padding(scaffoldPaddings),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun OnrampMainComponentContent(state: OnrampMainComponentUM, modifier: Modifier = Modifier) {
|
||||
internal fun OnrampMainComponentContent(
|
||||
state: OnrampMainComponentUM,
|
||||
marketingBannerComponent: MarketingBannerComponent,
|
||||
linkedMarketingBannerComponent: MarketingBannerComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
|
|
@ -56,7 +70,11 @@ internal fun OnrampMainComponentContent(state: OnrampMainComponentUM, modifier:
|
|||
) {
|
||||
when (state) {
|
||||
is OnrampMainComponentUM.InitialLoading -> InitialLoading(state = state)
|
||||
is OnrampMainComponentUM.Content -> Content(state = state)
|
||||
is OnrampMainComponentUM.Content -> Content(
|
||||
state = state,
|
||||
marketingBannerComponent = marketingBannerComponent,
|
||||
linkedMarketingBannerComponent = linkedMarketingBannerComponent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +136,12 @@ private fun OnrampAmountContentLoading() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(state: OnrampMainComponentUM.Content, modifier: Modifier = Modifier) {
|
||||
private fun Content(
|
||||
state: OnrampMainComponentUM.Content,
|
||||
marketingBannerComponent: MarketingBannerComponent,
|
||||
linkedMarketingBannerComponent: MarketingBannerComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -133,7 +156,14 @@ private fun Content(state: OnrampMainComponentUM.Content, modifier: Modifier = M
|
|||
) {
|
||||
OnrampAmountContent(state = state)
|
||||
|
||||
OnrampOffersContent(state = state.offersBlockState)
|
||||
TangemThemeRedesign {
|
||||
marketingBannerComponent.Content(Modifier.fillMaxWidth())
|
||||
}
|
||||
|
||||
OnrampOffersContent(
|
||||
state = state.offersBlockState,
|
||||
linkedMarketingBannerComponent = linkedMarketingBannerComponent,
|
||||
)
|
||||
|
||||
OnrampNotifications(state = state)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,15 +29,17 @@ import com.tangem.core.ui.components.buttons.common.TangemButtonSize
|
|||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemeRedesign
|
||||
import com.tangem.core.ui.test.OnrampOffersBlockTestTags
|
||||
import com.tangem.domain.onramp.model.OnrampPaymentMethod
|
||||
import com.tangem.domain.onramp.model.PaymentMethodType
|
||||
import com.tangem.features.marketing.api.MarketingBannerComponent
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.main.entity.*
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
internal fun OnrampOffersContent(state: OnrampOffersBlockUM) {
|
||||
internal fun OnrampOffersContent(state: OnrampOffersBlockUM, linkedMarketingBannerComponent: MarketingBannerComponent) {
|
||||
when (state) {
|
||||
is OnrampOffersBlockUM.Content -> {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
|
|
@ -52,7 +54,7 @@ internal fun OnrampOffersContent(state: OnrampOffersBlockUM) {
|
|||
|
||||
SpacerH(8.dp)
|
||||
|
||||
Offer(recentOffer)
|
||||
OfferWithLinkedBanner(recentOffer, linkedMarketingBannerComponent)
|
||||
|
||||
SpacerH(16.dp)
|
||||
}
|
||||
|
|
@ -74,7 +76,7 @@ internal fun OnrampOffersContent(state: OnrampOffersBlockUM) {
|
|||
|
||||
state.recommended.fastForEach { offer ->
|
||||
key("${offer.paymentMethod.id} ${offer.providerName} ${offer.rate}") {
|
||||
Offer(offer)
|
||||
OfferWithLinkedBanner(offer, linkedMarketingBannerComponent)
|
||||
SpacerH(8.dp)
|
||||
}
|
||||
}
|
||||
|
|
@ -102,13 +104,34 @@ internal fun OnrampOffersContent(state: OnrampOffersBlockUM) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
internal fun Offer(onrampOfferUM: OnrampOfferUM, modifier: Modifier = Modifier) {
|
||||
private fun OfferWithLinkedBanner(offer: OnrampOfferUM, linkedMarketingBannerComponent: MarketingBannerComponent) {
|
||||
val hasBanner = linkedMarketingBannerComponent.hasLinkedBanner(offer.providerId)
|
||||
// Square the offer's bottom corners so the bottom-rounded banner glues to it as one card.
|
||||
Offer(offer, roundBottom = !hasBanner)
|
||||
if (hasBanner) {
|
||||
TangemThemeRedesign {
|
||||
linkedMarketingBannerComponent.LinkedContent(
|
||||
providerId = offer.providerId,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun Offer(onrampOfferUM: OnrampOfferUM, modifier: Modifier = Modifier, roundBottom: Boolean = true) {
|
||||
// Square the bottom corners when a linked marketing banner is glued below, so they read as one card.
|
||||
val shape = if (roundBottom) {
|
||||
RoundedCornerShape(14.dp)
|
||||
} else {
|
||||
RoundedCornerShape(topStart = 14.dp, topEnd = 14.dp)
|
||||
}
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
shape = shape,
|
||||
)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
|
|
@ -386,6 +409,7 @@ private fun OnrampOffersContentPreview() {
|
|||
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
|
||||
type = PaymentMethodType.CARD,
|
||||
),
|
||||
providerId = "simplex",
|
||||
providerName = "Simplex",
|
||||
rate = "0,00045334 BTC",
|
||||
diff = stringReference("–27%"),
|
||||
|
|
@ -401,6 +425,7 @@ private fun OnrampOffersContentPreview() {
|
|||
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
|
||||
type = PaymentMethodType.CARD,
|
||||
),
|
||||
providerId = "simplex",
|
||||
providerName = "Simplex",
|
||||
rate = "0,0245334 BTC",
|
||||
diff = null,
|
||||
|
|
@ -415,6 +440,7 @@ private fun OnrampOffersContentPreview() {
|
|||
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
|
||||
type = PaymentMethodType.CARD,
|
||||
),
|
||||
providerId = "simplex",
|
||||
providerName = "Simplex",
|
||||
rate = "0,00145334 BTC",
|
||||
diff = stringReference("–0.07%"),
|
||||
|
|
@ -427,7 +453,10 @@ private fun OnrampOffersContentPreview() {
|
|||
),
|
||||
)
|
||||
TangemThemePreview {
|
||||
OnrampOffersContent(state)
|
||||
OnrampOffersContent(state = state, linkedMarketingBannerComponent = object : MarketingBannerComponent {
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) = Unit
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -436,6 +465,12 @@ private fun OnrampOffersContentPreview() {
|
|||
@Composable
|
||||
private fun OnrampOffersLoadingPreview() {
|
||||
TangemThemePreview {
|
||||
OnrampOffersContent(OnrampOffersBlockUM.Loading)
|
||||
OnrampOffersContent(
|
||||
state = OnrampOffersBlockUM.Loading,
|
||||
linkedMarketingBannerComponent = object : MarketingBannerComponent {
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) = Unit
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue