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.staking.FetchStakingTokensUseCase
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.feature.swap.analytics.StoriesEvents
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.features.swap.SwapFeatureToggles
import com.tangem.tap.common.extensions.setContext
@ -300,19 +301,32 @@ internal class MainViewModel @Inject constructor(
private fun preloadImages() {
try {
viewModelScope.launch {
val swapStories = if (swapFeatureToggles.isPromoStoriesEnabled) {
getStoryContentUseCase.invokeSync(StoryContentIds.STORY_FIRST_TIME_SWAP.id)
.getOrNull()
} else {
null
}
if (swapFeatureToggles.isPromoStoriesEnabled) {
val swapStories = getStoryContentUseCase.invokeSync(
id = StoryContentIds.STORY_FIRST_TIME_SWAP.id,
refresh = true,
).getOrNull()
val storiesImages = swapStories?.getImageUrls()
// try to preload images for stories
storiesImages?.forEach(imagePreloader::preload)
if (swapStories == null) {
analyticsEventHandler.send(
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) {
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 {
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
val storedPromo = promoStoriesStore.getSyncOrNull(storyId = id)
// 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 {
// Important to return
withTimeoutOrNull(STORIES_LOAD_DELAY) {

View file

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

View file

@ -18,7 +18,7 @@ interface PromoRepository {
// region Stories
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>

View file

@ -19,15 +19,4 @@ sealed class StoriesEvents(
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(
ifLeft = {
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
},
ifRight = { swapStory ->
if (swapStory == null) {
analyticsEventHandler.send(
StoriesEvents.Error(
source = params.screenSource,
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
),
)
openScreen(hideStories = false) // Fallback to target screen
} else {
_state.update {

View file

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

View file

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