summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/main/java/org/terst/feedreader/ui/ShareReceiverActivity.kt66
1 files changed, 39 insertions, 27 deletions
diff --git a/app/src/main/java/org/terst/feedreader/ui/ShareReceiverActivity.kt b/app/src/main/java/org/terst/feedreader/ui/ShareReceiverActivity.kt
index 1a383ae..70d45f0 100644
--- a/app/src/main/java/org/terst/feedreader/ui/ShareReceiverActivity.kt
+++ b/app/src/main/java/org/terst/feedreader/ui/ShareReceiverActivity.kt
@@ -22,11 +22,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import org.terst.feedreader.data.AppDatabase
-import org.terst.feedreader.data.Net
-import org.terst.feedreader.data.Settings
+import org.terst.feedreader.data.StashSaveOutcome
import org.terst.feedreader.data.ensureStandaloneArticle
-import org.terst.feedreader.stash.StashClient
-import org.terst.feedreader.stash.StashSaveResult
+import org.terst.feedreader.data.saveArticleToStash
private val URL_REGEX = Regex("""https?://\S+""")
@@ -54,29 +52,29 @@ class ShareReceiverActivity : ComponentActivity() {
@Composable
private fun ShareReceiverContent(url: String?, onDone: () -> Unit) {
val context = LocalContext.current
- var status by remember { mutableStateOf(if (url == null) "No link found in shared content" else "Saving…") }
+ var status by remember { mutableStateOf(if (url == null) "No link found in shared content" else null) }
var done by remember { mutableStateOf(url == null) }
+ // Requires an explicit tap before touching the network -- this activity is exported
+ // (any installed app can target it directly with an ACTION_SEND intent, not just via
+ // the system share sheet), and starting extraction/Stash-save immediately on launch
+ // meant ANY other app could silently make this one fetch+JS-render an arbitrary URL
+ // and archive it to the user's own Stash server with zero user interaction. A tap
+ // here is the only thing standing between "another app sent an intent" and "this app
+ // did real network work," so it can't be skipped just because url is non-null.
+ var confirmed by remember { mutableStateOf(false) }
- LaunchedEffect(url) {
- if (url == null) return@LaunchedEffect
+ LaunchedEffect(url, confirmed) {
+ if (url == null || !confirmed) return@LaunchedEffect
+ status = "Saving…"
val db = AppDatabase.get(context)
val entity = ensureStandaloneArticle(context, db, url)
if (!entity.isSaved) db.articleDao().setSaved(entity.id, true)
- val settings = Settings(context)
- val client = StashClient(Net.client, settings.stashBaseUrlNow(), settings.stashApiKeyNow())
- status = when (val result = client.saveArticle(url, entity.title)) {
- is StashSaveResult.Saved -> {
- db.articleDao().setStashArtifactId(entity.id, result.artifact.id)
- "Saved for offline reading and archived to Stash"
- }
- is StashSaveResult.AlreadySaved -> {
- db.articleDao().setStashArtifactId(entity.id, result.existingId)
- "Saved for offline reading (already in Stash)"
- }
- is StashSaveResult.Failed ->
- "Saved for offline reading. Stash archive failed: ${result.message}"
+ status = when (val outcome = saveArticleToStash(context, db, entity.id, url, entity.title)) {
+ is StashSaveOutcome.Saved -> "Saved for offline reading and archived to Stash"
+ is StashSaveOutcome.AlreadySaved -> "Saved for offline reading (already in Stash)"
+ is StashSaveOutcome.Failed -> "Saved for offline reading. Stash archive failed: ${outcome.reason}"
}
done = true
}
@@ -86,13 +84,27 @@ private fun ShareReceiverContent(url: String?, onDone: () -> Unit) {
Column(modifier = Modifier.padding(20.dp)) {
Text("Feeds", style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(8.dp))
- Text(status)
- if (!done) {
- Spacer(modifier = Modifier.height(8.dp))
- CircularProgressIndicator()
- } else {
- Spacer(modifier = Modifier.height(8.dp))
- Button(onClick = onDone) { Text("Done") }
+ when {
+ url == null -> {
+ Text(status ?: "No link found in shared content")
+ Spacer(modifier = Modifier.height(8.dp))
+ Button(onClick = onDone) { Text("Done") }
+ }
+ !confirmed -> {
+ Text(url)
+ Spacer(modifier = Modifier.height(8.dp))
+ Button(onClick = { confirmed = true }) { Text("Save") }
+ }
+ else -> {
+ Text(status ?: "Saving…")
+ if (!done) {
+ Spacer(modifier = Modifier.height(8.dp))
+ CircularProgressIndicator()
+ } else {
+ Spacer(modifier = Modifier.height(8.dp))
+ Button(onClick = onDone) { Text("Done") }
+ }
+ }
}
}
}