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 import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map 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. */ class Settings(private val context: Context) { val stashBaseUrl: Flow = context.settingsDataStore.data.map { it[SettingsKeys.STASH_BASE_URL] ?: "" } val stashApiKey: Flow = context.settingsDataStore.data.map { it[SettingsKeys.STASH_API_KEY] ?: "" } suspend fun stashBaseUrlNow(): String = stashBaseUrl.first() suspend fun stashApiKeyNow(): String = stashApiKey.first() suspend fun setStash(baseUrl: String, apiKey: String) { context.settingsDataStore.edit { prefs -> prefs[SettingsKeys.STASH_BASE_URL] = baseUrl.trim().trimEnd('/') 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 = context.settingsDataStore.data.map { it[SettingsKeys.LAST_FETCH_AT] ?: 0L } val lastFetchNewArticles: Flow = context.settingsDataStore.data.map { it[SettingsKeys.LAST_FETCH_NEW_ARTICLES] ?: 0 } val lastAcknowledgedFetchAt: Flow = 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 } } }