Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-19 13:00:40 +05:00
parent 4e4e360ae1
commit 9adbde6e55
12 changed files with 454 additions and 75 deletions

View file

@ -1,9 +1,12 @@
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.IsHotWalletCreationSupported
import com.tangem.domain.hotwallet.IsAccessCodeSimpleUseCase
import com.tangem.domain.hotwallet.SetAccessCodeSkippedUseCase
import com.tangem.domain.hotwallet.ShouldShowUpgradeHotWalletBannerUseCase
import com.tangem.domain.hotwallet.repository.HotWalletRepository
import dagger.Module
import dagger.Provides
@ -40,4 +43,28 @@ internal object HotWalletDomainModule {
): IsHotWalletCreationSupported {
return IsHotWalletCreationSupported(hotWalletRepository)
}
@Provides
@Singleton
fun provideCheckHotWalletUpgradeBannerUseCase(
hotWalletRepository: HotWalletRepository,
): CheckHotWalletUpgradeBannerUseCase {
return CheckHotWalletUpgradeBannerUseCase(hotWalletRepository)
}
@Provides
@Singleton
fun provideCloseHotWalletUpgradeBannerUseCase(
hotWalletRepository: HotWalletRepository,
): CloseHotWalletUpgradeBannerUseCase {
return CloseHotWalletUpgradeBannerUseCase(hotWalletRepository)
}
@Provides
@Singleton
fun provideShouldShowUpgradeHotWalletBannerUseCase(
hotWalletRepository: HotWalletRepository,
): ShouldShowUpgradeHotWalletBannerUseCase {
return ShouldShowUpgradeHotWalletBannerUseCase(hotWalletRepository)
}
}

View file

