Cache hero billboard pool to disk so it paints immediately on cold start

This commit is contained in:
KhooLy 2026-07-19 15:44:41 +03:00
parent a77c73803c
commit dfbae648cd
2 changed files with 42 additions and 2 deletions

View file

@ -99,3 +99,27 @@ class HomeCategoryCache @Inject constructor(
return if (profile?.isGuest == true || profile == null) "home_cache_guest" else "home_cache_${profile.id}"
}
}
@Singleton
class HomeBillboardCache @Inject constructor(
@ApplicationContext context: Context,
private val gson: Gson
) {
private val prefs = context.getSharedPreferences("fluxa_home_billboard_cache", Context.MODE_PRIVATE)
fun load(profile: UserProfile?): List<Meta> {
val saved = prefs.getString(key(profile), null) ?: return emptyList()
return runCatching {
val listType = object : TypeToken<List<Meta>>() {}.type
gson.fromJson<List<Meta>>(saved, listType).orEmpty()
}.getOrElse { emptyList() }
}
fun save(profile: UserProfile?, pool: List<Meta>) {
prefs.edit().putString(key(profile), gson.toJson(pool)).apply()
}
private fun key(profile: UserProfile?): String {
return if (profile?.isGuest == true || profile == null) "billboard_cache_guest" else "billboard_cache_${profile.id}"
}
}

View file

@ -51,6 +51,7 @@ class HomeViewModel @Inject constructor(
private val watchlistStore: WatchlistStore,
private val searchHistoryStore: SearchHistoryStore,
private val homeCategoryCache: HomeCategoryCache,
private val homeBillboardCache: HomeBillboardCache,
private val forgottenContinueWatchingStore: ForgottenContinueWatchingStore,
private val coordinatorFactory: HomeViewModelCoordinatorFactory,
private val externalSyncPushCoordinator: ExternalSyncPushCoordinator,
@ -254,6 +255,7 @@ class HomeViewModel @Inject constructor(
private var externalContinueWatching: List<Meta> = emptyList()
private var traktWatchedState: TraktWatchedState = TraktWatchedState()
private var currentActiveProfile: UserProfile? = null
private var hasFetchedFreshBillboard = false
private var searchJob: Job? = null
private val _isSearchLoading = MutableStateFlow(false)
val isSearchLoading: StateFlow<Boolean> = _isSearchLoading.asStateFlow()
@ -349,7 +351,10 @@ class HomeViewModel @Inject constructor(
fetchCs3FeedItems = { feed ->
platformContentGateway.cloudFeedItems(feed.key)
},
setPool = { billboardState.poolValue = it },
setPool = { pool ->
billboardState.poolValue = pool
homeBillboardCache.save(currentActiveProfile, pool)
},
updateContent = billboardRuntime::updateContent,
normalizePool = { items -> items.distinctBy(HomeBillboardRanking::contentIdentityKey) },
startRotation = billboardRuntime::startRotation
@ -1018,9 +1023,20 @@ class HomeViewModel @Inject constructor(
}
scheduleCs3Refresh()
}
if (force || billboardState.poolValue.isEmpty()) {
if (billboardState.poolValue.isEmpty()) {
val cachedPool = homeBillboardCache.load(activeProfile)
if (cachedPool.isNotEmpty()) {
billboardState.poolValue = cachedPool
viewModelScope.launch {
billboardRuntime.updateContent(cachedPool[0])
billboardRuntime.startRotation()
}
}
}
if (force || !hasFetchedFreshBillboard) {
viewModelScope.launch {
billboardLoader.load(activeProfile)
hasFetchedFreshBillboard = true
}
}
}