package org.terst.feedreader.ui import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn 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.material3.Button import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember 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.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.Settings @OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingsScreen(onBack: () -> Unit) { val context = LocalContext.current val db = remember { AppDatabase.get(context) } val settings = remember { Settings(context) } val scope = rememberCoroutineScope() var stashUrl by remember { mutableStateOf("") } var stashKey by remember { mutableStateOf("") } LaunchedEffect(Unit) { stashUrl = settings.stashBaseUrlNow() stashKey = settings.stashApiKeyNow() } var newRulePattern by remember { mutableStateOf("") } var newRuleAction by remember { mutableStateOf(FilterAction.MUTE) } var actionMenuExpanded by remember { mutableStateOf(false) } val rules by db.filterRuleDao().observeAll().collectAsState(initial = emptyList()) Scaffold( topBar = { TopAppBar( title = { Text("Settings") }, navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.Default.ArrowBack, contentDescription = "Back") } } ) } ) { padding -> LazyColumn(modifier = Modifier.fillMaxWidth().padding(padding)) { item { Column(modifier = Modifier.padding(16.dp)) { Text("FeeDreader", style = MaterialTheme.typography.titleMedium) Spacer(modifier = Modifier.height(4.dp)) Text( "If a site paywalls its own feed, that's the site's call. But this reader " + "won't go out of its way to help a soft paywall block you from reading " + "something you already subscribed to get -- reader mode extracts the " + "article you were sent, nothing more, nothing sneaky.", style = MaterialTheme.typography.bodyMedium ) } HorizontalDivider() } item { Column(modifier = Modifier.padding(16.dp)) { Text("Stash", style = MaterialTheme.typography.titleMedium) Spacer(modifier = Modifier.height(8.dp)) OutlinedTextField( value = stashUrl, onValueChange = { stashUrl = it }, label = { Text("Base URL") }, placeholder = { Text("https://stash.example.com") }, modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(8.dp)) OutlinedTextField( value = stashKey, onValueChange = { stashKey = it }, label = { Text("API key") }, modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(8.dp)) Button( onClick = { scope.launch { settings.setStash(stashUrl, stashKey) } }, modifier = Modifier.fillMaxWidth() ) { Text("Save") } } HorizontalDivider() } item { Column(modifier = Modifier.padding(16.dp)) { Text("Filter rules", style = MaterialTheme.typography.titleMedium) Spacer(modifier = Modifier.height(8.dp)) Row(modifier = Modifier.fillMaxWidth()) { OutlinedTextField( value = newRulePattern, onValueChange = { newRulePattern = it }, label = { Text("Keyword") }, modifier = Modifier.weight(1f) ) Spacer(modifier = Modifier.width(8.dp)) Box { Button(onClick = { actionMenuExpanded = true }) { Text(if (newRuleAction == FilterAction.MUTE) "Mute" else "Highlight") } DropdownMenu(expanded = actionMenuExpanded, onDismissRequest = { actionMenuExpanded = false }) { DropdownMenuItem(text = { Text("Mute") }, onClick = { newRuleAction = FilterAction.MUTE; actionMenuExpanded = false }) DropdownMenuItem(text = { Text("Highlight") }, onClick = { newRuleAction = FilterAction.HIGHLIGHT; actionMenuExpanded = false }) } } } Spacer(modifier = Modifier.height(8.dp)) Button( enabled = newRulePattern.isNotBlank(), onClick = { scope.launch { db.filterRuleDao().insert( FilterRuleEntity( pattern = newRulePattern.trim(), action = newRuleAction, createdAt = System.currentTimeMillis() ) ) newRulePattern = "" } }, modifier = Modifier.fillMaxWidth() ) { Text("Add rule") } } } items(rules, key = { it.id }) { rule -> ListItem( headlineContent = { Text(rule.pattern) }, supportingContent = { Text(if (rule.action == FilterAction.MUTE) "Mute" else "Highlight") }, trailingContent = { IconButton(onClick = { scope.launch { db.filterRuleDao().delete(rule.id) } }) { Icon(Icons.Default.Delete, contentDescription = "Delete rule") } } ) HorizontalDivider() } } } }