@ -134,10 +134,6 @@ object PreferencesKeys {
val SHOULD_SHOW_UPGRADE_BANNER_KEY by lazy { stringPreferencesKey(name = "shouldShowUpgradeBanner") }
val SHOULD_SHOW_NEXT_TIME_UPGRADE_BANNER_KEY by lazy {
stringPreferencesKey(name = "shouldShowNextTimeUpgradeBanner")
}
val UPGRADE_BANNER_CLOSURE_TIMESTAMP_KEY by lazy { stringPreferencesKey(name = "upgradeBannerClosureTimestamp") }
val WALLET_CREATION_TIMESTAMP_KEY by lazy { stringPreferencesKey(name = "walletCreationTimestamp") }

View file

@ -10,11 +10,14 @@ import com.tangem.domain.hotwallet.repository.HotWalletRepository
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import java.util.concurrent.ConcurrentHashMap
internal class DefaultHotWalletRepository(
private val appPreferencesStore: AppPreferencesStore,
) : HotWalletRepository {
private val firstTopUpDetectedThisSession = ConcurrentHashMap<String, Unit>()
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.Q)
override fun isWalletCreationSupported(): Boolean {
return BuildConfig.DEBUG || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
@ -50,28 +53,12 @@ internal class DefaultHotWalletRepository(
}
}
override fun shouldShowNextTimeUpgradeBanner(userWalletId: UserWalletId): Flow<Boolean> = appPreferencesStore
.getObjectMap<Boolean>(PreferencesKeys.SHOULD_SHOW_NEXT_TIME_UPGRADE_BANNER_KEY)
.map { it[userWalletId.stringValue] == true }
override suspend fun setShouldShowNextTimeUpgradeBanner(userWalletId: UserWalletId, shouldShow: Boolean) {
appPreferencesStore.editData { mutablePreferences ->
mutablePreferences.setObjectMap(
key = PreferencesKeys.SHOULD_SHOW_NEXT_TIME_UPGRADE_BANNER_KEY,
value = mutablePreferences.getObjectMap<Boolean>(
PreferencesKeys.SHOULD_SHOW_NEXT_TIME_UPGRADE_BANNER_KEY,
)
.plus(userWalletId.stringValue to shouldShow),
)
}
}
override suspend fun getUpgradeBannerClosureTimestamp(userWalletId: UserWalletId): Long? {
return appPreferencesStore
.getObjectMapSync<Long>(PreferencesKeys.UPGRADE_BANNER_CLOSURE_TIMESTAMP_KEY)[userWalletId.stringValue]
}
override suspend fun setUpgradeBannerClosureTimestamp(userWalletId: UserWalletId, timestamp: Long) {
override suspend fun setUpgradeBannerClosureTimestamp(userWalletId: UserWalletId, timestamp: Long?) {
appPreferencesStore.editData { mutablePreferences ->
mutablePreferences.setObjectMap(
key = PreferencesKeys.UPGRADE_BANNER_CLOSURE_TIMESTAMP_KEY,
@ -110,4 +97,12 @@ internal class DefaultHotWalletRepository(
)
}
}
override fun isFirstTopUpDetectedThisSession(userWalletId: UserWalletId): Boolean {
return firstTopUpDetectedThisSession.containsKey(userWalletId.stringValue)
}
override fun markFirstTopUpDetectedThisSession(userWalletId: UserWalletId) {
firstTopUpDetectedThisSession[userWalletId.stringValue] = Unit
}
}

View file

@ -14,4 +14,10 @@ dependencies {
implementation(projects.domain.wallets.models)
implementation(deps.kotlin.coroutines)
implementation(deps.arrow.core)
testImplementation(deps.test.junit)
testImplementation(deps.test.coroutine)
testImplementation(deps.test.truth)
testImplementation(deps.test.mockk)
}

View file

@ -0,0 +1,61 @@
package com.tangem.domain.hotwallet
import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.tangem.domain.hotwallet.repository.HotWalletRepository
import com.tangem.domain.models.wallet.UserWalletId
import java.util.concurrent.TimeUnit
class CheckHotWalletUpgradeBannerUseCase(
private val hotWalletRepository: HotWalletRepository,
) {
suspend operator fun invoke(
walletId: UserWalletId,
hasBalance: Boolean,
shouldShowUpgradeBanner: Boolean,
): Either<Throwable, Boolean> = try {
val currentTime = System.currentTimeMillis()
val creationTimestamp = hotWalletRepository.getWalletCreationTimestamp(walletId)
val creationTimestampActual = if (creationTimestamp == null) {
// If creationTimestamp is null (wallet was created before this feature was released),
// store the current timestamp and use it below
hotWalletRepository.setWalletCreationTimestamp(walletId, currentTime)
currentTime
} else {
creationTimestamp
}
val closureTimestamp = hotWalletRepository.getUpgradeBannerClosureTimestamp(walletId)
val hasHadFirstTopUp = hotWalletRepository.hasHadFirstTopUp(walletId)
val daysSinceCreation = TimeUnit.MILLISECONDS.toDays(currentTime - creationTimestampActual)
val daysSinceClosure = closureTimestamp?.let { TimeUnit.MILLISECONDS.toDays(currentTime - it) }
// Wallet balance is positive, but the first top-up hasn't been tracked yet
if (hasBalance && !hasHadFirstTopUp) {
hotWalletRepository.setHasHadFirstTopUp(walletId, true)
hotWalletRepository.setShouldShowUpgradeBanner(walletId, true)
hotWalletRepository.setUpgradeBannerClosureTimestamp(walletId, null)
hotWalletRepository.markFirstTopUpDetectedThisSession(walletId)
}
val shouldShow = when {
// Banner should be shown (e.g., because of the first top-up in the previous session)
shouldShowUpgradeBanner -> !hotWalletRepository.isFirstTopUpDetectedThisSession(walletId)
// Banner was closed; it happened more than BANNER_RESHOW_DAYS (30) days ago
closureTimestamp != null && daysSinceClosure != null && daysSinceClosure >= BANNER_RESHOW_DAYS -> true
// Banner hasn't been closed; wallet was created more than BANNER_RESHOW_DAYS (30) days ago
closureTimestamp == null && daysSinceCreation >= BANNER_RESHOW_DAYS -> true
else -> false
}
shouldShow.right()
} catch (e: Exception) {
e.left()
}
companion object {
const val BANNER_RESHOW_DAYS = 30L
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.domain.hotwallet
import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.tangem.domain.hotwallet.repository.HotWalletRepository
import com.tangem.domain.models.wallet.UserWalletId
class CloseHotWalletUpgradeBannerUseCase(
private val hotWalletRepository: HotWalletRepository,
) {
suspend operator fun invoke(walletId: UserWalletId): Either<Throwable, Unit> = try {
val currentTime = System.currentTimeMillis()
hotWalletRepository.setShouldShowUpgradeBanner(walletId, false)
hotWalletRepository.setUpgradeBannerClosureTimestamp(walletId, currentTime)
Unit.right()
} catch (e: Exception) {
e.left()
}
}

View file

@ -0,0 +1,12 @@
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 ShouldShowUpgradeHotWalletBannerUseCase(
private val hotWalletRepository: HotWalletRepository,
) {
operator fun invoke(userWalletId: UserWalletId): Flow<Boolean> =
hotWalletRepository.shouldShowUpgradeBanner(userWalletId)
}

View file

@ -17,13 +17,9 @@ interface HotWalletRepository {
suspend fun setShouldShowUpgradeBanner(userWalletId: UserWalletId, shouldShow: Boolean)
fun shouldShowNextTimeUpgradeBanner(userWalletId: UserWalletId): Flow<Boolean>
suspend fun setShouldShowNextTimeUpgradeBanner(userWalletId: UserWalletId, shouldShow: Boolean)
suspend fun getUpgradeBannerClosureTimestamp(userWalletId: UserWalletId): Long?
suspend fun setUpgradeBannerClosureTimestamp(userWalletId: UserWalletId, timestamp: Long)
suspend fun setUpgradeBannerClosureTimestamp(userWalletId: UserWalletId, timestamp: Long?)
suspend fun getWalletCreationTimestamp(userWalletId: UserWalletId): Long?
@ -32,4 +28,8 @@ interface HotWalletRepository {
suspend fun hasHadFirstTopUp(userWalletId: UserWalletId): Boolean
suspend fun setHasHadFirstTopUp(userWalletId: UserWalletId, hasTopUp: Boolean)
fun isFirstTopUpDetectedThisSession(userWalletId: UserWalletId): Boolean
fun markFirstTopUpDetectedThisSession(userWalletId: UserWalletId)
}

View file

@ -0,0 +1,257 @@
package com.tangem.domain.hotwallet
import arrow.core.Either
import com.google.common.truth.Truth.assertThat
import com.tangem.domain.hotwallet.repository.HotWalletRepository
import com.tangem.domain.models.wallet.UserWalletId
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.test.runTest
import org.junit.Test
import java.util.concurrent.TimeUnit
class CheckHotWalletUpgradeBannerUseCaseTest {
private val hotWalletRepository: HotWalletRepository = mockk(relaxed = true)
private val useCase = CheckHotWalletUpgradeBannerUseCase(hotWalletRepository)
private val walletId = UserWalletId("0123456789ABCDEF")
@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
val result = useCase(
walletId = walletId,
hasBalance = false,
shouldShowUpgradeBanner = false,
)
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isFalse()
coVerify { hotWalletRepository.setWalletCreationTimestamp(walletId, any()) }
}
@Test
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
val result = useCase(
walletId = walletId,
hasBalance = true,
shouldShowUpgradeBanner = true,
)
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isTrue()
}
@Test
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
val result = useCase(
walletId = walletId,
hasBalance = false,
shouldShowUpgradeBanner = true,
)
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isTrue()
}
@Test
fun `GIVEN closure timestamp exists and 30 days since closure WHEN invoke THEN return true`() = runTest {
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
val result = useCase(
walletId = walletId,
hasBalance = true,
shouldShowUpgradeBanner = false,
)
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isTrue()
}
@Test
fun `GIVEN closure timestamp exists but less than 30 days since closure WHEN invoke THEN return false`() =
runTest {
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
val result = useCase(
walletId = walletId,
hasBalance = true,
shouldShowUpgradeBanner = false,
)
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isFalse()
}
@Test
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
val result = useCase(
walletId = walletId,
hasBalance = false,
shouldShowUpgradeBanner = false,
)
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isTrue()
}
@Test
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
val result = useCase(
walletId = walletId,
hasBalance = false,
shouldShowUpgradeBanner = false,
)
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isFalse()
}
@Test
fun `GIVEN no flags set but closure timestamp exists WHEN invoke THEN return false`() = runTest {
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
val result = useCase(
walletId = walletId,
hasBalance = false,
shouldShowUpgradeBanner = false,
)
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isFalse()
}
@Test
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
val result = useCase(
walletId = walletId,
hasBalance = true,
shouldShowUpgradeBanner = false,
)
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isFalse()
coVerify { hotWalletRepository.setHasHadFirstTopUp(walletId, true) }
coVerify { hotWalletRepository.setShouldShowUpgradeBanner(walletId, true) }
coVerify { hotWalletRepository.setUpgradeBannerClosureTimestamp(walletId, null) }
verify { hotWalletRepository.markFirstTopUpDetectedThisSession(walletId) }
}
@Test
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
val result = useCase(
walletId = walletId,
hasBalance = true,
shouldShowUpgradeBanner = true,
)
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isFalse()
}
@Test
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
useCase(
walletId = walletId,
hasBalance = true,
shouldShowUpgradeBanner = true,
)
coVerify(exactly = 0) { hotWalletRepository.setHasHadFirstTopUp(any(), any()) }
coVerify(exactly = 0) { hotWalletRepository.setShouldShowUpgradeBanner(any(), any()) }
}
@Test
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
val result1 = useCase(
walletId = walletId,
hasBalance = false,
shouldShowUpgradeBanner = false,
)
val result2 = useCase(
walletId = walletId,
hasBalance = false,
shouldShowUpgradeBanner = false,
)
val result3 = useCase(
walletId = walletId,
hasBalance = false,
shouldShowUpgradeBanner = false,
)
assertThat((result1 as Either.Right).value).isTrue()
assertThat((result2 as Either.Right).value).isTrue()
assertThat((result3 as Either.Right).value).isTrue()
}
}

