diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-08-22 01:06:35 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-08-22 01:06:35 +0000 |
| commit | 429594e6bc1069f7e7141060df3a0fd8bf63749b (patch) | |
| tree | e9aa74d339a7f3fa06aa4cce70858fd0260f4177 /app/src/main/java/org/terst/feedreader/data/Settings.kt | |
| parent | 2352cb39d9623074192a6a1a7fec47e22ab4606b (diff) | |
Concurrent feed fetching with durable fetch-result tracking
Feeds are fetched with bounded concurrency (semaphore of 8) instead of
strictly sequentially -- with 100+ subscriptions, one slow/stalling
server no longer delays every feed queued behind it. A process-local
mutex prevents the periodic and manual/alarm-triggered one-time
fetches (two different WorkManager unique-work names, so its own dedup
doesn't cover this) from ever running concurrently and racing writes
to the same feed rows.
Settings.recordFetchResult persists lastFetchAt/lastFetchNewArticles
on every run, not derived from WorkInfo.State -- a PeriodicWorkRequest
cycles back to ENQUEUED after every successful run and never reaches
SUCCEEDED, so anything watching for that only ever saw manual fetches.
ArticleListScreen surfaces this as a "N new articles" snackbar
(acknowledged durably, so returning to the screen doesn't re-show it)
and computes live "Checking feed X of Y" progress from the worker's
tagged WorkInfo, scoped to the Unread/Older views that actually
aggregate every subscription.
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/data/Settings.kt')
| -rw-r--r-- | app/src/main/java/org/terst/feedreader/data/Settings.kt | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/app/src/main/java/org/terst/feedreader/data/Settings.kt b/app/src/main/java/org/terst/feedreader/data/Settings.kt index b4af70e..ce05974 100644 --- a/app/src/main/java/org/terst/feedreader/data/Settings.kt +++ b/app/src/main/java/org/terst/feedreader/data/Settings.kt @@ -2,6 +2,8 @@ package org.terst.feedreader.data import android.content.Context import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.longPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import kotlinx.coroutines.flow.Flow @@ -13,6 +15,9 @@ val Context.settingsDataStore by preferencesDataStore(name = "settings") private object SettingsKeys { val STASH_BASE_URL = stringPreferencesKey("stash_base_url") val STASH_API_KEY = stringPreferencesKey("stash_api_key") + val LAST_FETCH_AT = longPreferencesKey("last_fetch_at") + val LAST_FETCH_NEW_ARTICLES = intPreferencesKey("last_fetch_new_articles") + val LAST_ACKNOWLEDGED_FETCH_AT = longPreferencesKey("last_acknowledged_fetch_at") } /** Stash connection settings, configured on the Settings screen. Both blank until set. */ @@ -31,4 +36,32 @@ class Settings(private val context: Context) { prefs[SettingsKeys.STASH_API_KEY] = apiKey.trim() } } + + // Durable record of "when did a feed fetch last finish, and with how many new + // articles" -- deliberately NOT derived from WorkManager's WorkInfo.State. A + // PeriodicWorkRequest's state cycles back to ENQUEUED after every successful run + // (it never settles on SUCCEEDED the way a one-time request does), so anything + // watching for SUCCEEDED only ever sees manual/alarm-triggered one-time fetches -- + // the actual hourly background fetch is invisible to that approach. Writing the + // result here on every run (periodic or one-time alike) gives the UI one consistent + // signal regardless of which triggered it. + val lastFetchAt: Flow<Long> = + context.settingsDataStore.data.map { it[SettingsKeys.LAST_FETCH_AT] ?: 0L } + val lastFetchNewArticles: Flow<Int> = + context.settingsDataStore.data.map { it[SettingsKeys.LAST_FETCH_NEW_ARTICLES] ?: 0 } + val lastAcknowledgedFetchAt: Flow<Long> = + context.settingsDataStore.data.map { it[SettingsKeys.LAST_ACKNOWLEDGED_FETCH_AT] ?: 0L } + + suspend fun recordFetchResult(newArticles: Int) { + context.settingsDataStore.edit { prefs -> + prefs[SettingsKeys.LAST_FETCH_AT] = System.currentTimeMillis() + prefs[SettingsKeys.LAST_FETCH_NEW_ARTICLES] = newArticles + } + } + + // Persisted (not just in-memory Compose state) so that navigating away from a screen + // and back doesn't forget a fetch was already acknowledged and re-show its snackbar. + suspend fun acknowledgeFetch(at: Long) { + context.settingsDataStore.edit { prefs -> prefs[SettingsKeys.LAST_ACKNOWLEDGED_FETCH_AT] = at } + } } |
