Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-25 16:42:18 +05:00
parent f79fbcb530
commit 7bdbd18cad
15 changed files with 86 additions and 52 deletions

View file

@ -15,14 +15,14 @@ internal object PromoDomainModule {
@Singleton
fun provideShouldShowSwapPromoWalletUseCase(
promoSettingsRepository: PromoRepository,
): ShouldShowSwapPromoWalletUseCase {
return ShouldShowSwapPromoWalletUseCase(promoSettingsRepository)
): ShouldShowPromoWalletUseCase {
return ShouldShowPromoWalletUseCase(promoSettingsRepository)
}
@Provides
@Singleton
fun provideShouldShowSwapPromoTokenUseCase(promoRepository: PromoRepository): ShouldShowSwapPromoTokenUseCase {
return ShouldShowSwapPromoTokenUseCase(promoRepository)
fun provideShouldShowSwapPromoTokenUseCase(promoRepository: PromoRepository): ShouldShowPromoTokenUseCase {
return ShouldShowPromoTokenUseCase(promoRepository)
}
@Provides

View file

@ -123,8 +123,12 @@ object PreferencesKeys {
val WALLETS_NFT_ENABLED_STATES_KEY by lazy { stringPreferencesKey(name = "walletsNftEnabledStates") }
// region Promo
fun getShouldShowStoriesKey(storyId: String) = booleanPreferencesKey("shouldShowStories_$storyId")
fun getShouldShowPromoKey(promoId: String) = booleanPreferencesKey("shouldShowPromo_$promoId")
// endregion
// region Permission
fun getShouldShowPermission(permission: String) = booleanPreferencesKey("shouldShowPushPermission_$permission")

View file

@ -4,13 +4,16 @@ import com.tangem.data.promo.converters.StoryContentResponseConverter
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.PreferencesKeys.getShouldShowStoriesKey
import com.tangem.datasource.local.preferences.utils.get
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.store
import com.tangem.datasource.local.promo.PromoStoriesStore
import com.tangem.domain.promo.PromoRepository
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.promo.models.StoryContent
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
@ -27,20 +30,30 @@ internal class DefaultPromoRepository(
private val storyContentConverter = StoryContentResponseConverter()
override fun isReadyToShowWalletSwapPromo(): Flow<Boolean> {
return flowOf(false) // Use it on new promo action
override fun isReadyToShowWalletPromo(userWalletId: UserWalletId, promoId: PromoId): Flow<Boolean> {
return appPreferencesStore.get(
key = PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name),
default = true,
)
}
override fun isReadyToShowTokenSwapPromo(): Flow<Boolean> {
return flowOf(false) // Use it on new promo action
override fun isReadyToShowTokenPromo(promoId: PromoId): Flow<Boolean> {
return if (promoId == PromoId.Referral) {
flowOf(false)
} else {
appPreferencesStore.get(
PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name),
default = false,
)
}
}
override suspend fun setNeverToShowWalletSwapPromo() {
// Use it on new promo action
override suspend fun setNeverToShowWalletPromo(promoId: PromoId) {
appPreferencesStore.store(PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name), false)
}
override suspend fun setNeverToShowTokenSwapPromo() {
// Use it on new promo action
override suspend fun setNeverToShowTokenPromo(promoId: PromoId) {
appPreferencesStore.store(PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name), false)
}
override fun getStoryById(id: String): Flow<StoryContent?> = isReadyToShowStories(id).mapLatest {

View file

@ -23,4 +23,8 @@ data class PromoBanner(
private companion object {
const val ACTIVE_STATUS = "active"
}
}
enum class PromoId {
Referral,
}

View file

@ -1,18 +1,20 @@
package com.tangem.domain.promo
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.promo.models.StoryContent
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
interface PromoRepository {
// region Promo
fun isReadyToShowWalletSwapPromo(): Flow<Boolean>
fun isReadyToShowWalletPromo(userWalletId: UserWalletId, promoId: PromoId): Flow<Boolean>
fun isReadyToShowTokenSwapPromo(): Flow<Boolean>
fun isReadyToShowTokenPromo(promoId: PromoId): Flow<Boolean>
suspend fun setNeverToShowWalletSwapPromo()
suspend fun setNeverToShowWalletPromo(promoId: PromoId)
suspend fun setNeverToShowTokenSwapPromo()
suspend fun setNeverToShowTokenPromo(promoId: PromoId)
// endregion
// region Stories

View file

@ -0,0 +1,11 @@
package com.tangem.domain.promo
import com.tangem.domain.promo.models.PromoId
import kotlinx.coroutines.flow.Flow
class ShouldShowPromoTokenUseCase(private val promoRepository: PromoRepository) {
operator fun invoke(promoId: PromoId): Flow<Boolean> = promoRepository.isReadyToShowTokenPromo(promoId)
suspend fun neverToShow(promoId: PromoId) = promoRepository.setNeverToShowTokenPromo(promoId)
}

View file

@ -0,0 +1,14 @@
package com.tangem.domain.promo
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
class ShouldShowPromoWalletUseCase(private val promoRepository: PromoRepository) {
operator fun invoke(userWalletId: UserWalletId, promoId: PromoId): Flow<Boolean> {
return promoRepository.isReadyToShowWalletPromo(userWalletId, promoId)
}
suspend fun neverToShow(promoId: PromoId) = promoRepository.setNeverToShowWalletPromo(promoId)
}

View file

@ -1,10 +0,0 @@
package com.tangem.domain.promo
import kotlinx.coroutines.flow.Flow
class ShouldShowSwapPromoTokenUseCase(private val promoRepository: PromoRepository) {
operator fun invoke(): Flow<Boolean> = promoRepository.isReadyToShowTokenSwapPromo()
suspend fun neverToShow() = promoRepository.setNeverToShowTokenSwapPromo()
}

View file

@ -1,10 +0,0 @@
package com.tangem.domain.promo
import kotlinx.coroutines.flow.Flow
class ShouldShowSwapPromoWalletUseCase(private val promoRepository: PromoRepository) {
operator fun invoke(): Flow<Boolean> = promoRepository.isReadyToShowWalletSwapPromo()
suspend fun neverToShow() = promoRepository.setNeverToShowWalletSwapPromo()
}

View file

@ -12,6 +12,7 @@ dependencies {
implementation(projects.domain.models)
implementation(projects.domain.txhistory.models)
implementation(projects.domain.staking.models)
implementation(projects.domain.promo.models)
/** Other dependencies */
implementation(deps.kotlin.serialization)

View file

@ -1,5 +1,6 @@
package com.tangem.domain.tokens.model.warnings
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.tokens.model.CryptoCurrency
import org.joda.time.DateTime
import java.math.BigDecimal
@ -42,6 +43,7 @@ sealed class CryptoCurrencyWarning {
data class Rent(val rent: BigDecimal, val exemptionAmount: BigDecimal) : CryptoCurrencyWarning()
data class SwapPromo(
val promoId: PromoId,
val startDateTime: DateTime,
val endDateTime: DateTime,
) : CryptoCurrencyWarning()

View file

@ -2,6 +2,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.model
import com.tangem.common.ui.bottomsheet.receive.AddressModel
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig
@ -49,9 +50,9 @@ interface TokenDetailsClickIntents {
fun onGoToProviderClick(url: String)
fun onSwapPromoDismiss()
fun onSwapPromoDismiss(promoId: PromoId)
fun onSwapPromoClick()
fun onSwapPromoClick(promoId: PromoId)
fun onGenerateExtendedKey()

View file

@ -35,7 +35,8 @@ import com.tangem.domain.card.NetworkHasDerivationUseCase
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.promo.ShouldShowSwapPromoTokenUseCase
import com.tangem.domain.promo.ShouldShowPromoTokenUseCase
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
import com.tangem.domain.staking.GetStakingEntryInfoUseCase
@ -101,7 +102,7 @@ internal class TokenDetailsModel @Inject constructor(
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getCurrencyWarningsUseCase: GetCurrencyWarningsUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val shouldShowSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase,
private val shouldShowPromoTokenUseCase: ShouldShowPromoTokenUseCase,
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
private val getExtendedPublicKeyForCurrencyUseCase: GetExtendedPublicKeyForCurrencyUseCase,
private val getStakingEntryInfoUseCase: GetStakingEntryInfoUseCase,
@ -811,9 +812,9 @@ internal class TokenDetailsModel @Inject constructor(
router.openUrl(url)
}
override fun onSwapPromoDismiss() {
override fun onSwapPromoDismiss(promoId: PromoId) {
modelScope.launch(dispatchers.main) {
shouldShowSwapPromoTokenUseCase.neverToShow()
shouldShowPromoTokenUseCase.neverToShow(promoId)
analyticsEventsHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Token,
@ -824,9 +825,9 @@ internal class TokenDetailsModel @Inject constructor(
}
}
override fun onSwapPromoClick() {
override fun onSwapPromoClick(promoId: PromoId) {
modelScope.launch(dispatchers.main) {
shouldShowSwapPromoTokenUseCase.neverToShow()
shouldShowPromoTokenUseCase.neverToShow(promoId)
analyticsEventsHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Token,

View file

@ -10,10 +10,10 @@ import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.domain.tokens.model.warnings.HederaWarnings
import com.tangem.domain.tokens.model.warnings.KaspaWarnings
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification.*
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.removeBy
@ -98,8 +98,8 @@ internal class TokenDetailsNotificationConverter(
is CryptoCurrencyWarning.SwapPromo -> SwapPromo(
startDateTime = warning.startDateTime,
endDateTime = warning.endDateTime,
onSwapClick = clickIntents::onSwapPromoClick,
onCloseClick = clickIntents::onSwapPromoDismiss,
onSwapClick = { clickIntents.onSwapPromoClick(warning.promoId) },
onCloseClick = { clickIntents.onSwapPromoDismiss(warning.promoId) },
)
is CryptoCurrencyWarning.BeaconChainShutdown -> NetworkShutdown(
title = resourceReference(R.string.warning_beacon_chain_retirement_title),

View file

@ -13,7 +13,8 @@ import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
import com.tangem.domain.promo.ShouldShowSwapPromoWalletUseCase
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.redux.LegacyAction
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.settings.NeverToSuggestRateAppUseCase
@ -67,7 +68,7 @@ internal interface WalletWarningsClickIntents {
fun onCloseRateAppWarningClick()
fun onCloseSwapPromoClick()
fun onClosePromoClick(promoId: PromoId)
fun onSupportClick()
@ -98,7 +99,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val reduxStateHolder: ReduxStateHolder,
private val dispatchers: CoroutineDispatcherProvider,
private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
@ -268,7 +269,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
}
override fun onCloseSwapPromoClick() {
override fun onClosePromoClick(promoId: PromoId) {
analyticsEventHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Main,
@ -277,7 +278,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
),
)
modelScope.launch(dispatchers.main) {
shouldShowSwapPromoWalletUseCase.neverToShow()
shouldShowPromoWalletUseCase.neverToShow(promoId)
}
}