Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-12 17:35:12 +05:00
parent 69754e4159
commit 34cfc843ab
31 changed files with 322 additions and 170 deletions

View file

@ -6,7 +6,7 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase
import com.tangem.domain.promo.ShouldShowStoriesUseCase
import com.tangem.domain.promo.models.StoryContentIds
import com.tangem.feature.stories.api.StoriesComponent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -22,7 +22,7 @@ internal class StoriesModel @Inject constructor(
private val router: Router,
override val dispatchers: CoroutineDispatcherProvider,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase,
private val shouldShowStoriesUseCase: ShouldShowStoriesUseCase,
) : Model() {
val state: StateFlow<SwapStoriesUM> get() = _state
@ -38,7 +38,7 @@ internal class StoriesModel @Inject constructor(
private fun openScreen(hideStories: Boolean = true) {
modelScope.launch {
if (hideStories) {
shouldShowSwapStoriesUseCase.neverToShow()
shouldShowStoriesUseCase.neverToShow(StoryContentIds.STORY_FIRST_TIME_SWAP.id)
}
router.pop()
router.push(params.nextScreen)
@ -47,17 +47,21 @@ internal class StoriesModel @Inject constructor(
private fun initStories() {
modelScope.launch {
getStoryContentUseCase(params.storyId).fold(
getStoryContentUseCase.invokeSync(params.storyId).fold(
ifLeft = {
Timber.e("Unable to load stories for ${StoryContentIds.STORY_FIRST_TIME_SWAP.id}")
openScreen(hideStories = false) // Fallback to target screen
},
ifRight = { swapStory ->
_state.update {
SwapStoriesFactory.createStoriesState(
swapStory = swapStory,
onStoriesClose = ::openScreen,
)
if (swapStory == null) {
openScreen(hideStories = false) // Fallback to target screen
} else {
_state.update {
SwapStoriesFactory.createStoriesState(
swapStory = swapStory,
onStoriesClose = ::openScreen,
)
}
}
},
)

View file

@ -28,7 +28,7 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase
import com.tangem.domain.promo.ShouldShowStoriesUseCase
import com.tangem.domain.promo.models.StoryContentIds
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.model.CryptoCurrency
@ -90,7 +90,7 @@ internal class SwapViewModel @Inject constructor(
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase,
private val shouldShowStoriesUseCase: ShouldShowStoriesUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val featureToggles: SwapFeatureToggles,
swapInteractorFactory: SwapInteractor.Factory,
@ -172,9 +172,7 @@ internal class SwapViewModel @Inject constructor(
init {
viewModelScope.launch {
val isShowPromoStories = shouldShowSwapStoriesUseCase.invokeSync() && featureToggles.isPromoStoriesEnabled
if (isShowPromoStories) {
if (featureToggles.isPromoStoriesEnabled) {
initStories()
swapRouter.openScreen(SwapNavScreen.PromoStories)
}
@ -289,12 +287,14 @@ internal class SwapViewModel @Inject constructor(
private fun initStories() {
viewModelScope.launch {
getStoryContentUseCase(StoryContentIds.STORY_FIRST_TIME_SWAP.id).fold(
getStoryContentUseCase.invokeSync(StoryContentIds.STORY_FIRST_TIME_SWAP.id).fold(
ifLeft = {
Timber.e("Unable to load stories for ${StoryContentIds.STORY_FIRST_TIME_SWAP.id}")
},
ifRight = {
uiState = stateBuilder.createStoriesState(uiState, it)
ifRight = { story ->
story?.let {
uiState = stateBuilder.createStoriesState(uiState, story)
}
},
)
}
@ -1145,7 +1145,7 @@ internal class SwapViewModel @Inject constructor(
swapRouter.openScreen(SwapNavScreen.Success)
},
onStoriesClose = {
viewModelScope.launch { shouldShowSwapStoriesUseCase.neverToShow() }
viewModelScope.launch { shouldShowStoriesUseCase.neverToShow(StoryContentIds.STORY_FIRST_TIME_SWAP.id) }
swapRouter.openScreen(SwapNavScreen.Main)
},
)

View file

@ -2,7 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
@ -34,13 +34,13 @@ internal class MultiWalletContentLoader(
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val swapFeatureToggles: SwapFeatureToggles,
private val deepLinksRegistry: DeepLinksRegistry,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> {
return listOfNotNull(
return buildList {
MultiWalletTokenListSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,
@ -52,7 +52,7 @@ internal class MultiWalletContentLoader(
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
deepLinksRegistry = deepLinksRegistry,
),
).let(::add)
MultiWalletWarningsSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,
@ -60,13 +60,14 @@ internal class MultiWalletContentLoader(
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
),
MultiWalletActionButtonsSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,
shouldShowSwapStoriesUseCase = shouldShowSwapStoriesUseCase,
swapFeatureToggles = swapFeatureToggles,
),
)
).let(::add)
if (swapFeatureToggles.isPromoStoriesEnabled) {
MultiWalletActionButtonsSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,
getStoryContentUseCase = getStoryContentUseCase,
).let(::add)
}
}
}
}

View file

@ -2,7 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
@ -31,7 +31,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val swapFeatureToggles: SwapFeatureToggles,
private val deepLinksRegistry: DeepLinksRegistry,
) {
@ -50,7 +50,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
shouldShowSwapStoriesUseCase = shouldShowSwapStoriesUseCase,
getStoryContentUseCase = getStoryContentUseCase,
swapFeatureToggles = swapFeatureToggles,
deepLinksRegistry = deepLinksRegistry,
)

View file

@ -2,7 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
@ -32,13 +32,13 @@ internal class SingleWalletWithTokenContentLoader(
private val tokenListStore: MultiWalletTokenListStore,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val swapFeatureToggles: SwapFeatureToggles,
private val deepLinksRegistry: DeepLinksRegistry,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> {
return listOf(
return buildList {
SingleWalletWithTokenListSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,
@ -49,7 +49,7 @@ internal class SingleWalletWithTokenContentLoader(
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
deepLinksRegistry = deepLinksRegistry,
),
).let(::add)
MultiWalletWarningsSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,
@ -57,13 +57,14 @@ internal class SingleWalletWithTokenContentLoader(
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
),
MultiWalletActionButtonsSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,
shouldShowSwapStoriesUseCase = shouldShowSwapStoriesUseCase,
swapFeatureToggles = swapFeatureToggles,
),
)
).let(::add)
if (swapFeatureToggles.isPromoStoriesEnabled) {
MultiWalletActionButtonsSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,
getStoryContentUseCase = getStoryContentUseCase,
).let(::add)
}
}
}
}

View file

@ -2,7 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
@ -28,7 +28,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val swapFeatureToggles: SwapFeatureToggles,
private val deepLinksRegistry: DeepLinksRegistry,
) {
@ -46,7 +46,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
shouldShowSwapStoriesUseCase = shouldShowSwapStoriesUseCase,
getStoryContentUseCase = getStoryContentUseCase,
swapFeatureToggles = swapFeatureToggles,
deepLinksRegistry = deepLinksRegistry,
)

View file

@ -1,30 +1,28 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.promo.models.StoryContentIds
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.UpdateMultiWalletActionButtonBadgeTransformer
import com.tangem.features.swap.SwapFeatureToggles
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
internal class MultiWalletActionButtonsSubscriber(
private val userWallet: UserWallet,
private val stateHolder: WalletStateController,
private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase,
private val swapFeatureToggles: SwapFeatureToggles,
private val getStoryContentUseCase: GetStoryContentUseCase,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<*> = shouldShowSwapStoriesUseCase()
.distinctUntilChanged()
.map { showSwapBadge ->
val showBadge = swapFeatureToggles.isPromoStoriesEnabled && showSwapBadge
stateHolder.update(
UpdateMultiWalletActionButtonBadgeTransformer(
userWalletId = userWallet.walletId,
showSwapBadge = showBadge,
),
)
}
override fun create(coroutineScope: CoroutineScope): Flow<*> = getStoryContentUseCase(
id = StoryContentIds.STORY_FIRST_TIME_SWAP.id,
).map { maybeSwapStories ->
val isSwapStoriesNotNull = maybeSwapStories.getOrNull() != null
stateHolder.update(
UpdateMultiWalletActionButtonBadgeTransformer(
userWalletId = userWallet.walletId,
showSwapBadge = isSwapStoriesNotNull,
),
)
}
}

View file

@ -28,7 +28,7 @@ import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.promo.models.StoryContentIds
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.staking.model.stakekit.Yield
@ -113,7 +113,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private val removeCurrencyUseCase: RemoveCurrencyUseCase,
private val getExploreUrlUseCase: GetExploreUrlUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val dispatchers: CoroutineDispatcherProvider,
private val reduxStateHolder: ReduxStateHolder,
@ -610,8 +610,8 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private suspend fun getSwapRoute(targetRoute: AppRoute): AppRoute {
val isSwapStoriesEnabled = swapFeatureToggles.isPromoStoriesEnabled
val shouldShowSwapStories = shouldShowSwapStoriesUseCase.invokeSync()
val showSwapStories = shouldShowSwapStories && isSwapStoriesEnabled
val maybeSwapStories = getStoryContentUseCase.invokeSync(StoryContentIds.STORY_FIRST_TIME_SWAP.id)
val showSwapStories = maybeSwapStories.getOrNull() != null && isSwapStoriesEnabled
return if (showSwapStories) {
AppRoute.Stories(