diff --git a/app/src/main/kotlin/com/wisp/app/ui/screen/FeedScreen.kt b/app/src/main/kotlin/com/wisp/app/ui/screen/FeedScreen.kt index 595da92..b87faaf 100644 --- a/app/src/main/kotlin/com/wisp/app/ui/screen/FeedScreen.kt +++ b/app/src/main/kotlin/com/wisp/app/ui/screen/FeedScreen.kt @@ -34,7 +34,6 @@ import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit -import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.KeyboardArrowDown import androidx.compose.material.icons.filled.KeyboardArrowUp import androidx.compose.material3.AlertDialog @@ -292,7 +291,6 @@ fun FeedScreen( val newNotesButtonHidden by viewModel.newNotesButtonHidden.collectAsState() val initLoadingState by viewModel.initLoadingState.collectAsState() val relayFeedStatus by viewModel.relayFeedStatus.collectAsState() - val localRelayConfig by viewModel.keyRepo.localRelayFlow.collectAsState() val pendingFirstFollow by viewModel.pendingFirstFollow.collectAsState() val firstFollowCheckDone by viewModel.firstFollowCheckDone.collectAsState() val zapInProgress by viewModel.zapInProgress.collectAsState() @@ -466,7 +464,6 @@ fun FeedScreen( favoriteRelays = favoriteRelays, relaySets = ownRelaySets, relayInfoRepo = viewModel.relayInfoRepo, - localRelayUrl = localRelayConfig?.let { if (it.enabled) it.url else null }, onSelect = { url -> viewModel.setSelectedRelay(url) viewModel.setFeedType(FeedType.RELAY) @@ -1670,7 +1667,6 @@ private fun RelayPickerDialog( favoriteRelays: List, relaySets: List, relayInfoRepo: RelayInfoRepository, - localRelayUrl: String? = null, onSelect: (String) -> Unit, onSelectRelaySet: (RelaySet) -> Unit, onCreateRelaySet: (String) -> Unit, @@ -1751,44 +1747,6 @@ private fun RelayPickerDialog( Spacer(Modifier.size(12.dp)) LazyColumn(modifier = Modifier.heightIn(max = 400.dp)) { - // Local relay — always first when configured - if (localRelayUrl != null) { - item(key = "local-relay") { - Surface( - onClick = { onSelect(localRelayUrl) }, - shape = RoundedCornerShape(8.dp), - color = MaterialTheme.colorScheme.surfaceVariant, - modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp) - ) { - Row( - modifier = Modifier.padding(horizontal = 12.dp, vertical = 10.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - Icons.Filled.Home, - contentDescription = null, - modifier = Modifier.size(24.dp), - tint = MaterialTheme.colorScheme.onSurfaceVariant - ) - Spacer(Modifier.width(8.dp)) - Column { - Text( - stringResource(R.string.local_relay_my_relay), - style = MaterialTheme.typography.bodyMedium, - fontWeight = FontWeight.Medium, - color = MaterialTheme.colorScheme.onSurface - ) - Text( - localRelayUrl.removePrefix("ws://").removePrefix("wss://").removeSuffix("/"), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - } - } - } - } - // Favorites section if (favoriteRelays.isNotEmpty()) { item { diff --git a/app/src/main/kotlin/com/wisp/app/ui/screen/RelayScreen.kt b/app/src/main/kotlin/com/wisp/app/ui/screen/RelayScreen.kt index 6145be5..3b49700 100644 --- a/app/src/main/kotlin/com/wisp/app/ui/screen/RelayScreen.kt +++ b/app/src/main/kotlin/com/wisp/app/ui/screen/RelayScreen.kt @@ -11,7 +11,6 @@ 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.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Add @@ -23,35 +22,25 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.RadioButton import androidx.compose.material3.Scaffold import androidx.compose.material3.ScrollableTabRow -import androidx.compose.material3.Switch import androidx.compose.material3.Tab import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import android.widget.Toast import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import com.wisp.app.relay.LocalRelayConfig -import com.wisp.app.relay.LocalRelayWritePolicy import com.wisp.app.relay.RelayConfig import com.wisp.app.relay.RelayPool import com.wisp.app.relay.RelaySetType import com.wisp.app.R -import com.wisp.app.ui.theme.wispSwitchColors import com.wisp.app.viewmodel.RelayViewModel @OptIn(ExperimentalMaterial3Api::class) @@ -69,7 +58,6 @@ fun RelayScreen( val searchRelays by viewModel.searchRelays.collectAsState() val blockedRelays by viewModel.blockedRelays.collectAsState() val newRelayUrl by viewModel.newRelayUrl.collectAsState() - val localRelayConfig by viewModel.localRelay.collectAsState() val tabs = RelaySetType.entries @@ -108,81 +96,53 @@ fun RelayScreen( } Column(modifier = Modifier.padding(16.dp)) { - if (selectedTab == RelaySetType.LOCAL) { - // Local tab: show URL input only when no relay is configured - if (localRelayConfig == null) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() - ) { - OutlinedTextField( - value = newRelayUrl, - onValueChange = { viewModel.updateNewRelayUrl(it) }, - label = { Text(stringResource(R.string.local_relay_url_hint)) }, - singleLine = true, - modifier = Modifier.weight(1f) - ) - Spacer(Modifier.width(8.dp)) - IconButton(onClick = { viewModel.addRelay() }) { - Icon(Icons.Default.Add, stringResource(R.string.cd_add_relay)) - } - } - Spacer(Modifier.height(8.dp)) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { + OutlinedTextField( + value = newRelayUrl, + onValueChange = { viewModel.updateNewRelayUrl(it) }, + label = { Text(stringResource(R.string.placeholder_relay_url)) }, + singleLine = true, + modifier = Modifier.weight(1f) + ) + Spacer(Modifier.width(8.dp)) + IconButton(onClick = { viewModel.addRelay() }) { + Icon(Icons.Default.Add, stringResource(R.string.cd_add_relay)) } + } - LocalRelayTab(localRelayConfig, viewModel) - } else { - // Network relay tabs: URL input + broadcast - Row( - verticalAlignment = Alignment.CenterVertically, + Spacer(Modifier.height(8.dp)) + + if (relayPool != null) { + val buttonLabel = when (selectedTab) { + RelaySetType.GENERAL -> stringResource(R.string.broadcast_nip65) + RelaySetType.DM -> stringResource(R.string.broadcast_dm_relays) + RelaySetType.SEARCH -> stringResource(R.string.broadcast_search_relays) + RelaySetType.BLOCKED -> stringResource(R.string.broadcast_blocked_relays) + } + val successMsg = stringResource(R.string.error_relay_broadcast) + val failureMsg = stringResource(R.string.error_broadcast_failed) + Button( + onClick = { + val ok = viewModel.publishRelayList(relayPool, signer = signer) + val msg = if (ok) successMsg else failureMsg + Toast.makeText(context, msg, Toast.LENGTH_SHORT).show() + }, modifier = Modifier.fillMaxWidth() ) { - OutlinedTextField( - value = newRelayUrl, - onValueChange = { viewModel.updateNewRelayUrl(it) }, - label = { Text(stringResource(R.string.placeholder_relay_url)) }, - singleLine = true, - modifier = Modifier.weight(1f) - ) - Spacer(Modifier.width(8.dp)) - IconButton(onClick = { viewModel.addRelay() }) { - Icon(Icons.Default.Add, stringResource(R.string.cd_add_relay)) - } + Text(buttonLabel) } + } - Spacer(Modifier.height(8.dp)) + Spacer(Modifier.height(16.dp)) - if (relayPool != null) { - val buttonLabel = when (selectedTab) { - RelaySetType.GENERAL -> stringResource(R.string.broadcast_nip65) - RelaySetType.DM -> stringResource(R.string.broadcast_dm_relays) - RelaySetType.SEARCH -> stringResource(R.string.broadcast_search_relays) - RelaySetType.BLOCKED -> stringResource(R.string.broadcast_blocked_relays) - RelaySetType.LOCAL -> "" // unreachable - } - val successMsg = stringResource(R.string.error_relay_broadcast) - val failureMsg = stringResource(R.string.error_broadcast_failed) - Button( - onClick = { - val ok = viewModel.publishRelayList(relayPool, signer = signer) - val msg = if (ok) successMsg else failureMsg - Toast.makeText(context, msg, Toast.LENGTH_SHORT).show() - }, - modifier = Modifier.fillMaxWidth() - ) { - Text(buttonLabel) - } - } - - Spacer(Modifier.height(16.dp)) - - when (selectedTab) { - RelaySetType.GENERAL -> GeneralRelayList(relays, viewModel) - RelaySetType.DM -> SimpleRelayList(dmRelays, viewModel) - RelaySetType.SEARCH -> SimpleRelayList(searchRelays, viewModel) - RelaySetType.BLOCKED -> SimpleRelayList(blockedRelays, viewModel) - RelaySetType.LOCAL -> {} // handled above - } + when (selectedTab) { + RelaySetType.GENERAL -> GeneralRelayList(relays, viewModel) + RelaySetType.DM -> SimpleRelayList(dmRelays, viewModel) + RelaySetType.SEARCH -> SimpleRelayList(searchRelays, viewModel) + RelaySetType.BLOCKED -> SimpleRelayList(blockedRelays, viewModel) } } } @@ -235,145 +195,6 @@ private fun GeneralRelayList(relays: List, viewModel: RelayViewMode } } -@Composable -private fun LocalRelayTab(config: LocalRelayConfig?, viewModel: RelayViewModel) { - if (config == null) return - - Column { - // URL + enabled toggle - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() - ) { - Column(modifier = Modifier.weight(1f)) { - Text( - text = config.url, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurface - ) - } - Switch( - checked = config.enabled, - onCheckedChange = { viewModel.toggleLocalRelayEnabled() }, - colors = wispSwitchColors() - ) - } - - Spacer(Modifier.height(12.dp)) - - // Write policy selector - Text( - text = stringResource(R.string.local_relay_write_policy), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - LocalRelayWritePolicy.entries.forEach { policy -> - Row(verticalAlignment = Alignment.CenterVertically) { - RadioButton( - selected = config.writePolicy == policy, - onClick = { viewModel.updateLocalRelayPolicy(policy) } - ) - Text( - text = when (policy) { - LocalRelayWritePolicy.OWN_NOTES -> stringResource(R.string.local_relay_own_notes) - LocalRelayWritePolicy.TAGGED -> stringResource(R.string.local_relay_tagged) - LocalRelayWritePolicy.ALL_NOTES -> stringResource(R.string.local_relay_all_notes) - }, - style = MaterialTheme.typography.bodyMedium - ) - } - } - - Spacer(Modifier.height(12.dp)) - - // Kind chips - Text( - text = stringResource(R.string.local_relay_kinds), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - Spacer(Modifier.height(4.dp)) - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - FilterChip( - selected = 1 in config.kinds, - onClick = { - val updated = if (1 in config.kinds) config.kinds - 1 else config.kinds + 1 - viewModel.updateLocalRelayKinds(updated) - }, - label = { Text(stringResource(R.string.local_relay_kind_notes)) } - ) - FilterChip( - selected = 1059 in config.kinds, - onClick = { - val updated = if (1059 in config.kinds) config.kinds - 1059 else config.kinds + 1059 - viewModel.updateLocalRelayKinds(updated) - }, - label = { Text(stringResource(R.string.local_relay_kind_dms)) } - ) - FilterChip( - selected = 9735 in config.kinds, - onClick = { - val updated = if (9735 in config.kinds) config.kinds - 9735 else config.kinds + 9735 - viewModel.updateLocalRelayKinds(updated) - }, - label = { Text(stringResource(R.string.local_relay_kind_zaps)) } - ) - } - - // Custom kind input - var customKindInput by remember { mutableStateOf("") } - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(top = 4.dp) - ) { - OutlinedTextField( - value = customKindInput, - onValueChange = { customKindInput = it.filter { c -> c.isDigit() } }, - label = { Text(stringResource(R.string.local_relay_add_kind)) }, - singleLine = true, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - modifier = Modifier.weight(1f) - ) - Spacer(Modifier.width(8.dp)) - IconButton(onClick = { - val kind = customKindInput.toIntOrNull() - if (kind != null && kind !in config.kinds) { - viewModel.updateLocalRelayKinds(config.kinds + kind) - customKindInput = "" - } - }) { - Icon(Icons.Default.Add, stringResource(R.string.cd_add_relay)) - } - } - - // Show custom kinds as removable chips - val customKinds = config.kinds - setOf(1, 1059, 9735) - if (customKinds.isNotEmpty()) { - Spacer(Modifier.height(4.dp)) - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - customKinds.forEach { kind -> - FilterChip( - selected = true, - onClick = { viewModel.updateLocalRelayKinds(config.kinds - kind) }, - label = { Text("Kind $kind") } - ) - } - } - } - - Spacer(Modifier.height(16.dp)) - TextButton( - onClick = { viewModel.removeRelay(config.url) }, - modifier = Modifier.fillMaxWidth() - ) { - Text( - stringResource(R.string.local_relay_remove), - color = MaterialTheme.colorScheme.error - ) - } - } -} - @Composable private fun SimpleRelayList(urls: List, viewModel: RelayViewModel) { LazyColumn {