View file

@ -0,0 +1,39 @@
package com.tangem.domain.hotwallet
import arrow.core.Either
import com.google.common.truth.Truth.assertThat
import com.tangem.domain.hotwallet.repository.HotWalletRepository
import com.tangem.domain.models.wallet.UserWalletId
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.Test
class CloseHotWalletUpgradeBannerUseCaseTest {
private val hotWalletRepository: HotWalletRepository = mockk(relaxed = true)
private val useCase = CloseHotWalletUpgradeBannerUseCase(hotWalletRepository)
private val walletId = UserWalletId("0123456789ABCDEF")
@Test
fun `WHEN invoke THEN set banner flag to false and closure timestamp`() = runTest {
val result = useCase(walletId)
assertThat(result).isInstanceOf(Either.Right::class.java)
coVerify { hotWalletRepository.setShouldShowUpgradeBanner(walletId, false) }
coVerify { hotWalletRepository.setUpgradeBannerClosureTimestamp(walletId, any()) }
}
@Test
fun `GIVEN repository throws exception WHEN invoke THEN return Either Left`() = runTest {
val exception = RuntimeException("Test error")
coEvery { hotWalletRepository.setShouldShowUpgradeBanner(walletId, false) } throws exception
val result = useCase(walletId)
assertThat(result).isInstanceOf(Either.Left::class.java)
assertThat((result as Either.Left).value).isEqualTo(exception)
}
}

