Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-26 15:43:23 +05:00
parent cf1f3c8b30
commit 8459eb743b
8 changed files with 30 additions and 54 deletions

View file

@ -29,6 +29,7 @@ import com.tangem.domain.settings.IncrementAppLaunchCounterUseCase
import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase
import com.tangem.domain.staking.FetchStakingTokensUseCase import com.tangem.domain.staking.FetchStakingTokensUseCase
import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.feature.swap.analytics.StoriesEvents
import com.tangem.features.onramp.OnrampFeatureToggles import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.features.swap.SwapFeatureToggles import com.tangem.features.swap.SwapFeatureToggles
import com.tangem.tap.common.extensions.setContext import com.tangem.tap.common.extensions.setContext
@ -300,19 +301,32 @@ internal class MainViewModel @Inject constructor(
private fun preloadImages() { private fun preloadImages() {
try { try {
viewModelScope.launch { viewModelScope.launch {
val swapStories = if (swapFeatureToggles.isPromoStoriesEnabled) { if (swapFeatureToggles.isPromoStoriesEnabled) {
getStoryContentUseCase.invokeSync(StoryContentIds.STORY_FIRST_TIME_SWAP.id) val swapStories = getStoryContentUseCase.invokeSync(
.getOrNull() id = StoryContentIds.STORY_FIRST_TIME_SWAP.id,
} else { refresh = true,
null ).getOrNull()
}
val storiesImages = swapStories?.getImageUrls() if (swapStories == null) {
// try to preload images for stories analyticsEventHandler.send(
storiesImages?.forEach(imagePreloader::preload) StoriesEvents.Error(
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
),
)
} else {
val storiesImages = swapStories.getImageUrls()
// try to preload images for stories
storiesImages.forEach(imagePreloader::preload)
}
}
} }
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.e(ex.message) Timber.e(ex.message)
analyticsEventHandler.send(
StoriesEvents.Error(
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
),
)
} }
} }
} }

View file

