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/ui/SettingsScreen.kt | 54 ++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) (limited to 'app/src/main/java/org/terst/feedreader/ui/SettingsScreen.kt') diff --git a/app/src/main/java/org/terst/feedreader/ui/SettingsScreen.kt b/app/src/main/java/org/terst/feedreader/ui/SettingsScreen.kt index b7897f4..3bd6e3e 100644 --- a/app/src/main/java/org/terst/feedreader/ui/SettingsScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/SettingsScreen.kt @@ -13,7 +13,10 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api @@ -36,12 +39,17 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import org.terst.feedreader.data.AppDatabase import org.terst.feedreader.data.FilterAction import org.terst.feedreader.data.FilterRuleEntity +import org.terst.feedreader.data.Net import org.terst.feedreader.data.Settings +import org.terst.feedreader.stash.StashClient +import org.terst.feedreader.stash.StashVerifyResult @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -53,6 +61,9 @@ fun SettingsScreen(onBack: () -> Unit) { var stashUrl by remember { mutableStateOf("") } var stashKey by remember { mutableStateOf("") } + var stashKeyVisible by remember { mutableStateOf(false) } + var isVerifyingStash by remember { mutableStateOf(false) } + var stashStatus by remember { mutableStateOf(null) } LaunchedEffect(Unit) { stashUrl = settings.stashBaseUrlNow() stashKey = settings.stashApiKeyNow() @@ -105,15 +116,52 @@ fun SettingsScreen(onBack: () -> Unit) { Spacer(modifier = Modifier.height(8.dp)) OutlinedTextField( value = stashKey, - onValueChange = { stashKey = it }, + onValueChange = { stashKey = it; stashStatus = null }, label = { Text("API key") }, + singleLine = true, + visualTransformation = if (stashKeyVisible) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + IconButton(onClick = { stashKeyVisible = !stashKeyVisible }) { + Icon( + if (stashKeyVisible) Icons.Default.VisibilityOff else Icons.Default.Visibility, + contentDescription = if (stashKeyVisible) "Hide API key" else "Show API key" + ) + } + }, modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(8.dp)) Button( - onClick = { scope.launch { settings.setStash(stashUrl, stashKey) } }, + enabled = !isVerifyingStash, + onClick = { + scope.launch { + isVerifyingStash = true + stashStatus = null + val client = StashClient(Net.client, stashUrl.trim(), stashKey.trim()) + when (val result = client.verifyCredentials()) { + is StashVerifyResult.Valid -> { + settings.setStash(stashUrl.trim(), stashKey.trim()) + stashStatus = "Verified and saved" + } + is StashVerifyResult.Invalid -> { + stashStatus = result.message + } + } + isVerifyingStash = false + } + }, modifier = Modifier.fillMaxWidth() - ) { Text("Save") } + ) { + if (isVerifyingStash) { + CircularProgressIndicator(modifier = Modifier.height(20.dp).width(20.dp)) + } else { + Text("Verify and save") + } + } + stashStatus?.let { + Spacer(modifier = Modifier.height(8.dp)) + Text(it, style = MaterialTheme.typography.bodySmall) + } } HorizontalDivider() } -- cgit v1.2.3