Updated on 2026-08-14
This commit is contained in:
parent
7cecc3f90c
commit
0f1a926cc4
5 changed files with 200 additions and 1 deletions
|
|
@ -143,6 +143,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
|||
is WalletNotificationUM.CloreMigration,
|
||||
is WalletNotificationUM.TangemPayRefreshNeeded,
|
||||
WalletNotificationUM.TangemPayUnreachable,
|
||||
is WalletNotificationUM.YieldBoostPromo,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,11 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.domain.yield.supply.promo.usecase.ShouldShowYieldBoostMainBannerUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetShouldShowMainPromoUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificationUM
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
|
||||
import com.tangem.utils.extensions.addIf
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -28,6 +31,9 @@ internal class GetWalletNotificationsCarouselFactory @Inject constructor(
|
|||
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
private val notificationsRepository: NotificationsRepository,
|
||||
private val shouldShowYieldBoostMainBannerUseCase: ShouldShowYieldBoostMainBannerUseCase,
|
||||
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
|
||||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
|
||||
) {
|
||||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotificationUM>> {
|
||||
return combine(
|
||||
|
|
@ -36,7 +42,8 @@ internal class GetWalletNotificationsCarouselFactory @Inject constructor(
|
|||
).distinctUntilChanged(),
|
||||
flow2 = isReadyToShowRateAppUseCase().distinctUntilChanged(),
|
||||
flow3 = getWalletsUseCase().conflate(),
|
||||
) { showPushesNotification, showRateAppPromo, wallets ->
|
||||
flow4 = yieldSupplyGetShouldShowMainPromoUseCase().distinctUntilChanged(),
|
||||
) { showPushesNotification, showRateAppPromo, wallets, shouldShowYieldPromoLocal ->
|
||||
|
||||
buildList {
|
||||
addNoteMigrationNotification(userWallet, wallets, clickIntents)
|
||||
|
|
@ -47,10 +54,33 @@ internal class GetWalletNotificationsCarouselFactory @Inject constructor(
|
|||
isPushesAllowed = notificationsRepository.isUserAllowToSubscribeOnPushNotifications(),
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
addYieldBoostBannerNotification(
|
||||
userWallet = userWallet,
|
||||
shouldShowLocal = shouldShowYieldPromoLocal,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
}.sortedBy { it.type.ordinal }.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun MutableList<WalletNotificationUM>.addYieldBoostBannerNotification(
|
||||
userWallet: UserWallet,
|
||||
shouldShowLocal: Boolean,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
if (!shouldShowLocal) return
|
||||
if (!yieldSupplyFeatureToggles.isYieldPromoEnabled) return
|
||||
val shouldShow = shouldShowYieldBoostMainBannerUseCase(userWallet.walletId).getOrNull() == true
|
||||
if (!shouldShow) return
|
||||
add(
|
||||
WalletNotificationUM.YieldBoostPromo(
|
||||
onExploreClick = { clickIntents.onYieldBoostBannerClick(userWallet.walletId) },
|
||||
onLaterClick = { clickIntents.onDismissYieldBoostBanner(userWallet.walletId) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addRateAppNotification(
|
||||
isReadyToShowRating: Boolean,
|
||||
clickIntents: WalletClickIntents,
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ import com.tangem.core.ui.ds.message.TangemMessageButtonUM
|
|||
import com.tangem.core.ui.ds.message.TangemMessageEffect
|
||||
import com.tangem.core.ui.ds.message.TangemMessageUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.pluralReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
|
@ -412,6 +414,36 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
|
|||
type = WalletNotificationType.Promo,
|
||||
)
|
||||
|
||||
data class YieldBoostPromo(
|
||||
val onExploreClick: () -> Unit,
|
||||
val onLaterClick: () -> Unit,
|
||||
) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "YieldBoostPromoNotification",
|
||||
title = combinedReference(
|
||||
resourceReference(CoreResR.string.yield_apy_boost_banner_title),
|
||||
stringReference(" · "),
|
||||
resourceReference(CoreResR.string.yield_apy_boost_banner_title_apy_multiplied),
|
||||
),
|
||||
subtitle = resourceReference(CoreResR.string.yield_apy_boost_banner_subtitle),
|
||||
iconUM = TangemIconUM.Image(imageRes = CoreUiR.drawable.ic_yield_32),
|
||||
messageEffect = TangemMessageEffect.Magic,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(CoreResR.string.common_later),
|
||||
type = TangemButtonType.Secondary,
|
||||
onClick = onLaterClick,
|
||||
),
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(CoreResR.string.yield_apy_boost_banner_button_title),
|
||||
type = TangemButtonType.Primary,
|
||||
onClick = onExploreClick,
|
||||
),
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Promo,
|
||||
)
|
||||
|
||||
// endregion
|
||||
|
||||
// region Survey
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import arrow.core.Either
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.domain.yield.supply.promo.usecase.ShouldShowYieldBoostMainBannerUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetShouldShowMainPromoUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificationUM
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.MethodSource
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class GetWalletNotificationsCarouselFactoryTest {
|
||||
|
||||
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase = mockk()
|
||||
private val getWalletsUseCase: GetWalletsUseCase = mockk()
|
||||
private val notificationsRepository: NotificationsRepository = mockk()
|
||||
private val shouldShowYieldBoostMainBannerUseCase: ShouldShowYieldBoostMainBannerUseCase = mockk()
|
||||
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase = mockk()
|
||||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles = mockk()
|
||||
private val clickIntents: WalletClickIntents = mockk(relaxed = true)
|
||||
private val userWallet: UserWallet.Hot = mockk(relaxed = true)
|
||||
|
||||
private val factory = GetWalletNotificationsCarouselFactory(
|
||||
isReadyToShowRateAppUseCase = isReadyToShowRateAppUseCase,
|
||||
getWalletsUseCase = getWalletsUseCase,
|
||||
notificationsRepository = notificationsRepository,
|
||||
shouldShowYieldBoostMainBannerUseCase = shouldShowYieldBoostMainBannerUseCase,
|
||||
yieldSupplyGetShouldShowMainPromoUseCase = yieldSupplyGetShouldShowMainPromoUseCase,
|
||||
yieldSupplyFeatureToggles = yieldSupplyFeatureToggles,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
clearMocks(
|
||||
isReadyToShowRateAppUseCase,
|
||||
getWalletsUseCase,
|
||||
notificationsRepository,
|
||||
shouldShowYieldBoostMainBannerUseCase,
|
||||
yieldSupplyGetShouldShowMainPromoUseCase,
|
||||
yieldSupplyFeatureToggles,
|
||||
clickIntents,
|
||||
userWallet,
|
||||
)
|
||||
// Defaults: only the yield boost banner can appear; every other notification is suppressed.
|
||||
every { userWallet.walletId } returns WALLET_ID
|
||||
every { notificationsRepository.getShouldShowNotification(any()) } returns flowOf(false)
|
||||
coEvery { notificationsRepository.isUserAllowToSubscribeOnPushNotifications() } returns true
|
||||
every { isReadyToShowRateAppUseCase() } returns flowOf(false)
|
||||
every { getWalletsUseCase() } returns flowOf(emptyList())
|
||||
every { yieldSupplyGetShouldShowMainPromoUseCase() } returns flowOf(true)
|
||||
every { yieldSupplyFeatureToggles.isYieldPromoEnabled } returns true
|
||||
coEvery { shouldShowYieldBoostMainBannerUseCase(any()) } returns Either.Right(true)
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestModels")
|
||||
fun `GIVEN gating conditions WHEN create THEN yield boost banner visibility matches`(model: Model) = runTest {
|
||||
// Arrange
|
||||
every { yieldSupplyFeatureToggles.isYieldPromoEnabled } returns model.toggleEnabled
|
||||
every { yieldSupplyGetShouldShowMainPromoUseCase() } returns flowOf(model.shouldShowLocal)
|
||||
coEvery { shouldShowYieldBoostMainBannerUseCase(WALLET_ID) } returns model.mainBanner
|
||||
|
||||
// Act
|
||||
val result = factory.create(userWallet, clickIntents).first()
|
||||
|
||||
// Assert
|
||||
assertThat(result.any { it is WalletNotificationUM.YieldBoostPromo }).isEqualTo(model.expectedShown)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN banner shown WHEN buttons clicked THEN routes to click intents`() = runTest {
|
||||
// Arrange
|
||||
val banner = factory.create(userWallet, clickIntents).first()
|
||||
.filterIsInstance<WalletNotificationUM.YieldBoostPromo>()
|
||||
.first()
|
||||
|
||||
// Act
|
||||
banner.onExploreClick()
|
||||
banner.onLaterClick()
|
||||
|
||||
// Assert
|
||||
verify { clickIntents.onYieldBoostBannerClick(WALLET_ID) }
|
||||
verify { clickIntents.onDismissYieldBoostBanner(WALLET_ID) }
|
||||
}
|
||||
|
||||
internal data class Model(
|
||||
val toggleEnabled: Boolean,
|
||||
val shouldShowLocal: Boolean,
|
||||
val mainBanner: Either<Throwable, Boolean>,
|
||||
val expectedShown: Boolean,
|
||||
)
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
Model(toggleEnabled = true, shouldShowLocal = true, mainBanner = Either.Right(true), expectedShown = true),
|
||||
Model(toggleEnabled = false, shouldShowLocal = true, mainBanner = Either.Right(true), expectedShown = false),
|
||||
Model(toggleEnabled = true, shouldShowLocal = false, mainBanner = Either.Right(true), expectedShown = false),
|
||||
Model(toggleEnabled = true, shouldShowLocal = true, mainBanner = Either.Right(false), expectedShown = false),
|
||||
Model(
|
||||
toggleEnabled = true,
|
||||
shouldShowLocal = true,
|
||||
mainBanner = Either.Left(RuntimeException("boom")),
|
||||
expectedShown = false,
|
||||
),
|
||||
)
|
||||
|
||||
private companion object {
|
||||
val WALLET_ID = UserWalletId("01")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue