vc=33 fix: add FeedCacheStore.kt to git
git commit -am only stages tracked files. The new file existed locally but wasn't in the previous commit; build failed with Unresolved reference 'FeedCache' on every site that used it.
This commit is contained in:
parent
69560889ae
commit
c74b06436f
1 changed files with 71 additions and 0 deletions
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Sulkta-Coop
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Persistent per-channel cache for the subscription feed. Survives
|
||||
* process death, so opening Subs after a cold start shows the last
|
||||
* successful fetch immediately instead of waiting 5+ seconds for 30
|
||||
* channel browses to resolve.
|
||||
*
|
||||
* Storage: SharedPreferences with a single JSON blob. Total payload is
|
||||
* small (30 subs * 30 items * ~250 bytes = ~225 KB), well within SP's
|
||||
* comfortable size and well below the multi-MB threshold where you'd
|
||||
* want to graduate to Room or a file.
|
||||
*
|
||||
* Concurrency: writes from the feed VM are debounced via the single
|
||||
* `persist` call inside fetchChannelInto's success path. Reads happen
|
||||
* on VM init and are synchronous.
|
||||
*/
|
||||
|
||||
package com.sulkta.straw.data
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.sulkta.straw.feature.search.StreamItem
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Serializable
|
||||
data class FeedCacheEntry(
|
||||
val fetchedAt: Long,
|
||||
val items: List<StreamItem>,
|
||||
)
|
||||
|
||||
private const val PREFS = "straw_feed_cache"
|
||||
private const val KEY = "cache_v1"
|
||||
|
||||
class FeedCacheStore(context: Context) {
|
||||
private val sp: SharedPreferences = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||
private val json = Json { ignoreUnknownKeys = true; isLenient = true }
|
||||
|
||||
/** Snapshot of the disk cache. Returns empty map if nothing saved. */
|
||||
fun load(): Map<String, FeedCacheEntry> = runCatching {
|
||||
val s = sp.getString(KEY, null) ?: return emptyMap()
|
||||
json.decodeFromString<Map<String, FeedCacheEntry>>(s)
|
||||
}.getOrDefault(emptyMap())
|
||||
|
||||
/** Atomic write. Caller is responsible for diffing if needed. */
|
||||
fun save(map: Map<String, FeedCacheEntry>) {
|
||||
val s = json.encodeToString(map)
|
||||
sp.edit().putString(KEY, s).apply()
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
sp.edit().remove(KEY).apply()
|
||||
}
|
||||
}
|
||||
|
||||
object FeedCache {
|
||||
@Volatile private var instance: FeedCacheStore? = null
|
||||
|
||||
fun init(context: Context) {
|
||||
if (instance == null) {
|
||||
synchronized(this) {
|
||||
if (instance == null) instance = FeedCacheStore(context.applicationContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun get(): FeedCacheStore = instance
|
||||
?: error("FeedCacheStore not initialized — call FeedCache.init(context)")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue