Updated on 2026-08-14
This commit is contained in:
parent
85158f31dc
commit
e99ffe39f0
28 changed files with 480 additions and 52 deletions
|
|
@ -3,14 +3,15 @@ package com.tangem.domain.marketing.models
|
|||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* USD min/max eligibility gate. Applies only to swap/onramp campaigns and only when [amountUsd] is known;
|
||||
* otherwise the campaign passes (non-amount screens and the "amount unknown" case are not gated).
|
||||
* USD min/max eligibility gate (mirrors iOS `satisfiesAmount`). A campaign without min/max bounds is
|
||||
* always eligible. A bounded campaign requires a known [amountUsd] — while the amount is unknown the
|
||||
* campaign is NOT eligible (hidden until a quote/amount arrives), then it must fall within the bounds.
|
||||
*/
|
||||
fun MarketingCampaign.matchesUsdAmount(amountUsd: BigDecimal?): Boolean {
|
||||
val isAmountScreen = type == MarketingScreenType.SWAP || type == MarketingScreenType.ONRAMP
|
||||
if (!isAmountScreen || amountUsd == null) return true
|
||||
if (minAmount == null && maxAmount == null) return true
|
||||
|
||||
if (minAmount != null && amountUsd < minAmount) return false
|
||||
if (maxAmount != null && amountUsd > maxAmount) return false
|
||||
val usd = amountUsd ?: return false
|
||||
if (minAmount != null && usd < minAmount) return false
|
||||
if (maxAmount != null && usd > maxAmount) return false
|
||||
return true
|
||||
}
|
||||
|
|
@ -21,16 +21,24 @@ internal class MarketingCampaignAmountTest {
|
|||
)
|
||||
|
||||
@Test
|
||||
fun `GIVEN non swap-onramp type WHEN matchesUsdAmount THEN always true`() {
|
||||
val c = campaign(MarketingScreenType.TOKEN_DETAILS, minAmount = BigDecimal(50), maxAmount = BigDecimal(300))
|
||||
fun `GIVEN no min max bounds WHEN matchesUsdAmount THEN always true`() {
|
||||
val c = campaign(MarketingScreenType.TOKEN_DETAILS, minAmount = null, maxAmount = null)
|
||||
assertThat(c.matchesUsdAmount(BigDecimal(10))).isTrue()
|
||||
assertThat(c.matchesUsdAmount(null)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN swap with null amount WHEN matchesUsdAmount THEN true`() {
|
||||
fun `GIVEN bounded campaign of any type WHEN amount out of range THEN false`() {
|
||||
// Bounds apply regardless of screen type (iOS parity): type no longer exempts a bounded campaign.
|
||||
val c = campaign(MarketingScreenType.TOKEN_DETAILS, minAmount = BigDecimal(50), maxAmount = BigDecimal(300))
|
||||
assertThat(c.matchesUsdAmount(BigDecimal(10))).isFalse()
|
||||
assertThat(c.matchesUsdAmount(BigDecimal(100))).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN bounded campaign with null amount WHEN matchesUsdAmount THEN false`() {
|
||||
val c = campaign(MarketingScreenType.SWAP, minAmount = BigDecimal(50))
|
||||
assertThat(c.matchesUsdAmount(null)).isTrue()
|
||||
assertThat(c.matchesUsdAmount(null)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ class GetMarketingBannerUseCase(
|
|||
campaigns.asSequence()
|
||||
.filterNot { it.id in dismissed }
|
||||
.filter { matchesTarget(it, screen) }
|
||||
.filter { it.matchesUsdAmount(amountUsd) }
|
||||
// Amount gating runs reactively in the consumer (with the live amount). Skip it here when
|
||||
// no amount is provided, so bounded swap/onramp campaigns aren't dropped on the pre-fetch.
|
||||
.filter { amountUsd == null || it.matchesUsdAmount(amountUsd) }
|
||||
.sortedBy { it.priority }
|
||||
.toList()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue