Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-26 16:03:11 +04:00
parent ff8f9c9b18
commit 6aab25427d
75 changed files with 2122 additions and 57 deletions

View file

@ -33,8 +33,10 @@ import com.tangem.domain.settings.NeverToSuggestRateAppUseCase
import com.tangem.domain.settings.RemindToRateAppLaterUseCase
import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
import com.tangem.domain.stories.models.StoryContentIds
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.wallets.usecase.*
import com.tangem.domain.yield.supply.usecase.YieldSupplySetShouldShowMainPromoUseCase
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
@ -87,6 +89,10 @@ internal interface WalletWarningsClickIntents {
fun onDismissAssetsDiscoveryNotification(userWalletId: UserWalletId)
fun onAssetsDiscoveryManageClick(userWalletId: UserWalletId)
fun onYieldBoostBannerClick(userWalletId: UserWalletId)
fun onDismissYieldBoostBanner(userWalletId: UserWalletId)
}
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
@ -118,6 +124,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val reviewManager: ReviewManager,
private val closeHotWalletUpgradeBannerUseCase: CloseHotWalletUpgradeBannerUseCase,
private val acknowledgeAssetsDiscoveryCompletionUseCase: AcknowledgeAssetsDiscoveryCompletionUseCase,
private val yieldSupplySetShouldShowMainPromoUseCase: YieldSupplySetShouldShowMainPromoUseCase,
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
override fun onAddBackupCardClick() {
@ -408,4 +415,21 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
AccountId.forMainCryptoPortfolio(userWalletId),
)
}
override fun onYieldBoostBannerClick(userWalletId: UserWalletId) {
appRouter.push(
Stories(
storyId = StoryContentIds.STORY_FIRST_TIME_YIELD_PROMO.id,
nextScreen = null,
screenSource = "YieldMainBanner",
shouldMarkAsSeenOnClose = false,
),
)
}
override fun onDismissYieldBoostBanner(userWalletId: UserWalletId) {
modelScope.launch {
yieldSupplySetShouldShowMainPromoUseCase(shouldShow = false)
}
}
}

View file

@ -98,6 +98,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
is WalletNotification.Warning.TangemPayRefreshNeeded -> null
is WalletNotification.Warning.TangemPayUnreachable -> null
is WalletNotification.UpgradeHotWalletPromo -> null
is WalletNotification.YieldBoostPromo -> null
is WalletNotification.AssetsDiscoveryCompleted -> null
is WalletNotification.CreateTangemPayAccount -> TangemPayAnalyticsEvents.PermanentBannerShowed()
}

View file

@ -5,6 +5,7 @@ package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.common.ui.notifications.NotificationId
import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.account.models.AccountStatusList
@ -28,7 +29,10 @@ import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
import com.tangem.domain.assetsdiscovery.model.AssetsDiscoveryProgress
import com.tangem.domain.assetsdiscovery.usecase.ObserveAssetsDiscoveryUseCase
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
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.WalletActivationBannerType
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.account.AccountDependencies
@ -60,6 +64,10 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private val checkHotWalletUpgradeBannerUseCase: CheckHotWalletUpgradeBannerUseCase,
private val observeAssetsDiscoveryUseCase: ObserveAssetsDiscoveryUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val shouldShowYieldBoostMainBannerUseCase: ShouldShowYieldBoostMainBannerUseCase,
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
private val designFeatureToggles: DesignFeatureToggles,
) {
@Suppress("UNCHECKED_CAST", "MagicNumber", "LongMethod", "CastNullableToNonNullableType")
@ -87,6 +95,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
getUpgradeBannerClosureTimestampUseCase(userWallet.walletId)
.distinctUntilChanged(),
assetsDiscoveryProgressFlow,
yieldSupplyGetShouldShowMainPromoUseCase().distinctUntilChanged(),
) { array -> array }
.map { array ->
val accountStatusList = array[0] as AccountStatusList
@ -97,6 +106,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val shouldShowUpgradeBanner = array[5] as Boolean
val closureTimestamp = array[6] as? Long
val assetsDiscoveryProgress = array[7] as AssetsDiscoveryProgress
val shouldShowYieldBoostPromoLocal = array[8] as Boolean
val flattenCurrencies = accountStatusList.flattenCurrencies()
val paymentAccountStatus = accountStatusList.accountStatuses
@ -165,10 +175,34 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
walletClickIntents = clickIntents,
)
}
addYieldBoostBannerNotification(
userWallet = userWallet,
shouldShowLocal = shouldShowYieldBoostPromoLocal,
clickIntents = clickIntents,
)
}.toImmutableList()
}
}
private suspend fun MutableList<WalletNotification>.addYieldBoostBannerNotification(
userWallet: UserWallet,
shouldShowLocal: Boolean,
clickIntents: WalletClickIntents,
) {
if (!shouldShowLocal) return
if (!yieldSupplyFeatureToggles.isYieldPromoEnabled) return
if (designFeatureToggles.isRedesignEnabled) return
val shouldShow = shouldShowYieldBoostMainBannerUseCase(userWallet.walletId).getOrNull() == true
if (!shouldShow) return
add(
WalletNotification.YieldBoostPromo(
onClick = { clickIntents.onYieldBoostBannerClick(userWallet.walletId) },
onCloseClick = { clickIntents.onDismissYieldBoostBanner(userWallet.walletId) },
),
)
}
private fun MutableList<WalletNotification>.addTangemPayWarnings(
status: AccountStatus.Payment,
userWallet: UserWallet,

View file

@ -9,6 +9,7 @@ import com.tangem.core.ui.components.notifications.NotificationConfig.IconTint
import com.tangem.core.ui.extensions.TextReference
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.feature.wallet.child.wallet.model.WalletActivationBannerType
import com.tangem.feature.wallet.impl.R
@ -319,6 +320,27 @@ sealed class WalletNotification(val config: NotificationConfig) {
),
)
data class YieldBoostPromo(
val onClick: () -> Unit,
val onCloseClick: () -> Unit,
) : WalletNotification(
config = NotificationConfig(
title = com.tangem.core.ui.extensions.combinedReference(
resourceReference(com.tangem.core.res.R.string.yield_apy_boost_banner_title),
stringReference(" · "),
resourceReference(com.tangem.core.res.R.string.yield_apy_boost_banner_title_apy_multiplied),
),
subtitle = resourceReference(com.tangem.core.res.R.string.yield_apy_boost_banner_subtitle),
iconResId = com.tangem.core.ui.R.drawable.ic_analytics_up_24,
iconTint = IconTint.Accent,
onCloseClick = onCloseClick,
buttonsState = ButtonsState.PrimaryButtonConfig(
text = resourceReference(com.tangem.core.res.R.string.yield_apy_boost_banner_button_title),
onClick = onClick,
),
),
)
data class AssetsDiscoveryCompleted(
val onCloseClick: () -> Unit,
val onManageTokensClick: () -> Unit,

View file

@ -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
@ -15,7 +17,9 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
@Suppress("LongParameterList")
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
internal class MultiWalletWarningsSubscriber @AssistedInject constructor(
@Assisted private val userWallet: UserWallet,
@ -24,6 +28,7 @@ internal class MultiWalletWarningsSubscriber @AssistedInject constructor(
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
private val getStoryContentUseCase: GetStoryContentUseCase,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<ImmutableList<WalletNotification>> {
@ -31,6 +36,15 @@ internal class MultiWalletWarningsSubscriber @AssistedInject constructor(
.conflate()
.distinctUntilChanged()
.onEach { warnings ->
if (warnings.any { it is WalletNotification.YieldBoostPromo }) {
coroutineScope.launch {
getStoryContentUseCase.invokeSync(
id = StoryContentIds.STORY_FIRST_TIME_YIELD_PROMO.id,
refresh = true,
)
}
}
val displayedState = stateController.getWalletState(userWallet.walletId)
// Wait until the wallet appears in the list

View file

@ -2,11 +2,18 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import com.tangem.common.ui.notifications.CreatePaymentAccountNotification
import com.tangem.core.ui.components.notifications.NoteMigrationNotification
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.extensions.annotatedReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.ForceDarkTheme
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.impl.R
@ -48,6 +55,14 @@ internal fun LazyListScope.notifications(configs: ImmutableList<WalletNotificati
)
}
}
is WalletNotification.YieldBoostPromo -> {
Notification(
config = item.config.copy(title = annotatedReference(yieldBoostPromoTitle())),
modifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null),
iconTint = TangemTheme.colors.icon.accent,
subtitleColor = TangemTheme.colors.text.secondary,
)
}
is WalletNotification.CreateTangemPayAccount -> {
CreatePaymentAccountNotification(
modifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null),
@ -76,4 +91,18 @@ internal fun LazyListScope.notifications(configs: ImmutableList<WalletNotificati
}
},
)
}
@Composable
private fun yieldBoostPromoTitle(): AnnotatedString {
val accent = TangemTheme.colors.text.accent
val baseTitle = stringResourceSafe(com.tangem.core.res.R.string.yield_apy_boost_banner_title)
val tailTitle = stringResourceSafe(com.tangem.core.res.R.string.yield_apy_boost_banner_title_apy_multiplied)
return buildAnnotatedString {
append(baseTitle)
append(" · ")
withStyle(SpanStyle(color = accent)) {
append(tailTitle)
}
}
}