diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnBackupScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnBackupScreen.kt index 9d57679de4..b3ce768968 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnBackupScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnBackupScreen.kt @@ -32,9 +32,6 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold @@ -56,11 +53,15 @@ import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.cancel +import com.vitorpamplona.amethyst.commons.resources.cordn_backup_section_export +import com.vitorpamplona.amethyst.commons.resources.cordn_backup_section_passphrase +import com.vitorpamplona.amethyst.commons.resources.cordn_backup_section_restore import com.vitorpamplona.amethyst.commons.resources.cordn_backup_title import com.vitorpamplona.amethyst.commons.ui.components.EmptyState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsSection import com.vitorpamplona.amethyst.ui.stringRes import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -191,57 +192,66 @@ fun CordnBackupScreen( color = MaterialTheme.colorScheme.onSurfaceVariant, ) - OutlinedTextField( - value = passphrase, - onValueChange = { - passphrase = it - error = null - }, - label = { Text(stringRes(R.string.cordn_backup_passphrase)) }, - visualTransformation = PasswordVisualTransformation(), - singleLine = true, - modifier = Modifier.fillMaxWidth(), - ) - - Card( - modifier = Modifier.fillMaxWidth(), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), - ) { - Column(Modifier.padding(12.dp)) { - Text(stringRes(R.string.cordn_backup_contents_title), style = MaterialTheme.typography.titleSmall) - Text(stringRes(R.string.cordn_backup_contents_body), style = MaterialTheme.typography.bodySmall) + // The passphrase is its own section because it governs both of the + // two below: the same word exports and restores, and a field + // floating above two unrelated-looking cards did not say so. + SettingsSection(Res.string.cordn_backup_section_passphrase) { + SettingsFormBlock { + OutlinedTextField( + value = passphrase, + onValueChange = { + passphrase = it + error = null + }, + label = { Text(stringRes(R.string.cordn_backup_passphrase)) }, + visualTransformation = PasswordVisualTransformation(), + singleLine = true, + modifier = Modifier.fillMaxWidth(), + ) } } - Button( - onClick = { saver.launch("cordn-backup.bin") }, - // No passphrase, no export. The file carries ratchet trees and - // private key material; there is no version of it that is safe - // to write unprotected. - enabled = !busy && passphrase.isNotBlank(), - modifier = Modifier.fillMaxWidth(), - ) { - // Key derivation here is scrypt, which is slow on purpose, so - // a greyed-out button was the only sign anything was happening - // for several seconds. - BusyLabel(busy, stringRes(R.string.cordn_backup_export)) + SettingsSection(Res.string.cordn_backup_section_export) { + SettingsFormBlock { + Text(stringRes(R.string.cordn_backup_contents_title), style = MaterialTheme.typography.titleSmall) + Text( + text = stringRes(R.string.cordn_backup_contents_body), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + Button( + onClick = { saver.launch("cordn-backup.bin") }, + // No passphrase, no export. The file carries ratchet + // trees and private key material; there is no version + // of it that is safe to write unprotected. + enabled = !busy && passphrase.isNotBlank(), + modifier = Modifier.fillMaxWidth(), + ) { + // Key derivation here is scrypt, which is slow on + // purpose, so a greyed-out button was the only sign + // anything was happening for several seconds. + BusyLabel(busy, stringRes(R.string.cordn_backup_export)) + } + } } - HorizontalDivider(Modifier.padding(vertical = 8.dp)) + SettingsSection(Res.string.cordn_backup_section_restore) { + SettingsFormBlock { + Text( + text = stringRes(R.string.cordn_backup_restore_body), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) - Text(stringRes(R.string.cordn_backup_restore_title), style = MaterialTheme.typography.titleSmall) - Text( - text = stringRes(R.string.cordn_backup_restore_body), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - - Button( - onClick = { picker.launch("*/*") }, - enabled = !busy && passphrase.isNotBlank(), - modifier = Modifier.fillMaxWidth(), - ) { - BusyLabel(busy, stringRes(R.string.cordn_backup_restore)) + Button( + onClick = { picker.launch("*/*") }, + enabled = !busy && passphrase.isNotBlank(), + modifier = Modifier.fillMaxWidth(), + ) { + BusyLabel(busy, stringRes(R.string.cordn_backup_restore)) + } + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt index e58176fc4c..0c9852fe31 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt @@ -36,7 +36,6 @@ import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.Card import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField @@ -60,6 +59,9 @@ import com.vitorpamplona.amethyst.commons.cordn.CordnCoordinatorDiscovery import com.vitorpamplona.amethyst.commons.cordn.DiscoveredCoordinator import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.cancel +import com.vitorpamplona.amethyst.commons.resources.cordn_coordinators_section_discover +import com.vitorpamplona.amethyst.commons.resources.cordn_coordinators_section_manual +import com.vitorpamplona.amethyst.commons.resources.cordn_coordinators_section_yours import com.vitorpamplona.amethyst.commons.resources.cordn_coordinators_title import com.vitorpamplona.amethyst.commons.ui.components.EmptyState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav @@ -68,6 +70,7 @@ import com.vitorpamplona.amethyst.model.cordn.CordnRuntime import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled import com.vitorpamplona.amethyst.ui.note.timeAgoNoDot import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsSection import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.quartz.cordn.spec00Coordinator.CoordinatorServerInfo import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer @@ -136,25 +139,39 @@ fun CordnCoordinatorsScreen( color = MaterialTheme.colorScheme.onSurfaceVariant, ) - coordinators.forEach { config -> - CoordinatorCard(config, runtime) + // Three sections rather than one column split by rules: what you + // already use, what is on offer, and the manual escape hatch. The + // dividers said "something else follows" without saying what. + SettingsSection(Res.string.cordn_coordinators_section_yours) { + SettingsFormBlock { + coordinators.forEach { config -> + CoordinatorCard(config, runtime) + } + + if (coordinators.isEmpty()) { + Text( + text = stringRes(R.string.cordn_coordinators_none), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } } - if (coordinators.isEmpty()) { - Text( - text = stringRes(R.string.cordn_coordinators_none), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) + SettingsSection(Res.string.cordn_coordinators_section_discover) { + SettingsFormBlock { + DiscoverCoordinators(runtime, accountViewModel, coordinators.map { it.pubKey }.toSet()) + } } - HorizontalDivider(Modifier.padding(vertical = 8.dp)) - - DiscoverCoordinators(runtime, accountViewModel, coordinators.map { it.pubKey }.toSet()) - - HorizontalDivider(Modifier.padding(vertical = 8.dp)) - - AddCoordinator(runtime) + // Last, and it reads as the fallback it is now that discovery is + // above it: pasting a 64-character key is what you do when nobody + // announced the one you were told to use. + SettingsSection(Res.string.cordn_coordinators_section_manual) { + SettingsFormBlock { + AddCoordinator(runtime) + } + } } } } @@ -308,7 +325,6 @@ private fun DiscoverCoordinators( var error by remember { mutableStateOf(null) } val failed = stringRes(R.string.cordn_coordinators_discover_failed) - Text(stringRes(R.string.cordn_coordinators_discover), style = MaterialTheme.typography.titleSmall) Text( text = stringRes(R.string.cordn_coordinators_discover_explainer), style = MaterialTheme.typography.bodySmall, @@ -446,8 +462,6 @@ private fun AddCoordinator(runtime: CordnRuntime) { } } - Text(stringRes(R.string.cordn_coordinators_add), style = MaterialTheme.typography.titleSmall) - OutlinedTextField( value = pubKeyInput, onValueChange = { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnKeyPackagesScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnKeyPackagesScreen.kt index 9658791bcf..046b96885d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnKeyPackagesScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnKeyPackagesScreen.kt @@ -53,6 +53,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.commons.cordn.CoordinatorConfig import com.vitorpamplona.amethyst.commons.resources.Res +import com.vitorpamplona.amethyst.commons.resources.cordn_keypackages_section import com.vitorpamplona.amethyst.commons.resources.cordn_keypackages_title import com.vitorpamplona.amethyst.commons.ui.components.EmptyState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav @@ -61,6 +62,7 @@ import com.vitorpamplona.amethyst.model.cordn.CordnKeyPackageRow import com.vitorpamplona.amethyst.model.cordn.CordnRuntime import com.vitorpamplona.amethyst.ui.pluralStringRes import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsSection import com.vitorpamplona.amethyst.ui.stringRes import kotlinx.coroutines.launch @@ -128,17 +130,29 @@ fun CordnKeyPackagesScreen( color = MaterialTheme.colorScheme.onSurfaceVariant, ) - if (coordinators.isEmpty()) { - Text( - text = stringRes(R.string.cordn_coordinators_none), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } + // One section holding every coordinator rather than one each: + // SettingsSection titles come from a StringResource and a + // coordinator's label is runtime text, so a section per + // coordinator is not something this primitive can say. The + // dividers stay, inside, where they now separate peers within a + // box instead of floating in open page. + SettingsSection(Res.string.cordn_keypackages_section) { + SettingsFormBlock { + if (coordinators.isEmpty()) { + Text( + text = stringRes(R.string.cordn_coordinators_none), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } - coordinators.forEach { config -> - CoordinatorKeyPackages(config, runtime) - HorizontalDivider() + coordinators.forEachIndexed { index, config -> + // Between, not after: a rule under the last one drew a + // line to nothing. + if (index > 0) HorizontalDivider() + CoordinatorKeyPackages(config, runtime) + } + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnLinkScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnLinkScreen.kt index 1d77752590..097b84adfb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnLinkScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnLinkScreen.kt @@ -60,11 +60,13 @@ import com.vitorpamplona.amethyst.commons.resources.cordn_link_no_coordinator import com.vitorpamplona.amethyst.commons.resources.cordn_link_not_joinable import com.vitorpamplona.amethyst.commons.resources.cordn_link_paste import com.vitorpamplona.amethyst.commons.resources.cordn_link_relays +import com.vitorpamplona.amethyst.commons.resources.cordn_link_section_input import com.vitorpamplona.amethyst.commons.resources.cordn_link_title import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.SimpleQrCodeScanner +import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsSection import com.vitorpamplona.amethyst.ui.stringRes import kotlinx.coroutines.launch import org.jetbrains.compose.resources.stringResource @@ -122,49 +124,56 @@ fun CordnLinkScreen( color = MaterialTheme.colorScheme.onSurfaceVariant, ) - OutlinedTextField( - value = input, - onValueChange = { - input = it - // Clearing on edit rather than re-parsing per keystroke: a - // half-typed ref is always invalid, and showing that while - // someone is still pasting is noise, not feedback. - inspection = null - requestState = null - }, - label = { Text(stringResource(Res.string.cordn_link_field)) }, - singleLine = false, - modifier = Modifier.fillMaxWidth(), - ) - - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - Button( - onClick = { inspection = CordnLinkInspection.of(input) }, - enabled = input.isNotBlank(), - ) { - Text(stringResource(Res.string.cordn_link_inspect)) - } - OutlinedButton( - onClick = { - clipboard.getText()?.text?.let { + // The field, its buttons and the scanner are one thing to do; the + // verdict below is another. Boxing the input says where the screen + // asks something of you and where it answers. + SettingsSection(Res.string.cordn_link_section_input) { + SettingsFormBlock { + OutlinedTextField( + value = input, + onValueChange = { input = it - inspection = CordnLinkInspection.of(it) - } - }, - ) { - Text(stringResource(Res.string.cordn_link_paste)) - } - OutlinedButton(onClick = { scanning = true }) { - Text(stringRes(R.string.cordn_link_scan)) - } - if (input.isNotEmpty()) { - OutlinedButton( - onClick = { - input = "" + // Clearing on edit rather than re-parsing per keystroke: a + // half-typed ref is always invalid, and showing that while + // someone is still pasting is noise, not feedback. inspection = null + requestState = null }, - ) { - Text(stringResource(Res.string.cordn_link_clear)) + label = { Text(stringResource(Res.string.cordn_link_field)) }, + singleLine = false, + modifier = Modifier.fillMaxWidth(), + ) + + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Button( + onClick = { inspection = CordnLinkInspection.of(input) }, + enabled = input.isNotBlank(), + ) { + Text(stringResource(Res.string.cordn_link_inspect)) + } + OutlinedButton( + onClick = { + clipboard.getText()?.text?.let { + input = it + inspection = CordnLinkInspection.of(it) + } + }, + ) { + Text(stringResource(Res.string.cordn_link_paste)) + } + OutlinedButton(onClick = { scanning = true }) { + Text(stringRes(R.string.cordn_link_scan)) + } + if (input.isNotEmpty()) { + OutlinedButton( + onClick = { + input = "" + inspection = null + }, + ) { + Text(stringResource(Res.string.cordn_link_clear)) + } + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt index dfe6af4c49..db9b3d4d4e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt @@ -31,7 +31,6 @@ import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.Card -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField @@ -71,6 +70,8 @@ import com.vitorpamplona.amethyst.commons.resources.cordn_migrate_receive import com.vitorpamplona.amethyst.commons.resources.cordn_migrate_replaces_warning import com.vitorpamplona.amethyst.commons.resources.cordn_migrate_scan import com.vitorpamplona.amethyst.commons.resources.cordn_migrate_scan_this +import com.vitorpamplona.amethyst.commons.resources.cordn_migrate_section_receive +import com.vitorpamplona.amethyst.commons.resources.cordn_migrate_section_send import com.vitorpamplona.amethyst.commons.resources.cordn_migrate_sign_in_first import com.vitorpamplona.amethyst.commons.resources.cordn_migrate_title import com.vitorpamplona.amethyst.commons.ui.components.EmptyState @@ -81,6 +82,7 @@ import com.vitorpamplona.amethyst.model.cordn.CordnRuntime import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.QrCodeDrawer import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.SimpleQrCodeScanner +import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsSection import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.quartz.cordn.appMultiDevice.CordnHandoffCode import kotlinx.coroutines.launch @@ -146,11 +148,21 @@ fun CordnMigrateScreen( color = MaterialTheme.colorScheme.onSurfaceVariant, ) - SendSide(runtime, accountViewModel) + // Two boxes, because they are two different devices' jobs and + // only one of them is yours today. A rule between them said they + // were separate without saying which was which — and doing the + // wrong one here hands your account to another phone. + SettingsSection(Res.string.cordn_migrate_section_send) { + SettingsFormBlock { + SendSide(runtime, accountViewModel) + } + } - HorizontalDivider(Modifier.padding(vertical = 8.dp)) - - ReceiveSide(runtime, accountViewModel) + SettingsSection(Res.string.cordn_migrate_section_receive) { + SettingsFormBlock { + ReceiveSide(runtime, accountViewModel) + } + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/SettingsFormBlock.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/SettingsFormBlock.kt new file mode 100644 index 0000000000..ca8825fec0 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/SettingsFormBlock.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.cordn + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +/** + * Form content inside a `SettingsSection`. + * + * The section's card deliberately has no padding of its own: it is built for + * rows like `SettingsItem`, which carry theirs so a divider can run edge to + * edge between them. cordn's pages are forms rather than row lists — text + * fields, explainers and buttons — and those need the inset the rows would + * otherwise have supplied. + * + * One helper rather than a `Modifier.padding(16.dp)` repeated down five + * screens, so the pages stay identical to each other as they change. + */ +@Composable +fun SettingsFormBlock(content: @Composable ColumnScope.() -> Unit) { + Column( + modifier = Modifier.fillMaxWidth().padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + content = content, + ) +} diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index d826420159..b2ce11a2a5 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -367,7 +367,6 @@ Declining is permanent. Getting back in means being invited again. Yesterday Add a coordinator in Settings to start using cordn groups. - Coordinators announcing themselves Reads announcements your relays already carry. No coordinator is contacted, so none of them learns you looked. Look for coordinators Add @@ -402,7 +401,6 @@ A coordinator orders one group\'s messages and is the only thing that can. Losing it loses the ordering, and a second one does not mirror the first — so these are named things you choose, not interchangeable relays. No coordinators yet. Unnamed coordinator - Add a coordinator Add Could not open that coordinator. A name for it (yours, optional) diff --git a/commonsUI/src/commonMain/composeResources/values/strings.xml b/commonsUI/src/commonMain/composeResources/values/strings.xml index c32b527459..11895c0511 100644 --- a/commonsUI/src/commonMain/composeResources/values/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values/strings.xml @@ -5401,6 +5401,16 @@ Groups delivered by a coordinator you choose, instead of by relays. Inspect a group link See what a cordn1… link points at before you act on it + Move off this phone + Take over from another phone + Published key packages + The link + Coordinators you use + Announcing themselves + Add one by hand + Passphrase + Make a backup + Restore from a backup The coordinator This device Coordinators