commit cc1bb85b257dbf308780b2721ea2d27d23261ca5
parent ec8162461c197159ed60f648c21ef0a93c75f81d
Author: triesap <tyson@radroots.org>
Date: Sun, 7 Jun 2026 14:55:56 -0700
ui: bootstrap account settings view
Diffstat:
5 files changed, 438 insertions(+), 12 deletions(-)
diff --git a/crates/desktop/src/source_guards.rs b/crates/desktop/src/source_guards.rs
@@ -43,6 +43,16 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[
"account-more",
"account-profile-change-photo",
"account-profile-remove-photo",
+ "account-settings-add-relay",
+ "account-settings-blossom-product-photos",
+ "account-settings-blossom-profile-farm-media",
+ "account-settings-relay-localhost-8080",
+ "account-settings-relay-localhost-8081",
+ "account-settings-remove-relay-localhost-8080",
+ "account-settings-remove-relay-localhost-8081",
+ "account-settings-reset-blossom",
+ "account-settings-reset-relays",
+ "account-settings-save",
"account-farm-continue-location",
"account-farm-save-draft",
"account-farm-view-profile",
@@ -184,6 +194,7 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[
"home-today-open-products-low-stock",
"home-products-scroll",
"home-today-scroll",
+ "http://localhost:8082",
"today-reminder-chip",
"https://auth.example/challenge",
"identity",
@@ -342,6 +353,8 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[
"switch_relays",
"startup-title-radroots",
"startup-title-starting",
+ "ws://localhost:8080",
+ "ws://localhost:8081",
"wss://relay.example",
"wss://relay.radroots.example",
"{currency_code} {dollars}.{cents:02}",
@@ -363,7 +376,7 @@ const REQUIRED_WINDOW_COPY_KEYS: &[&str] = &[
"AppTextKey::AccountTabProfile",
"AppTextKey::AccountTabFarmDetails",
"AppTextKey::AccountTabPreferences",
- "AppTextKey::AccountTabSecurity",
+ "AppTextKey::AccountTabSettings",
"AppTextKey::AccountNotImplemented",
"AppTextKey::AccountProfilePersonalDetailsTitle",
"AppTextKey::AccountProfilePictureLabel",
@@ -440,6 +453,26 @@ const REQUIRED_WINDOW_COPY_KEYS: &[&str] = &[
"AppTextKey::AccountFarmDetailsFarmTypeNurseryPlantFarm",
"AppTextKey::AccountFarmDetailsFarmTypeMixedFarm",
"AppTextKey::AccountFarmDetailsFarmTypeOther",
+ "AppTextKey::AccountSettingsTitle",
+ "AppTextKey::AccountSettingsNostrRelaysTitle",
+ "AppTextKey::AccountSettingsNostrRelaysHelper",
+ "AppTextKey::AccountSettingsRelayAccessReadWrite",
+ "AppTextKey::AccountSettingsRelayAccessReadOnly",
+ "AppTextKey::AccountSettingsRemoveRelayAction",
+ "AppTextKey::AccountSettingsAddRelayLabel",
+ "AppTextKey::AccountSettingsAddRelayPlaceholder",
+ "AppTextKey::AccountSettingsAddRelayAction",
+ "AppTextKey::AccountSettingsResetRelaysAction",
+ "AppTextKey::AccountSettingsDefaultRelaysNote",
+ "AppTextKey::AccountSettingsBlossomServerTitle",
+ "AppTextKey::AccountSettingsBlossomServerHelper",
+ "AppTextKey::AccountSettingsBlossomServerUrlLabel",
+ "AppTextKey::AccountSettingsBlossomProductPhotosLabel",
+ "AppTextKey::AccountSettingsBlossomProfileFarmMediaLabel",
+ "AppTextKey::AccountSettingsResetBlossomServerAction",
+ "AppTextKey::AccountSettingsBlossomConnectionHealthy",
+ "AppTextKey::AccountSettingsBlossomUploadsAvailable",
+ "AppTextKey::AccountSettingsSaveChangesAction",
"AppTextKey::HomeSetupBackAction",
"AppTextKey::HomeSetupBrowseMarketplaceAction",
"AppTextKey::HomeSetupConnectSignerAction",
diff --git a/crates/desktop/src/window.rs b/crates/desktop/src/window.rs
@@ -30,12 +30,12 @@ use radroots_app_sync::{
SyncConflictKind, SyncConflictResolutionStatus, SyncConflictSeverity,
};
use radroots_app_ui::{
- APP_UI_THEME, AppCheckboxFieldSpec, AppFormFieldSpec,
+ APP_UI_THEME, AppCheckboxFieldSpec, AppFormFieldSpec, AppIconButtonSpec,
AppSegmentButtonIconSpec as IconSegmentButtonSpec, AppUnderlineTabSpec, LabelValueRow,
SettingsPreferencesGeneralRowState, app_button_account_selector_row as account_selector_row,
app_button_card, app_button_choice as choice_button,
- app_button_compact as action_button_compact, app_button_list_row as list_row_button,
- app_button_primary as action_button_primary,
+ app_button_compact as action_button_compact, app_button_icon as action_icon_button,
+ app_button_list_row as list_row_button, app_button_primary as action_button_primary,
app_button_primary_disabled as action_button_primary_disabled,
app_button_primary_full_width as action_button_primary_full_width,
app_button_secondary as action_button, app_button_secondary_disabled as action_button_disabled,
@@ -177,7 +177,7 @@ enum AccountTab {
Profile,
FarmDetails,
Preferences,
- Security,
+ Settings,
}
impl AccountTab {
@@ -185,7 +185,7 @@ impl AccountTab {
Self::Profile,
Self::FarmDetails,
Self::Preferences,
- Self::Security,
+ Self::Settings,
];
const fn text_key(self) -> AppTextKey {
@@ -193,14 +193,15 @@ impl AccountTab {
Self::Profile => AppTextKey::AccountTabProfile,
Self::FarmDetails => AppTextKey::AccountTabFarmDetails,
Self::Preferences => AppTextKey::AccountTabPreferences,
- Self::Security => AppTextKey::AccountTabSecurity,
+ Self::Settings => AppTextKey::AccountTabSettings,
}
}
const fn panel_text_key(self) -> AppTextKey {
match self {
Self::Profile | Self::FarmDetails => self.text_key(),
- Self::Preferences | Self::Security => AppTextKey::AccountNotImplemented,
+ Self::Preferences => AppTextKey::AccountNotImplemented,
+ Self::Settings => AppTextKey::AccountSettingsTitle,
}
}
@@ -398,6 +399,26 @@ impl AccountFarmProfileFormState {
}
}
+#[derive(Clone)]
+struct AccountSettingsFormState {
+ add_relay_input: Entity<InputState>,
+ blossom_server_input: Entity<InputState>,
+}
+
+impl AccountSettingsFormState {
+ fn new(window: &mut Window, cx: &mut Context<HomeView>) -> Self {
+ Self {
+ add_relay_input: cx.new(|cx| {
+ InputState::new(window, cx)
+ .placeholder(app_text(AppTextKey::AccountSettingsAddRelayPlaceholder))
+ }),
+ blossom_server_input: cx.new(|cx| {
+ InputState::new(window, cx).default_value(ACCOUNT_SETTINGS_DEFAULT_BLOSSOM_SERVER)
+ }),
+ }
+ }
+}
+
fn account_farm_profile_select_state(
value_keys: &[AppTextKey],
window: &mut Window,
@@ -547,6 +568,7 @@ pub struct HomeView {
selected_account_tab: AccountTab,
account_profile_form: Option<AccountProfileFormState>,
account_farm_profile_form: Option<AccountFarmProfileFormState>,
+ account_settings_form: Option<AccountSettingsFormState>,
account_farm_profile_textarea_wrap_ready: bool,
account_farm_profile_textarea_wrap_requested: bool,
relay_client: Option<RadrootsNostrClient>,
@@ -671,6 +693,7 @@ impl HomeView {
selected_account_tab: AccountTab::default(),
account_profile_form: None,
account_farm_profile_form: None,
+ account_settings_form: None,
account_farm_profile_textarea_wrap_ready: false,
account_farm_profile_textarea_wrap_requested: false,
relay_client: None,
@@ -5236,9 +5259,13 @@ impl HomeView {
account_farm_profile_panel(&form, self.account_farm_profile_textarea_wrap_ready, cx)
.into_any_element()
}
- AccountTab::Preferences | AccountTab::Security => {
+ AccountTab::Preferences => {
account_placeholder_panel(selected_tab.panel_text_key()).into_any_element()
}
+ AccountTab::Settings => {
+ let form = self.account_settings_form(window, cx).clone();
+ account_settings_panel(&form, cx).into_any_element()
+ }
};
app_stack_v(APP_UI_THEME.shells.home_stack_gap_px)
@@ -5292,6 +5319,21 @@ impl HomeView {
form
}
+ fn account_settings_form(
+ &mut self,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> &AccountSettingsFormState {
+ if self.account_settings_form.is_none() {
+ self.account_settings_form = Some(AccountSettingsFormState::new(window, cx));
+ }
+
+ let Some(form) = self.account_settings_form.as_ref() else {
+ unreachable!();
+ };
+ form
+ }
+
fn prepare_account_farm_profile_textarea_wrap(
&mut self,
window: &mut Window,
@@ -9278,6 +9320,9 @@ fn account_profile_labeled_control(
const ACCOUNT_FORM_CONTROL_HEIGHT_PX: f32 = 28.0;
const ACCOUNT_FORM_CONTROL_RADIUS_PX: f32 = 8.0;
+const ACCOUNT_SETTINGS_DEFAULT_BLOSSOM_SERVER: &str = "http://localhost:8082";
+const ACCOUNT_SETTINGS_RELAY_LOCALHOST_8080: &str = "ws://localhost:8080";
+const ACCOUNT_SETTINGS_RELAY_LOCALHOST_8081: &str = "ws://localhost:8081";
fn account_form_text_input(input: &Entity<InputState>) -> impl IntoElement {
gpui::Styled::h(
@@ -9816,6 +9861,301 @@ fn account_farm_profile_action_row(cx: &mut Context<HomeView>) -> impl IntoEleme
)
}
+fn account_settings_panel(
+ form: &AccountSettingsFormState,
+ cx: &mut Context<HomeView>,
+) -> impl IntoElement {
+ app_stack_v(APP_UI_THEME.shells.home_stack_gap_px)
+ .w_full()
+ .child(account_section_heading(AppTextKey::AccountSettingsTitle))
+ .child(
+ div()
+ .w_full()
+ .flex()
+ .items_start()
+ .gap(px(APP_UI_THEME.shells.home_stack_gap_px))
+ .child(
+ div()
+ .flex_1()
+ .min_w_0()
+ .child(account_settings_nostr_relays_card(form, cx)),
+ )
+ .child(
+ div()
+ .flex_basis(relative(0.4))
+ .min_w(px(320.0))
+ .child(account_settings_blossom_server_card(form, cx)),
+ ),
+ )
+ .child(action_button_primary_full_width(
+ "account-settings-save",
+ app_shared_text(AppTextKey::AccountSettingsSaveChangesAction),
+ |_, _, _| {},
+ cx,
+ ))
+}
+
+fn account_settings_nostr_relays_card(
+ form: &AccountSettingsFormState,
+ cx: &mut Context<HomeView>,
+) -> impl IntoElement {
+ account_settings_card(
+ app_stack_v(APP_UI_THEME.shells.home_stack_gap_px)
+ .w_full()
+ .child(account_farm_profile_title_block(
+ AppTextKey::AccountSettingsNostrRelaysTitle,
+ AppTextKey::AccountSettingsNostrRelaysHelper,
+ ))
+ .child(account_settings_relay_list(cx))
+ .child(account_settings_add_relay_controls(form, cx))
+ .child(div().w(px(160.0)).child(action_button_full_width(
+ "account-settings-reset-relays",
+ app_shared_text(AppTextKey::AccountSettingsResetRelaysAction),
+ |_, _, _| {},
+ cx,
+ )))
+ .child(account_settings_helper_note(
+ AppTextKey::AccountSettingsDefaultRelaysNote,
+ )),
+ )
+}
+
+fn account_settings_relay_list(cx: &mut Context<HomeView>) -> impl IntoElement {
+ app_stack_v(0.0)
+ .w_full()
+ .border_1()
+ .border_color(rgb(APP_UI_THEME.foundation.surfaces.divider))
+ .rounded(px(APP_UI_THEME.foundation.radii.medium_px))
+ .overflow_hidden()
+ .child(account_settings_relay_row(
+ "account-settings-relay-localhost-8080",
+ "account-settings-remove-relay-localhost-8080",
+ ACCOUNT_SETTINGS_RELAY_LOCALHOST_8080,
+ AppTextKey::AccountSettingsRelayAccessReadWrite,
+ true,
+ cx,
+ ))
+ .child(account_settings_relay_row(
+ "account-settings-relay-localhost-8081",
+ "account-settings-remove-relay-localhost-8081",
+ ACCOUNT_SETTINGS_RELAY_LOCALHOST_8081,
+ AppTextKey::AccountSettingsRelayAccessReadOnly,
+ true,
+ cx,
+ ))
+}
+
+fn account_settings_relay_row(
+ checkbox_id: &'static str,
+ remove_id: &'static str,
+ relay_url: &'static str,
+ access_key: AppTextKey,
+ enabled: bool,
+ cx: &mut Context<HomeView>,
+) -> impl IntoElement {
+ div()
+ .w_full()
+ .min_h(px(52.0))
+ .px(px(APP_UI_THEME.foundation.spacing.medium_px))
+ .py(px(APP_UI_THEME.foundation.spacing.small_px))
+ .flex()
+ .items_center()
+ .gap(px(APP_UI_THEME.shells.home_stack_gap_px))
+ .child(div().flex_1().min_w_0().child(app_checkbox_field(
+ AppCheckboxFieldSpec::new(checkbox_id, relay_url, Option::<SharedString>::None),
+ enabled,
+ cx,
+ |_, _, _| {},
+ )))
+ .child(
+ div()
+ .w(px(104.0))
+ .text_size(px(APP_UI_THEME.foundation.typography.settings_row_text_px))
+ .text_color(rgb(APP_UI_THEME.foundation.text.secondary))
+ .child(app_shared_text(access_key)),
+ )
+ .child(action_icon_button(
+ AppIconButtonSpec::new(
+ remove_id,
+ app_shared_text(AppTextKey::AccountSettingsRemoveRelayAction),
+ IconName::Delete,
+ ),
+ |_, _, _| {},
+ cx,
+ ))
+}
+
+fn account_settings_add_relay_controls(
+ form: &AccountSettingsFormState,
+ cx: &mut Context<HomeView>,
+) -> impl IntoElement {
+ app_stack_v(APP_UI_THEME.foundation.spacing.tight_px)
+ .w_full()
+ .child(
+ div()
+ .w_full()
+ .text_size(px(APP_UI_THEME.foundation.typography.utility_title_text_px))
+ .font_weight(gpui::FontWeight::SEMIBOLD)
+ .text_color(rgb(APP_UI_THEME.foundation.text.secondary))
+ .child(app_shared_text(AppTextKey::AccountSettingsAddRelayLabel)),
+ )
+ .child(
+ div()
+ .w_full()
+ .flex()
+ .items_center()
+ .gap(px(APP_UI_THEME.shells.home_stack_gap_px))
+ .child(
+ div()
+ .flex_1()
+ .min_w_0()
+ .child(account_form_text_input(&form.add_relay_input)),
+ )
+ .child(action_button(
+ "account-settings-add-relay",
+ app_shared_text(AppTextKey::AccountSettingsAddRelayAction),
+ |_, _, _| {},
+ cx,
+ )),
+ )
+}
+
+fn account_settings_blossom_server_card(
+ form: &AccountSettingsFormState,
+ cx: &mut Context<HomeView>,
+) -> impl IntoElement {
+ account_settings_card(
+ app_stack_v(APP_UI_THEME.shells.home_stack_gap_px)
+ .w_full()
+ .child(account_farm_profile_title_block(
+ AppTextKey::AccountSettingsBlossomServerTitle,
+ AppTextKey::AccountSettingsBlossomServerHelper,
+ ))
+ .child(account_profile_labeled_control(
+ AppTextKey::AccountSettingsBlossomServerUrlLabel,
+ account_form_text_input(&form.blossom_server_input),
+ ))
+ .child(app_checkbox_field(
+ AppCheckboxFieldSpec::new(
+ "account-settings-blossom-product-photos",
+ app_shared_text(AppTextKey::AccountSettingsBlossomProductPhotosLabel),
+ Option::<SharedString>::None,
+ ),
+ true,
+ cx,
+ |_, _, _| {},
+ ))
+ .child(app_checkbox_field(
+ AppCheckboxFieldSpec::new(
+ "account-settings-blossom-profile-farm-media",
+ app_shared_text(AppTextKey::AccountSettingsBlossomProfileFarmMediaLabel),
+ Option::<SharedString>::None,
+ ),
+ true,
+ cx,
+ |_, _, _| {},
+ ))
+ .child(div().w(px(172.0)).child(action_button_full_width(
+ "account-settings-reset-blossom",
+ app_shared_text(AppTextKey::AccountSettingsResetBlossomServerAction),
+ |_, _, _| {},
+ cx,
+ )))
+ .child(account_settings_blossom_health_card()),
+ )
+}
+
+fn account_settings_blossom_health_card() -> impl IntoElement {
+ div()
+ .w_full()
+ .border_1()
+ .border_color(rgb(APP_UI_THEME.foundation.surfaces.divider))
+ .rounded(px(APP_UI_THEME.foundation.radii.medium_px))
+ .p(px(APP_UI_THEME.foundation.spacing.large_px))
+ .flex()
+ .items_center()
+ .justify_between()
+ .gap(px(APP_UI_THEME.shells.home_stack_gap_px))
+ .child(
+ div()
+ .flex()
+ .items_center()
+ .gap(px(APP_UI_THEME.foundation.spacing.medium_px))
+ .child(status_indicator(
+ APP_UI_THEME.components.app_status_indicator.online,
+ ))
+ .child(
+ app_stack_v(APP_UI_THEME.foundation.spacing.micro_px)
+ .min_w_0()
+ .child(
+ div()
+ .text_size(px(APP_UI_THEME
+ .foundation
+ .typography
+ .settings_row_text_px))
+ .font_weight(gpui::FontWeight::MEDIUM)
+ .text_color(rgb(APP_UI_THEME.foundation.text.primary))
+ .child(app_shared_text(
+ AppTextKey::AccountSettingsBlossomConnectionHealthy,
+ )),
+ )
+ .child(
+ div()
+ .text_size(px(APP_UI_THEME
+ .foundation
+ .typography
+ .settings_row_text_px))
+ .text_color(rgb(APP_UI_THEME.foundation.text.secondary))
+ .child(app_shared_text(
+ AppTextKey::AccountSettingsBlossomUploadsAvailable,
+ )),
+ ),
+ ),
+ )
+ .child(
+ Icon::new(IconName::CircleCheck)
+ .with_size(gpui_component::Size::Size(px(18.0)))
+ .text_color(rgb(APP_UI_THEME.components.app_status_indicator.online)),
+ )
+}
+
+fn account_settings_card(content: impl IntoElement) -> impl IntoElement {
+ div()
+ .w_full()
+ .border_1()
+ .border_color(rgb(APP_UI_THEME.foundation.surfaces.divider))
+ .rounded(px(APP_UI_THEME.foundation.radii.large_px))
+ .bg(transparent_black())
+ .child(
+ div()
+ .w_full()
+ .p(px(APP_UI_THEME.shells.home_card_padding_px))
+ .child(content),
+ )
+}
+
+fn account_settings_helper_note(text_key: AppTextKey) -> impl IntoElement {
+ div()
+ .w_full()
+ .flex()
+ .items_center()
+ .gap(px(APP_UI_THEME.foundation.spacing.small_px))
+ .child(
+ Icon::new(IconName::Info)
+ .with_size(gpui_component::Size::Size(px(14.0)))
+ .text_color(rgb(APP_UI_THEME.foundation.text.secondary)),
+ )
+ .child(
+ div()
+ .flex_1()
+ .min_w_0()
+ .text_size(px(APP_UI_THEME.foundation.typography.utility_title_text_px))
+ .line_height(relative(1.25))
+ .text_color(rgb(APP_UI_THEME.foundation.text.secondary))
+ .child(app_shared_text(text_key)),
+ )
+}
+
fn buyer_listings_feed(
section: PersonalSection,
rows: &[BuyerListingRow],
diff --git a/crates/i18n/src/keys.rs b/crates/i18n/src/keys.rs
@@ -31,7 +31,7 @@ define_app_text_keys! {
AccountTabProfile => "account.tab.profile",
AccountTabFarmDetails => "account.tab.farm_details",
AccountTabPreferences => "account.tab.preferences",
- AccountTabSecurity => "account.tab.security",
+ AccountTabSettings => "account.tab.settings",
AccountNotImplemented => "account.not_implemented",
AccountProfilePersonalDetailsTitle => "account.profile.personal_details.title",
AccountProfilePictureLabel => "account.profile.picture.label",
@@ -108,6 +108,26 @@ define_app_text_keys! {
AccountFarmDetailsFarmTypeNurseryPlantFarm => "account.farm_details.farm_type.nursery_plant_farm",
AccountFarmDetailsFarmTypeMixedFarm => "account.farm_details.farm_type.mixed_farm",
AccountFarmDetailsFarmTypeOther => "account.farm_details.farm_type.other",
+ AccountSettingsTitle => "account.settings.title",
+ AccountSettingsNostrRelaysTitle => "account.settings.nostr_relays.title",
+ AccountSettingsNostrRelaysHelper => "account.settings.nostr_relays.helper",
+ AccountSettingsRelayAccessReadWrite => "account.settings.relay_access.read_write",
+ AccountSettingsRelayAccessReadOnly => "account.settings.relay_access.read_only",
+ AccountSettingsRemoveRelayAction => "account.settings.remove_relay.action",
+ AccountSettingsAddRelayLabel => "account.settings.add_relay.label",
+ AccountSettingsAddRelayPlaceholder => "account.settings.add_relay.placeholder",
+ AccountSettingsAddRelayAction => "account.settings.add_relay.action",
+ AccountSettingsResetRelaysAction => "account.settings.reset_relays.action",
+ AccountSettingsDefaultRelaysNote => "account.settings.default_relays.note",
+ AccountSettingsBlossomServerTitle => "account.settings.blossom_server.title",
+ AccountSettingsBlossomServerHelper => "account.settings.blossom_server.helper",
+ AccountSettingsBlossomServerUrlLabel => "account.settings.blossom_server_url.label",
+ AccountSettingsBlossomProductPhotosLabel => "account.settings.blossom_product_photos.label",
+ AccountSettingsBlossomProfileFarmMediaLabel => "account.settings.blossom_profile_farm_media.label",
+ AccountSettingsResetBlossomServerAction => "account.settings.reset_blossom_server.action",
+ AccountSettingsBlossomConnectionHealthy => "account.settings.blossom_connection.healthy",
+ AccountSettingsBlossomUploadsAvailable => "account.settings.blossom_uploads.available",
+ AccountSettingsSaveChangesAction => "account.settings.save_changes.action",
HomeNavBrowse => "home.nav.browse",
HomeNavSearch => "home.nav.search",
HomeNavCart => "home.nav.cart",
diff --git a/crates/i18n/src/lib.rs b/crates/i18n/src/lib.rs
@@ -160,7 +160,7 @@ mod tests {
assert_eq!(app_text(AppTextKey::AccountTabProfile), "Profile");
assert_eq!(app_text(AppTextKey::AccountTabFarmDetails), "Farm details");
assert_eq!(app_text(AppTextKey::AccountTabPreferences), "Preferences");
- assert_eq!(app_text(AppTextKey::AccountTabSecurity), "Security");
+ assert_eq!(app_text(AppTextKey::AccountTabSettings), "Settings");
assert_eq!(
app_text(AppTextKey::AccountNotImplemented),
"Not implemented"
@@ -209,6 +209,19 @@ mod tests {
app_text(AppTextKey::AccountFarmDetailsFarmTypeVegetableFarm),
"Vegetable farm"
);
+ assert_eq!(app_text(AppTextKey::AccountSettingsTitle), "Settings");
+ assert_eq!(
+ app_text(AppTextKey::AccountSettingsNostrRelaysTitle),
+ "Nostr relays"
+ );
+ assert_eq!(
+ app_text(AppTextKey::AccountSettingsBlossomServerTitle),
+ "Blossom server"
+ );
+ assert_eq!(
+ app_text(AppTextKey::AccountSettingsSaveChangesAction),
+ "Save changes"
+ );
assert_eq!(
app_text(AppTextKey::AccountFarmDetailsSaveDraftAction),
"Save draft"
diff --git a/i18n/locales/en/messages.json b/i18n/locales/en/messages.json
@@ -10,7 +10,7 @@
"account.tab.profile": "Profile",
"account.tab.farm_details": "Farm details",
"account.tab.preferences": "Preferences",
- "account.tab.security": "Security",
+ "account.tab.settings": "Settings",
"account.not_implemented": "Not implemented",
"account.profile.personal_details.title": "Personal details",
"account.profile.picture.label": "Profile picture",
@@ -87,6 +87,26 @@
"account.farm_details.farm_type.nursery_plant_farm": "Nursery & plant farm",
"account.farm_details.farm_type.mixed_farm": "Mixed farm",
"account.farm_details.farm_type.other": "Other farm type",
+ "account.settings.title": "Settings",
+ "account.settings.nostr_relays.title": "Nostr relays",
+ "account.settings.nostr_relays.helper": "Manage the relays used to publish and read events.",
+ "account.settings.relay_access.read_write": "Read & write",
+ "account.settings.relay_access.read_only": "Read only",
+ "account.settings.remove_relay.action": "Remove relay",
+ "account.settings.add_relay.label": "Add relay",
+ "account.settings.add_relay.placeholder": "wss://your-relay.example",
+ "account.settings.add_relay.action": "Add relay",
+ "account.settings.reset_relays.action": "Reset to defaults",
+ "account.settings.default_relays.note": "Default relays are selected automatically when your account is created.",
+ "account.settings.blossom_server.title": "Blossom server",
+ "account.settings.blossom_server.helper": "Choose where media uploads are stored.",
+ "account.settings.blossom_server_url.label": "Server URL",
+ "account.settings.blossom_product_photos.label": "Use this server for product photos",
+ "account.settings.blossom_profile_farm_media.label": "Use this server for profile and farm media",
+ "account.settings.reset_blossom_server.action": "Reset default server",
+ "account.settings.blossom_connection.healthy": "Connection status: Healthy",
+ "account.settings.blossom_uploads.available": "Uploads available",
+ "account.settings.save_changes.action": "Save changes",
"home.nav.browse": "Browse",
"home.nav.search": "Search",
"home.nav.cart": "Cart",