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
9a072d9a12
commit
1e5e3d9f36
1 changed files with 71 additions and 0 deletions
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2026 Sulkta
|
||||||
|
* 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