From b3aa6845a7eaaade61eab71291a25b5ea2b79bd5 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sat, 22 Aug 2026 00:55:10 +0000 Subject: Extract shared saveArticleToStash helper (dedupe 5 copies) The build-a-StashClient-and-branch-on-the-result dance was independently copy-pasted at 5 call sites (ShareReceiverActivity, ReaderScreen x2, LinkReaderScreen x2). StashSaveOutcome carries the exact snackbar text plus the artifact id, as a sealed class so a caller that cares about the Saved/AlreadySaved distinction can still pattern-match instead of parsing the message back apart. Not yet adopted at any call site -- follow-up commits switch each one over. --- .../org/terst/feedreader/data/ArticleSaving.kt | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'app/src/main/java/org/terst/feedreader/data/ArticleSaving.kt') diff --git a/app/src/main/java/org/terst/feedreader/data/ArticleSaving.kt b/app/src/main/java/org/terst/feedreader/data/ArticleSaving.kt index f2bcb72..53d80eb 100644 --- a/app/src/main/java/org/terst/feedreader/data/ArticleSaving.kt +++ b/app/src/main/java/org/terst/feedreader/data/ArticleSaving.kt @@ -2,6 +2,8 @@ package org.terst.feedreader.data import android.content.Context import org.terst.feedreader.readability.ReaderExtractor +import org.terst.feedreader.stash.StashClient +import org.terst.feedreader.stash.StashSaveResult import java.security.MessageDigest fun urlToArticleId(url: String): String = @@ -28,3 +30,36 @@ suspend fun ensureStandaloneArticle(context: Context, db: AppDatabase, url: Stri db.articleDao().upsert(entity) return entity } + +/** + * Outcome of a Stash save, already carrying the exact snackbar text and (when the save + * succeeded, in either sense) the artifact id -- the same three-way handling (persist the + * id, pick a message) was independently copy-pasted at 5 call sites (ShareReceiverActivity, + * ReaderScreen x2, LinkReaderScreen x2) before this, all doing the identical + * request-build-and-branch dance. Most callers only need [message] (and, to also update + * their own local Compose state like `article = a.copy(stashArtifactId = ...)`, + * [artifactId]) -- it's a sealed class rather than a flat data class so a caller that DOES + * care about the Saved/AlreadySaved distinction (ShareReceiverActivity's wording does) can + * still pattern-match exhaustively instead of parsing [message] back apart. + */ +sealed class StashSaveOutcome(val message: String, val artifactId: String?) { + class Saved(artifactId: String) : StashSaveOutcome("Saved to Stash", artifactId) + class AlreadySaved(artifactId: String) : StashSaveOutcome("Already in Stash", artifactId) + class Failed(val reason: String) : StashSaveOutcome("Save failed: $reason", null) +} + +suspend fun saveArticleToStash(context: Context, db: AppDatabase, articleId: String, url: String, title: String?): StashSaveOutcome { + val settings = Settings(context) + val client = StashClient(Net.client, settings.stashBaseUrlNow(), settings.stashApiKeyNow()) + return when (val result = client.saveArticle(url, title)) { + is StashSaveResult.Saved -> { + db.articleDao().setStashArtifactId(articleId, result.artifact.id) + StashSaveOutcome.Saved(result.artifact.id) + } + is StashSaveResult.AlreadySaved -> { + db.articleDao().setStashArtifactId(articleId, result.existingId) + StashSaveOutcome.AlreadySaved(result.existingId) + } + is StashSaveResult.Failed -> StashSaveOutcome.Failed(result.message) + } +} -- cgit v1.2.3