From 4dc85ef2dca9ebe3da831ebc4bbea9d51a3b81d6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 13 Jul 2026 21:00:48 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../WalletNotificationsSubscriber.kt | 13 ++ .../WalletNotificationsSubscriberTest.kt | 123 ++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNotificationsSubscriberTest.kt diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNotificationsSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNotificationsSubscriber.kt index 57f51e06d1..fe23724ee9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNotificationsSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNotificationsSubscriber.kt @@ -1,6 +1,8 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.stories.GetStoryContentUseCase +import com.tangem.domain.stories.models.StoryContentIds import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender @@ -17,6 +19,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch @Suppress("LongParameterList") internal class WalletNotificationsSubscriber @AssistedInject constructor( @@ -27,6 +30,7 @@ internal class WalletNotificationsSubscriber @AssistedInject constructor( private val getWalletNotificationsCarouselFactory: GetWalletNotificationsCarouselFactory, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender, + private val getStoryContentUseCase: GetStoryContentUseCase, ) : WalletSubscriber() { override fun create(coroutineScope: CoroutineScope): Flow> { @@ -35,6 +39,15 @@ internal class WalletNotificationsSubscriber @AssistedInject constructor( flow2 = getWalletNotificationsCarouselFactory.create(userWallet, clickIntents).conflate() .distinctUntilChanged(), ) { notifications, notificationsCarousel -> + if (notificationsCarousel.any { it is WalletNotificationUM.YieldBoostPromo }) { + coroutineScope.launch { + getStoryContentUseCase.invokeSync( + id = StoryContentIds.STORY_FIRST_TIME_YIELD_PROMO.id, + refresh = true, + ) + } + } + val displayedWalletUM = stateHolder.getWalletUM(userWallet.walletId) // Wait until the wallet appears in the list diff --git a/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNotificationsSubscriberTest.kt b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNotificationsSubscriberTest.kt new file mode 100644 index 0000000000..e503f4691e --- /dev/null +++ b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNotificationsSubscriberTest.kt @@ -0,0 +1,123 @@ +package com.tangem.feature.wallet.presentation.wallet.subscribers + +import arrow.core.right +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.stories.GetStoryContentUseCase +import com.tangem.domain.stories.models.StoryContentIds +import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents +import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender +import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender +import com.tangem.feature.wallet.presentation.wallet.domain.GetWalletNotificationsCarouselFactory +import com.tangem.feature.wallet.presentation.wallet.domain.GetWalletNotificationsFactory +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificationUM +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM +import io.mockk.clearMocks +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.mockk +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +internal class WalletNotificationsSubscriberTest { + + private val stateHolder: WalletStateController = mockk(relaxed = true) + private val clickIntents: WalletClickIntents = mockk(relaxed = true) + private val getWalletNotificationsFactory: GetWalletNotificationsFactory = mockk() + private val getWalletNotificationsCarouselFactory: GetWalletNotificationsCarouselFactory = mockk() + private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender = mockk(relaxed = true) + private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender = mockk(relaxed = true) + private val getStoryContentUseCase: GetStoryContentUseCase = mockk() + private val userWallet: UserWallet = mockk(relaxed = true) + + private val subscriber = WalletNotificationsSubscriber( + userWallet = userWallet, + stateHolder = stateHolder, + clickIntents = clickIntents, + getWalletNotificationsFactory = getWalletNotificationsFactory, + getWalletNotificationsCarouselFactory = getWalletNotificationsCarouselFactory, + walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, + walletWarningsSingleEventSender = walletWarningsSingleEventSender, + getStoryContentUseCase = getStoryContentUseCase, + ) + + @BeforeEach + fun setup() { + clearMocks( + stateHolder, + getWalletNotificationsFactory, + getWalletNotificationsCarouselFactory, + getStoryContentUseCase, + userWallet, + ) + every { userWallet.walletId } returns WALLET_ID + + val walletUM = mockk(relaxed = true) + every { walletUM.walletsBalanceUM.id } returns WALLET_ID + val screenState = mockk(relaxed = true) + every { screenState.wallets2 } returns persistentListOf(walletUM) + every { stateHolder.uiState } returns MutableStateFlow(screenState) + every { stateHolder.getWalletUM(any()) } returns walletUM + + every { getWalletNotificationsFactory.create(any(), any()) } returns flowOf(persistentListOf()) + coEvery { getStoryContentUseCase.invokeSync(any(), any()) } returns null.right() + } + + @Test + fun `GIVEN carousel has yield boost banner WHEN subscribed THEN yield story is prefetched`() = runTest { + // Arrange + every { getWalletNotificationsCarouselFactory.create(any(), any()) } returns flowOf(yieldBoostCarousel()) + + // Act + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val scope = CoroutineScope(dispatcher) + subscriber.subscribe(scope, dispatcher) + advanceUntilIdle() + scope.cancel() + + // Assert + coVerify(exactly = 1) { + getStoryContentUseCase.invokeSync(id = StoryContentIds.STORY_FIRST_TIME_YIELD_PROMO.id, refresh = true) + } + } + + @Test + fun `GIVEN carousel has no yield boost banner WHEN subscribed THEN yield story is not prefetched`() = runTest { + // Arrange + every { getWalletNotificationsCarouselFactory.create(any(), any()) } returns flowOf(persistentListOf()) + + // Act + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val scope = CoroutineScope(dispatcher) + subscriber.subscribe(scope, dispatcher) + advanceUntilIdle() + scope.cancel() + + // Assert + coVerify(exactly = 0) { + getStoryContentUseCase.invokeSync(id = StoryContentIds.STORY_FIRST_TIME_YIELD_PROMO.id, refresh = true) + } + } + + private fun yieldBoostCarousel(): ImmutableList = persistentListOf( + WalletNotificationUM.YieldBoostPromo(onExploreClick = {}, onLaterClick = {}), + ) + + private companion object { + val WALLET_ID = UserWalletId("01") + } +} \ No newline at end of file