From 5e10c943a043a74b6f611d17145ed3020503a4a8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 20 Feb 2026 14:48:19 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/HotWalletDomainModule.kt | 9 ++++++ .../hotwallet/DefaultHotWalletRepository.kt | 7 ++--- .../CheckHotWalletUpgradeBannerUseCase.kt | 2 +- ...GetUpgradeBannerClosureTimestampUseCase.kt | 13 +++++++++ .../repository/HotWalletRepository.kt | 2 +- .../CheckHotWalletUpgradeBannerUseCaseTest.kt | 28 ++++++++++--------- .../domain/GetMultiWalletWarningsFactory.kt | 12 ++++++-- 7 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/GetUpgradeBannerClosureTimestampUseCase.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/HotWalletDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/HotWalletDomainModule.kt index c99860711a..f3ee5bef10 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/HotWalletDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/HotWalletDomainModule.kt @@ -3,6 +3,7 @@ package com.tangem.tap.di.domain import com.tangem.domain.hotwallet.CheckHotWalletUpgradeBannerUseCase import com.tangem.domain.hotwallet.CloseHotWalletUpgradeBannerUseCase import com.tangem.domain.hotwallet.GetAccessCodeSkippedUseCase +import com.tangem.domain.hotwallet.GetUpgradeBannerClosureTimestampUseCase import com.tangem.domain.hotwallet.IsHotWalletCreationSupported import com.tangem.domain.hotwallet.IsAccessCodeSimpleUseCase import com.tangem.domain.hotwallet.SetAccessCodeSkippedUseCase @@ -67,4 +68,12 @@ internal object HotWalletDomainModule { ): ShouldShowUpgradeHotWalletBannerUseCase { return ShouldShowUpgradeHotWalletBannerUseCase(hotWalletRepository) } + + @Provides + @Singleton + fun provideGetUpgradeBannerClosureTimestampUseCase( + hotWalletRepository: HotWalletRepository, + ): GetUpgradeBannerClosureTimestampUseCase { + return GetUpgradeBannerClosureTimestampUseCase(hotWalletRepository) + } } \ No newline at end of file diff --git a/data/hot-wallet/src/main/java/com/tangem/data/hotwallet/DefaultHotWalletRepository.kt b/data/hot-wallet/src/main/java/com/tangem/data/hotwallet/DefaultHotWalletRepository.kt index dbbe66db29..c112597c17 100644 --- a/data/hot-wallet/src/main/java/com/tangem/data/hotwallet/DefaultHotWalletRepository.kt +++ b/data/hot-wallet/src/main/java/com/tangem/data/hotwallet/DefaultHotWalletRepository.kt @@ -53,10 +53,9 @@ internal class DefaultHotWalletRepository( } } - override suspend fun getUpgradeBannerClosureTimestamp(userWalletId: UserWalletId): Long? { - return appPreferencesStore - .getObjectMapSync(PreferencesKeys.UPGRADE_BANNER_CLOSURE_TIMESTAMP_KEY)[userWalletId.stringValue] - } + override fun upgradeBannerClosureTimestamp(userWalletId: UserWalletId): Flow = appPreferencesStore + .getObjectMap(PreferencesKeys.UPGRADE_BANNER_CLOSURE_TIMESTAMP_KEY) + .map { it[userWalletId.stringValue] } override suspend fun setUpgradeBannerClosureTimestamp(userWalletId: UserWalletId, timestamp: Long?) { appPreferencesStore.editData { mutablePreferences -> diff --git a/domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/CheckHotWalletUpgradeBannerUseCase.kt b/domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/CheckHotWalletUpgradeBannerUseCase.kt index 1038773b7c..28f7be6bd3 100644 --- a/domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/CheckHotWalletUpgradeBannerUseCase.kt +++ b/domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/CheckHotWalletUpgradeBannerUseCase.kt @@ -14,6 +14,7 @@ class CheckHotWalletUpgradeBannerUseCase( walletId: UserWalletId, hasBalance: Boolean, shouldShowUpgradeBanner: Boolean, + closureTimestamp: Long?, ): Either = try { val currentTime = System.currentTimeMillis() val creationTimestamp = hotWalletRepository.getWalletCreationTimestamp(walletId) @@ -27,7 +28,6 @@ class CheckHotWalletUpgradeBannerUseCase( creationTimestamp } - val closureTimestamp = hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) val hasHadFirstTopUp = hotWalletRepository.hasHadFirstTopUp(walletId) val daysSinceCreation = TimeUnit.MILLISECONDS.toDays(currentTime - creationTimestampActual) diff --git a/domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/GetUpgradeBannerClosureTimestampUseCase.kt b/domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/GetUpgradeBannerClosureTimestampUseCase.kt new file mode 100644 index 0000000000..dbe9d2f2af --- /dev/null +++ b/domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/GetUpgradeBannerClosureTimestampUseCase.kt @@ -0,0 +1,13 @@ +package com.tangem.domain.hotwallet + +import com.tangem.domain.hotwallet.repository.HotWalletRepository +import com.tangem.domain.models.wallet.UserWalletId +import kotlinx.coroutines.flow.Flow + +class GetUpgradeBannerClosureTimestampUseCase( + private val hotWalletRepository: HotWalletRepository, +) { + operator fun invoke(userWalletId: UserWalletId): Flow { + return hotWalletRepository.upgradeBannerClosureTimestamp(userWalletId) + } +} \ No newline at end of file diff --git a/domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/repository/HotWalletRepository.kt b/domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/repository/HotWalletRepository.kt index 01229d8d3a..2d63b01cde 100644 --- a/domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/repository/HotWalletRepository.kt +++ b/domain/hot-wallet/src/main/kotlin/com/tangem/domain/hotwallet/repository/HotWalletRepository.kt @@ -17,7 +17,7 @@ interface HotWalletRepository { suspend fun setShouldShowUpgradeBanner(userWalletId: UserWalletId, shouldShow: Boolean) - suspend fun getUpgradeBannerClosureTimestamp(userWalletId: UserWalletId): Long? + fun upgradeBannerClosureTimestamp(userWalletId: UserWalletId): Flow suspend fun setUpgradeBannerClosureTimestamp(userWalletId: UserWalletId, timestamp: Long?) diff --git a/domain/hot-wallet/src/test/kotlin/com/tangem/domain/hotwallet/CheckHotWalletUpgradeBannerUseCaseTest.kt b/domain/hot-wallet/src/test/kotlin/com/tangem/domain/hotwallet/CheckHotWalletUpgradeBannerUseCaseTest.kt index 6e415efea4..447864f896 100644 --- a/domain/hot-wallet/src/test/kotlin/com/tangem/domain/hotwallet/CheckHotWalletUpgradeBannerUseCaseTest.kt +++ b/domain/hot-wallet/src/test/kotlin/com/tangem/domain/hotwallet/CheckHotWalletUpgradeBannerUseCaseTest.kt @@ -23,7 +23,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { @Test fun `GIVEN creation timestamp is null WHEN invoke THEN set timestamp and return false`() = runTest { coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns null - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns null coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns false every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns false @@ -31,6 +30,7 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = false, shouldShowUpgradeBanner = false, + closureTimestamp = null, ) assertThat(result).isInstanceOf(Either.Right::class.java) @@ -42,7 +42,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { fun `GIVEN shouldShowUpgradeBanner is true and hasBalance WHEN invoke THEN return true`() = runTest { val creationTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(5) coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns creationTimestamp - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns null coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns true every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns false @@ -50,6 +49,7 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = true, shouldShowUpgradeBanner = true, + closureTimestamp = null, ) assertThat(result).isInstanceOf(Either.Right::class.java) @@ -60,7 +60,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { fun `GIVEN shouldShowUpgradeBanner is true WHEN invoke THEN return true regardless of balance`() = runTest { val creationTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(5) coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns creationTimestamp - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns null coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns true every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns false @@ -68,6 +67,7 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = false, shouldShowUpgradeBanner = true, + closureTimestamp = null, ) assertThat(result).isInstanceOf(Either.Right::class.java) @@ -79,7 +79,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { val creationTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60) val closureTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31) coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns creationTimestamp - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns closureTimestamp coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns true every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns false @@ -87,6 +86,7 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = true, shouldShowUpgradeBanner = false, + closureTimestamp = closureTimestamp, ) assertThat(result).isInstanceOf(Either.Right::class.java) @@ -99,7 +99,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { val creationTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60) val closureTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(15) coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns creationTimestamp - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns closureTimestamp coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns true every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns false @@ -107,7 +106,8 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = true, shouldShowUpgradeBanner = false, - ) + closureTimestamp = closureTimestamp, + ) assertThat(result).isInstanceOf(Either.Right::class.java) assertThat((result as Either.Right).value).isFalse() @@ -117,7 +117,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { fun `GIVEN no flags set and no closure and 30 days since creation WHEN invoke THEN return true`() = runTest { val creationTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31) coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns creationTimestamp - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns null coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns false every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns false @@ -125,6 +124,7 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = false, shouldShowUpgradeBanner = false, + closureTimestamp = null, ) assertThat(result).isInstanceOf(Either.Right::class.java) @@ -135,7 +135,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { fun `GIVEN no flags set but less than 30 days since creation WHEN invoke THEN return false`() = runTest { val creationTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(15) coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns creationTimestamp - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns null coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns false every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns false @@ -143,6 +142,7 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = false, shouldShowUpgradeBanner = false, + closureTimestamp = null, ) assertThat(result).isInstanceOf(Either.Right::class.java) @@ -154,7 +154,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { val creationTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60) val closureTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(5) coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns creationTimestamp - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns closureTimestamp coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns false every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns false @@ -162,6 +161,7 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = false, shouldShowUpgradeBanner = false, + closureTimestamp = closureTimestamp, ) assertThat(result).isInstanceOf(Either.Right::class.java) @@ -172,7 +172,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { fun `GIVEN first top-up detected WHEN invoke THEN return false and mark session`() = runTest { val creationTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(5) coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns creationTimestamp - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns null coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns false every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns false @@ -180,6 +179,7 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = true, shouldShowUpgradeBanner = false, + closureTimestamp = null, ) assertThat(result).isInstanceOf(Either.Right::class.java) @@ -194,7 +194,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { fun `GIVEN first top-up detected this session WHEN invoke THEN return false`() = runTest { val creationTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(5) coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns creationTimestamp - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns null coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns true every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns true @@ -202,6 +201,7 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = true, shouldShowUpgradeBanner = true, + closureTimestamp = null, ) assertThat(result).isInstanceOf(Either.Right::class.java) @@ -212,7 +212,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { fun `GIVEN already had first top-up WHEN invoke with balance THEN do not set flags again`() = runTest { val creationTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(5) coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns creationTimestamp - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns null coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns true every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns false @@ -220,6 +219,7 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = true, shouldShowUpgradeBanner = true, + closureTimestamp = null, ) coVerify(exactly = 0) { hotWalletRepository.setHasHadFirstTopUp(any(), any()) } @@ -230,7 +230,6 @@ class CheckHotWalletUpgradeBannerUseCaseTest { fun `GIVEN multiple re-emissions with same state WHEN invoke THEN return same result`() = runTest { val creationTimestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31) coEvery { hotWalletRepository.getWalletCreationTimestamp(walletId) } returns creationTimestamp - coEvery { hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId) } returns null coEvery { hotWalletRepository.hasHadFirstTopUp(walletId) } returns false every { hotWalletRepository.isFirstTopUpDetectedThisSession(walletId) } returns false @@ -238,16 +237,19 @@ class CheckHotWalletUpgradeBannerUseCaseTest { walletId = walletId, hasBalance = false, shouldShowUpgradeBanner = false, + closureTimestamp = null, ) val result2 = useCase( walletId = walletId, hasBalance = false, shouldShowUpgradeBanner = false, + closureTimestamp = null, ) val result3 = useCase( walletId = walletId, hasBalance = false, shouldShowUpgradeBanner = false, + closureTimestamp = null, ) assertThat((result1 as Either.Right).value).isTrue() diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt index 6218c6257a..a243ddef48 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt @@ -15,6 +15,7 @@ import com.tangem.domain.core.lce.LceFlow import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.hotwallet.CheckHotWalletUpgradeBannerUseCase import com.tangem.domain.hotwallet.GetAccessCodeSkippedUseCase +import com.tangem.domain.hotwallet.GetUpgradeBannerClosureTimestampUseCase import com.tangem.domain.hotwallet.ShouldShowUpgradeHotWalletBannerUseCase import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TotalFiatBalance @@ -62,10 +63,11 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( private val accountDependencies: AccountDependencies, private val getAccessCodeSkippedUseCase: GetAccessCodeSkippedUseCase, private val shouldShowUpgradeHotWalletBannerUseCase: ShouldShowUpgradeHotWalletBannerUseCase, + private val getUpgradeBannerClosureTimestampUseCase: GetUpgradeBannerClosureTimestampUseCase, private val checkHotWalletUpgradeBannerUseCase: CheckHotWalletUpgradeBannerUseCase, ) { - @Suppress("UNCHECKED_CAST", "MagicNumber", "LongMethod") + @Suppress("UNCHECKED_CAST", "MagicNumber", "LongMethod", "CastNullableToNonNullableType") fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow> { val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver @@ -105,8 +107,10 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( .distinctUntilChanged(), shouldShowUpgradeHotWalletBannerUseCase.invoke(userWallet.walletId) .distinctUntilChanged(), + getUpgradeBannerClosureTimestampUseCase(userWallet.walletId) + .distinctUntilChanged(), ) { array -> array } - .combine(tokenListFlow()) { array, any: Any -> arrayOf(any).plus(elements = array) } + .combine(tokenListFlow()) { array, any: Any? -> arrayOf(any).plus(elements = array) } .map { array -> val lceTokens = array[0] as Lce>> val totalFiatBalance = lceTokens.map { it.first } @@ -119,6 +123,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( val shouldAccessCodeSkipped = array[6] as Boolean val shouldShowYieldPromo = array[7] as Boolean val shouldShowUpgradeBanner = array[8] as Boolean + val closureTimestamp = array[9] as? Long buildList { addUsedOutdatedDataNotification(totalFiatBalance) @@ -130,6 +135,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( flattenCurrencies = flattenCurrencies, clickIntents = clickIntents, shouldShowUpgradeBanner = shouldShowUpgradeBanner, + closureTimestamp = closureTimestamp, ) addFinishWalletActivationNotification( @@ -469,6 +475,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( flattenCurrencies: Lce>, clickIntents: WalletClickIntents, shouldShowUpgradeBanner: Boolean, + closureTimestamp: Long?, ) { if (userWallet !is UserWallet.Hot) return @@ -479,6 +486,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( walletId = userWallet.walletId, hasBalance = hasBalance, shouldShowUpgradeBanner = shouldShowUpgradeBanner, + closureTimestamp = closureTimestamp, ).getOrNull() ?: return addIf(