From f202c603259c40c4d21a4385c9f69a3efca87acc Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sat, 25 Jul 2026 00:55:59 +0000 Subject: Text-only bottom bar, re-extract action, fix YouTube thumbnails, verify Stash creds before saving - Bottom bar: Previous/Keep unread/Next are now plain text labels, no icons - Add "Re-download and re-extract" to the overflow menu (ReaderScreen and LinkReaderScreen) to force a fresh fetch + Readability pass on demand, overwriting whatever's cached - Fix a real compile bug in the previous commit's reader-mode-off handler (passed null into a non-null setExtractedContent) via a proper clearExtractedContent() DAO query - YouTube embeds: fixed the broken-image-glyph bug two ways -- (1) an 11-char video-ID regex was accidentally matching a playlist embed's literal "videoseries" placeholder path, building a thumbnail URL for a video that doesn't exist, and (2) Readability's link-density cleanup was discarding the replacement entirely (a bare reads as an ad/share unit). Restructured as a
/
pair -- the same shape a normal captioned photo takes -- confirmed live this survives extraction intact - Stash settings: API key field is now a password field with a show/hide toggle, and "Verify and save" makes a real authenticated request before persisting anything, showing why it failed instead of silently saving bad credentials Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL --- .../java/org/terst/feedreader/stash/StashClient.kt | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'app/src/main/java/org/terst/feedreader/stash') diff --git a/app/src/main/java/org/terst/feedreader/stash/StashClient.kt b/app/src/main/java/org/terst/feedreader/stash/StashClient.kt index 28cc2b3..b613d66 100644 --- a/app/src/main/java/org/terst/feedreader/stash/StashClient.kt +++ b/app/src/main/java/org/terst/feedreader/stash/StashClient.kt @@ -36,6 +36,11 @@ sealed class StashSaveResult { data class Failed(val message: String) : StashSaveResult() } +sealed class StashVerifyResult { + object Valid : StashVerifyResult() + data class Invalid(val message: String) : StashVerifyResult() +} + /** * Client for the parts of Stash's API this app needs: POST /artifacts to * archive an article. Uses sync=true so content_md comes back in the same @@ -48,6 +53,30 @@ class StashClient( private val baseUrl: String, private val apiKey: String ) { + // A cheap authenticated GET against the same collection /artifacts POSTs to, rather than + // a dedicated /me or /health endpoint this API doesn't document -- reads are safe/free of + // side effects, unlike actually creating an artifact just to test the key works. + suspend fun verifyCredentials(): StashVerifyResult = withContext(Dispatchers.IO) { + if (baseUrl.isBlank() || apiKey.isBlank()) { + return@withContext StashVerifyResult.Invalid("Base URL and API key are both required") + } + val request = Request.Builder() + .url("$baseUrl/artifacts") + .header("X-API-Key", apiKey) + .get() + .build() + + runCatching { + client.newCall(request).execute().use { response -> + when { + response.isSuccessful -> StashVerifyResult.Valid + response.code == 401 || response.code == 403 -> StashVerifyResult.Invalid("Rejected: API key not accepted") + else -> StashVerifyResult.Invalid("HTTP ${response.code}") + } + } + }.getOrElse { StashVerifyResult.Invalid(it.message ?: "Couldn't reach that URL") } + } + suspend fun saveArticle(url: String, title: String?): StashSaveResult = withContext(Dispatchers.IO) { if (baseUrl.isBlank() || apiKey.isBlank()) { return@withContext StashSaveResult.Failed("Stash isn't configured (set base URL and API key in Settings)") -- cgit v1.2.3