diff options
Diffstat (limited to 'app/src/main/java/org/terst/feedreader')
| -rw-r--r-- | app/src/main/java/org/terst/feedreader/ui/ShareReceiverActivity.kt | 66 |
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") } + } + } } } } |