@ -44,15 +44,15 @@ internal class DefaultPromoRepository(
} }
override fun getStoryById(id: String): Flow<StoryContent?> = isReadyToShowStories(id).mapLatest { override fun getStoryById(id: String): Flow<StoryContent?> = isReadyToShowStories(id).mapLatest {
getStoryByIdSync(id) getStoryByIdSync(id = id, refresh = false)
} }
override suspend fun getStoryByIdSync(id: String): StoryContent? = withContext(dispatchers.io) { override suspend fun getStoryByIdSync(id: String, refresh: Boolean): StoryContent? = withContext(dispatchers.io) {
if (!isReadyToShowStoriesSync(id)) return@withContext null if (!isReadyToShowStoriesSync(id)) return@withContext null
val storedPromo = promoStoriesStore.getSyncOrNull(storyId = id) val storedPromo = promoStoriesStore.getSyncOrNull(storyId = id)
// Get last stored promo by id if possible or get from network // Get last stored promo by id if possible or get from network
val story = if (storedPromo == null) { val story = if (storedPromo == null && refresh) {
val storyContent = runCatching { val storyContent = runCatching {
// Important to return // Important to return
withTimeoutOrNull(STORIES_LOAD_DELAY) { withTimeoutOrNull(STORIES_LOAD_DELAY) {

View file

@ -18,7 +18,7 @@ class GetStoryContentUseCase(
.catch { emit(it.left()) } .catch { emit(it.left()) }
.onEmpty { emit(null.right()) } .onEmpty { emit(null.right()) }
suspend fun invokeSync(id: String): Either<Throwable, StoryContent?> = Either.catch { suspend fun invokeSync(id: String, refresh: Boolean = false): Either<Throwable, StoryContent?> = Either.catch {
promoRepository.getStoryByIdSync(id) promoRepository.getStoryByIdSync(id, refresh)
} }
} }

View file

@ -18,7 +18,7 @@ interface PromoRepository {
// region Stories // region Stories
fun getStoryById(id: String): Flow<StoryContent?> fun getStoryById(id: String): Flow<StoryContent?>
suspend fun getStoryByIdSync(id: String): StoryContent? suspend fun getStoryByIdSync(id: String, refresh: Boolean): StoryContent?
fun isReadyToShowStories(storyId: String): Flow<Boolean> fun isReadyToShowStories(storyId: String): Flow<Boolean>

View file

@ -19,15 +19,4 @@ sealed class StoriesEvents(
WATCHED to watchCount, WATCHED to watchCount,
), ),
) )
data class Error(
val source: String,
val type: String,
) : StoriesEvents(
event = "Error",
params = mapOf(
AnalyticsParam.SOURCE to source,
AnalyticsParam.TYPE to type,
),
)
} }

View file

@ -53,22 +53,10 @@ internal class StoriesModel @Inject constructor(
getStoryContentUseCase.invokeSync(params.storyId).fold( getStoryContentUseCase.invokeSync(params.storyId).fold(
ifLeft = { ifLeft = {
Timber.e("Unable to load stories for ${StoryContentIds.STORY_FIRST_TIME_SWAP.id}") Timber.e("Unable to load stories for ${StoryContentIds.STORY_FIRST_TIME_SWAP.id}")
analyticsEventHandler.send(
StoriesEvents.Error(
source = params.screenSource,
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
),
)
openScreen(hideStories = false) // Fallback to target screen openScreen(hideStories = false) // Fallback to target screen
}, },
ifRight = { swapStory -> ifRight = { swapStory ->
if (swapStory == null) { if (swapStory == null) {
analyticsEventHandler.send(
StoriesEvents.Error(
source = params.screenSource,
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
),
)
openScreen(hideStories = false) // Fallback to target screen openScreen(hideStories = false) // Fallback to target screen
} else { } else {
_state.update { _state.update {

View file

@ -21,12 +21,10 @@ sealed class StoriesEvents(
) )
data class Error( data class Error(
val source: String,
val type: String, val type: String,
) : StoriesEvents( ) : StoriesEvents(
event = "Error", event = "Error",
params = mapOf( params = mapOf(
AnalyticsParam.SOURCE to source,
AnalyticsParam.TYPE to type, AnalyticsParam.TYPE to type,
), ),
) )

View file

@ -82,7 +82,6 @@ internal class SwapModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider, override val dispatchers: CoroutineDispatcherProvider,
private val blockchainInteractor: BlockchainInteractor, private val blockchainInteractor: BlockchainInteractor,
private val analyticsEventHandler: AnalyticsEventHandler, private val analyticsEventHandler: AnalyticsEventHandler,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusSyncUseCase, private val getCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusSyncUseCase,
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase, private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
@ -96,6 +95,7 @@ internal class SwapModel @Inject constructor(
private val shouldShowStoriesUseCase: ShouldShowStoriesUseCase, private val shouldShowStoriesUseCase: ShouldShowStoriesUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase, private val getStoryContentUseCase: GetStoryContentUseCase,
private val featureToggles: SwapFeatureToggles, private val featureToggles: SwapFeatureToggles,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
swapInteractorFactory: SwapInteractor.Factory, swapInteractorFactory: SwapInteractor.Factory,
private val urlOpener: UrlOpener, private val urlOpener: UrlOpener,
router: AppRouter, router: AppRouter,
@ -278,23 +278,10 @@ internal class SwapModel @Inject constructor(
getStoryContentUseCase.invokeSync(StoryContentIds.STORY_FIRST_TIME_SWAP.id).fold( getStoryContentUseCase.invokeSync(StoryContentIds.STORY_FIRST_TIME_SWAP.id).fold(
ifLeft = { ifLeft = {
Timber.e("Unable to load stories for ${StoryContentIds.STORY_FIRST_TIME_SWAP.id}") Timber.e("Unable to load stories for ${StoryContentIds.STORY_FIRST_TIME_SWAP.id}")
analyticsEventHandler.send(
StoriesEvents.Error(
source = params.screenSource,
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
),
)
}, },
ifRight = { story -> ifRight = { story ->
if (story != null) { if (story != null) {
uiState = stateBuilder.createStoriesState(uiState, story) uiState = stateBuilder.createStoriesState(uiState, story)
} else {
analyticsEventHandler.send(
StoriesEvents.Error(
source = params.screenSource,
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
),
)
} }
}, },
) )