From fe877f36479a9e23f02eb44ec965e1031f7b9652 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 16 Mar 2026 19:03:54 +0000 Subject: [PATCH 1/2] Improve add relay UI with inline add button and paste support Replace the separate "Add" button below the text field with a trailing icon button (Add icon) inside the OutlinedTextField, matching the existing pattern in EditConfigurationScreen. Also add a leading paste icon button for quickly pasting relay URLs from clipboard, and use the localized wss:// label instead of the hardcoded "Relay" string. https://claude.ai/code/session_01PNCVKFSSbD1zSWYhjsoo8S --- .../ui/DefaultProfileRelaysScreen.kt | 93 ++++++++++++------- .../ui/actions/EditRelaysDialog.kt | 90 ++++++++++++------ 2 files changed, 121 insertions(+), 62 deletions(-) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/DefaultProfileRelaysScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/DefaultProfileRelaysScreen.kt index 53ca8f21..d17283e3 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/DefaultProfileRelaysScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/DefaultProfileRelaysScreen.kt @@ -8,6 +8,12 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.ContentPaste +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Surface @@ -20,6 +26,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction @@ -33,7 +40,6 @@ import com.greenart7c3.nostrsigner.R import com.greenart7c3.nostrsigner.models.Account import com.greenart7c3.nostrsigner.service.TrustScoreService import com.greenart7c3.nostrsigner.ui.actions.onAddRelay -import com.greenart7c3.nostrsigner.ui.components.AmberButton import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -44,6 +50,7 @@ fun DefaultProfileRelaysScreen( ) { val scope = rememberCoroutineScope() val context = LocalContext.current + val clipboardManager = LocalClipboard.current val textFieldRelay = remember { mutableStateOf(TextFieldValue("")) } @@ -137,42 +144,64 @@ fun DefaultProfileRelaysScreen( }, ), label = { - Text("Relay") + Text(stringResource(R.string.wss)) }, - ) - - AmberButton( - modifier = Modifier - .fillMaxWidth(), - onClick = { - scope.launch(Dispatchers.IO) { - onAddRelay( - textFieldRelay, - isLoading, - relays2, - scope, - account, - context, - shouldCheckForBunker = false, - onDone = { - isLoading.value = true - Amber.instance.settings = Amber.instance.settings.copy( - defaultProfileRelays = relays2, - ) - LocalPreferences.saveSettingsToEncryptedStorage(Amber.instance.settings) - scope.launch(Dispatchers.IO) { - if (!BuildFlavorChecker.isOfflineFlavor()) { - Amber.instance.checkForNewRelaysAndUpdateAllFilters() - isLoading.value = false - } else { - isLoading.value = false - } + leadingIcon = { + IconButton( + onClick = { + scope.launch { + val text = clipboardManager.getClipEntry()?.clipData?.getItemAt(0)?.text?.toString() + if (!text.isNullOrBlank()) { + textFieldRelay.value = TextFieldValue(text) } - }, + } + }, + ) { + Icon( + Icons.Default.ContentPaste, + contentDescription = stringResource(R.string.paste_from_clipboard), + ) + } + }, + trailingIcon = { + IconButton( + colors = IconButtonDefaults.iconButtonColors().copy( + containerColor = MaterialTheme.colorScheme.primary, + ), + onClick = { + scope.launch(Dispatchers.IO) { + onAddRelay( + textFieldRelay, + isLoading, + relays2, + scope, + account, + context, + shouldCheckForBunker = false, + onDone = { + Amber.instance.settings = Amber.instance.settings.copy( + defaultProfileRelays = relays2, + ) + LocalPreferences.saveSettingsToEncryptedStorage(Amber.instance.settings) + scope.launch(Dispatchers.IO) { + if (!BuildFlavorChecker.isOfflineFlavor()) { + Amber.instance.checkForNewRelaysAndUpdateAllFilters() + isLoading.value = false + } else { + isLoading.value = false + } + } + }, + ) + } + }, + ) { + Icon( + Icons.Default.Add, + contentDescription = stringResource(R.string.add), ) } }, - text = stringResource(R.string.add), ) LazyColumn( diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/EditRelaysDialog.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/EditRelaysDialog.kt index 7defe116..e4d7b3f8 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/EditRelaysDialog.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/EditRelaysDialog.kt @@ -16,7 +16,13 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.ContentPaste import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Surface @@ -34,6 +40,7 @@ import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction @@ -96,6 +103,7 @@ fun DefaultRelaysScreen( ) { val scope = rememberCoroutineScope() val context = LocalContext.current + val clipboardManager = LocalClipboard.current val textFieldRelay = remember { mutableStateOf(TextFieldValue("")) } val isLoading = remember { mutableStateOf(false) } val relays2 = remember { mutableStateListOf(*Amber.instance.settings.defaultRelays.toTypedArray()) } @@ -207,41 +215,63 @@ fun DefaultRelaysScreen( }, ), label = { - Text("Relay") + Text(stringResource(R.string.wss)) }, - ) - - AmberButton( - modifier = Modifier - .fillMaxWidth(), - onClick = { - scope.launch(Dispatchers.IO) { - onAddRelay( - textFieldRelay, - isLoading, - relays2, - scope, - account, - context, - onDone = { - isLoading.value = true - Amber.instance.settings = Amber.instance.settings.copy( - defaultRelays = relays2, - ) - LocalPreferences.saveSettingsToEncryptedStorage(Amber.instance.settings) - scope.launch(Dispatchers.IO) { - if (!BuildFlavorChecker.isOfflineFlavor()) { - Amber.instance.checkForNewRelaysAndUpdateAllFilters() - isLoading.value = false - } else { - isLoading.value = false - } + leadingIcon = { + IconButton( + onClick = { + scope.launch { + val text = clipboardManager.getClipEntry()?.clipData?.getItemAt(0)?.text?.toString() + if (!text.isNullOrBlank()) { + textFieldRelay.value = TextFieldValue(text) } - }, + } + }, + ) { + Icon( + Icons.Default.ContentPaste, + contentDescription = stringResource(R.string.paste_from_clipboard), + ) + } + }, + trailingIcon = { + IconButton( + colors = IconButtonDefaults.iconButtonColors().copy( + containerColor = MaterialTheme.colorScheme.primary, + ), + onClick = { + scope.launch(Dispatchers.IO) { + onAddRelay( + textFieldRelay, + isLoading, + relays2, + scope, + account, + context, + onDone = { + Amber.instance.settings = Amber.instance.settings.copy( + defaultRelays = relays2, + ) + LocalPreferences.saveSettingsToEncryptedStorage(Amber.instance.settings) + scope.launch(Dispatchers.IO) { + if (!BuildFlavorChecker.isOfflineFlavor()) { + Amber.instance.checkForNewRelaysAndUpdateAllFilters() + isLoading.value = false + } else { + isLoading.value = false + } + } + }, + ) + } + }, + ) { + Icon( + Icons.Default.Add, + contentDescription = stringResource(R.string.add), ) } }, - text = stringResource(R.string.add), ) LazyColumn( From e6ce5979210fcc8974c5bb5b6bb3beba9d254148 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 16 Mar 2026 19:29:46 +0000 Subject: [PATCH 2/2] Add paste button and fix hardcoded string in NewNsecBunkerScreen Add leading paste icon button to the relay text field for clipboard pasting, matching the pattern used in the other relay screens. Also fix the hardcoded "Testing the relay..." loading text to use the existing string resource. https://claude.ai/code/session_01PNCVKFSSbD1zSWYhjsoo8S --- .../nostrsigner/ui/NewNsecBunkerScreen.kt | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/NewNsecBunkerScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/NewNsecBunkerScreen.kt index a164081d..ab254236 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/NewNsecBunkerScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/NewNsecBunkerScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.ContentPaste import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonDefaults @@ -68,6 +69,7 @@ fun NewNsecBunkerScreen( val secret = remember { mutableStateOf(UUID.randomUUID().toString()) } var name by remember { mutableStateOf(TextFieldValue(AnnotatedString(""))) } val context = LocalContext.current + val clipboardManager = LocalClipboard.current val relays = remember { @@ -99,7 +101,7 @@ fun NewNsecBunkerScreen( if (isLoading.value) { CenterCircularProgressIndicator( modifier = Modifier, - text = "Testing the relay...", + text = stringResource(R.string.testing_relay), ) } else { Column( @@ -173,6 +175,23 @@ fun NewNsecBunkerScreen( label = { Text(stringResource(R.string.wss)) }, + leadingIcon = { + IconButton( + onClick = { + scope.launch { + val text = clipboardManager.getClipEntry()?.clipData?.getItemAt(0)?.text?.toString() + if (!text.isNullOrBlank()) { + textFieldRelay.value = TextFieldValue(text) + } + } + }, + ) { + Icon( + Icons.Default.ContentPaste, + contentDescription = stringResource(R.string.paste_from_clipboard), + ) + } + }, trailingIcon = { IconButton( colors = IconButtonDefaults.iconButtonColors().copy(