View file

@ -17,7 +17,7 @@ import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.hotwallet.repository.HotWalletRepository
import com.tangem.domain.hotwallet.CloseHotWalletUpgradeBannerUseCase
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
@ -135,7 +135,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val getWalletsListForEnablingUseCase: GetWalletsForAutomaticallyPushEnablingUseCase,
private val uiMessageSender: UiMessageSender,
private val reviewManager: ReviewManager,
private val hotWalletRepository: HotWalletRepository,
private val closeHotWalletUpgradeBannerUseCase: CloseHotWalletUpgradeBannerUseCase,
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
override fun onAddBackupCardClick() {
@ -554,8 +554,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
modelScope.launch(dispatchers.main) {
val userWallet = getUserWalletUseCase(userWalletId).getOrNull()
if (userWallet is UserWallet.Hot) {
hotWalletRepository.setShouldShowUpgradeBanner(userWalletId, false)
hotWalletRepository.setShouldShowNextTimeUpgradeBanner(userWalletId, false)
appRouter.push(UpgradeWallet(userWalletId))
}
}
@ -565,15 +563,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
modelScope.launch(dispatchers.main) {
val userWallet = getUserWalletUseCase(userWalletId).getOrNull()
if (userWallet is UserWallet.Hot) {
val hasHadFirstTopUp = hotWalletRepository.hasHadFirstTopUp(userWalletId)
val currentTime = System.currentTimeMillis()
if (hasHadFirstTopUp) {
hotWalletRepository.setShouldShowUpgradeBanner(userWalletId, false)
hotWalletRepository.setUpgradeBannerClosureTimestamp(userWalletId, currentTime)
} else {
hotWalletRepository.setShouldShowUpgradeBanner(userWalletId, true)
}
closeHotWalletUpgradeBannerUseCase(userWalletId)
}
}
}

View file

@ -13,8 +13,9 @@ import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.core.lce.Lce
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.repository.HotWalletRepository
import com.tangem.domain.hotwallet.ShouldShowUpgradeHotWalletBannerUseCase
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.currency.CryptoCurrency
@ -44,7 +45,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@Suppress("LongParameterList", "LargeClass")
@ -60,7 +61,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private val notificationsRepository: NotificationsRepository,
private val accountDependencies: AccountDependencies,
private val getAccessCodeSkippedUseCase: GetAccessCodeSkippedUseCase,
private val hotWalletRepository: HotWalletRepository,
private val shouldShowUpgradeHotWalletBannerUseCase: ShouldShowUpgradeHotWalletBannerUseCase,
private val checkHotWalletUpgradeBannerUseCase: CheckHotWalletUpgradeBannerUseCase,
) {
@Suppress("UNCHECKED_CAST", "MagicNumber", "LongMethod")
@ -101,8 +103,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
getAccessCodeSkippedUseCase(userWallet.walletId).distinctUntilChanged(),
shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.YieldPromo)
.distinctUntilChanged(),
hotWalletRepository.shouldShowUpgradeBanner(userWallet.walletId).distinctUntilChanged(),
hotWalletRepository.shouldShowNextTimeUpgradeBanner(userWallet.walletId).distinctUntilChanged(),
shouldShowUpgradeHotWalletBannerUseCase.invoke(userWallet.walletId)
.distinctUntilChanged(),
) { array -> array }
.combine(tokenListFlow()) { array, any: Any -> arrayOf(any).plus(elements = array) }
.map { array ->
@ -117,7 +119,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val shouldAccessCodeSkipped = array[6] as Boolean
val shouldShowYieldPromo = array[7] as Boolean
val shouldShowUpgradeBanner = array[8] as Boolean
val shouldShowNextTimeUpgradeBanner = array[9] as Boolean
buildList {
addUsedOutdatedDataNotification(totalFiatBalance)
@ -129,7 +130,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
flattenCurrencies = flattenCurrencies,
clickIntents = clickIntents,
shouldShowUpgradeBanner = shouldShowUpgradeBanner,
shouldShowNextTimeUpgradeBanner = shouldShowNextTimeUpgradeBanner,
)
addFinishWalletActivationNotification(
@ -464,45 +464,22 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
)
}
@Suppress("CyclomaticComplexMethod")
private suspend fun MutableList<WalletNotification>.addUpgradeHotWalletPromoNotification(
userWallet: UserWallet,
flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
clickIntents: WalletClickIntents,
shouldShowUpgradeBanner: Boolean,
shouldShowNextTimeUpgradeBanner: Boolean,
) {
if (userWallet !is UserWallet.Hot) return
val currentTime = System.currentTimeMillis()
val creationTimestamp = hotWalletRepository.getWalletCreationTimestamp(userWallet.walletId)
val closureTimestamp = hotWalletRepository.getUpgradeBannerClosureTimestamp(userWallet.walletId)
val hasHadFirstTopUp = hotWalletRepository.hasHadFirstTopUp(userWallet.walletId)
if (creationTimestamp == null) {
hotWalletRepository.setWalletCreationTimestamp(userWallet.walletId, currentTime)
return
}
val daysSinceCreation = TimeUnit.MILLISECONDS.toDays(currentTime - creationTimestamp)
val daysSinceClosure = closureTimestamp?.let { TimeUnit.MILLISECONDS.toDays(currentTime - it) }
val currencies = flattenCurrencies.getOrNull(isPartialContentAccepted = true).orEmpty()
val hasBalance = currencies.any { it.value.amount.orZero().isPositive() }
if (hasBalance && !hasHadFirstTopUp) {
hotWalletRepository.setHasHadFirstTopUp(userWallet.walletId, true)
}
val shouldShow = when {
shouldShowUpgradeBanner && hasBalance -> true
shouldShowNextTimeUpgradeBanner && daysSinceClosure != null && daysSinceClosure >= UPGRADE_BANNER_RESHOW_DAYS -> true
!shouldShowUpgradeBanner && !shouldShowNextTimeUpgradeBanner && hasHadFirstTopUp && daysSinceCreation >= UPGRADE_BANNER_RESHOW_DAYS -> {
hotWalletRepository.setShouldShowNextTimeUpgradeBanner(userWallet.walletId, true)
true
}
else -> false
}
val shouldShow = checkHotWalletUpgradeBannerUseCase(
walletId = userWallet.walletId,
hasBalance = hasBalance,
shouldShowUpgradeBanner = shouldShowUpgradeBanner,
).getOrNull() ?: return
addIf(
element = WalletNotification.UpgradeHotWalletPromo(
@ -515,6 +492,5 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private companion object {
const val MAX_REMAINING_SIGNATURES_COUNT = 10
const val UPGRADE_BANNER_RESHOW_DAYS = 30
}
}