commit 96ee69fa0e9c3f1b773f0fbabafa00272cd283c6
parent 25dc5aaef41caee60a4cdbb5f793e522be9047c5
Author: triesap <tyson@radroots.org>
Date: Sun, 22 Mar 2026 16:25:59 +0000
desktop: add local account roster support
- surface desktop local accounts through the shared roster summary contract
- allow selecting an existing desktop account from the home roster
- reuse desktop local key generation as the home add-account action
- keep desktop backup remove and reset actions tied to the selected account
Diffstat:
1 file changed, 66 insertions(+), 6 deletions(-)
diff --git a/crates/desktop/src/main.rs b/crates/desktop/src/main.rs
@@ -10,10 +10,10 @@ use radroots_app_apple_security::{
};
use radroots_app_core::{
APP_NAME, HomeActionKind, HomeActionResult, HomeActionState, IdentityGateState,
- ImportActionState, RadrootsApp, RadrootsAppBackend, RadrootsLocationCountry,
- RadrootsLocationCountryCenterLookupResult, RadrootsLocationCountryListResult,
- RadrootsLocationPoint, RadrootsLocationResolverError, RadrootsLocationReverseOptions,
- RadrootsOfflineGeocoderPlatform, RadrootsOfflineGeocoderState,
+ ImportActionState, RadrootsAccountCustody, RadrootsAccountSummary, RadrootsApp,
+ RadrootsAppBackend, RadrootsLocationCountry, RadrootsLocationCountryCenterLookupResult,
+ RadrootsLocationCountryListResult, RadrootsLocationPoint, RadrootsLocationResolverError,
+ RadrootsLocationReverseOptions, RadrootsOfflineGeocoderPlatform, RadrootsOfflineGeocoderState,
RadrootsOfflineGeocoderUnavailableKind, RadrootsResolvedLocation,
RadrootsReverseLocationLookupResult, SetupActionState,
};
@@ -164,12 +164,30 @@ impl DesktopBackend {
RadrootsNostrSelectedAccountStatus::PublicOnly { .. } => IdentityGateState::Missing,
RadrootsNostrSelectedAccountStatus::Ready { account } => IdentityGateState::Ready {
account_id: account.account_id.to_string(),
- npub: account.public_identity.public_key_npub,
},
}
}
#[cfg(target_os = "macos")]
+ fn account_roster_from_manager(
+ manager: &RadrootsNostrAccountsManager,
+ ) -> Result<Vec<RadrootsAccountSummary>, String> {
+ manager
+ .list_accounts()
+ .map_err(|source| source.to_string())?
+ .into_iter()
+ .map(|record| {
+ Ok(RadrootsAccountSummary {
+ account_id: record.account_id.to_string(),
+ npub: record.public_identity.public_key_npub,
+ label: record.label,
+ custody: RadrootsAccountCustody::LocalManaged,
+ })
+ })
+ .collect()
+ }
+
+ #[cfg(target_os = "macos")]
fn remove_selected_local_identity(
manager: &RadrootsNostrAccountsManager,
) -> Result<IdentityGateState, String> {
@@ -297,6 +315,19 @@ impl RadrootsAppBackend for DesktopBackend {
}
}
+ fn load_account_roster(&self) -> Result<Vec<RadrootsAccountSummary>, String> {
+ #[cfg(target_os = "macos")]
+ {
+ let manager = Self::accounts_manager()?;
+ return Self::account_roster_from_manager(&manager);
+ }
+
+ #[cfg(not(target_os = "macos"))]
+ {
+ Ok(Vec::new())
+ }
+ }
+
fn offline_geocoder_state(&self) -> Option<RadrootsOfflineGeocoderState> {
Some(self.offline_geocoder.current_state())
}
@@ -489,6 +520,14 @@ impl RadrootsAppBackend for DesktopBackend {
}
}
+ fn home_setup_action_state(&self) -> Option<SetupActionState> {
+ Some(self.setup_action_state())
+ }
+
+ fn request_home_setup_action(&self) -> Result<Option<IdentityGateState>, String> {
+ self.request_setup_action()
+ }
+
fn import_action_state(&self) -> Option<ImportActionState> {
#[cfg(target_os = "macos")]
{
@@ -519,6 +558,28 @@ impl RadrootsAppBackend for DesktopBackend {
}
}
+ fn request_select_account(
+ &self,
+ account_id: &str,
+ ) -> Result<Option<IdentityGateState>, String> {
+ #[cfg(target_os = "macos")]
+ {
+ let manager = Self::accounts_manager()?;
+ let account_id = radroots_identity::RadrootsIdentityId::try_from(account_id)
+ .map_err(|_| "invalid account id".to_owned())?;
+ manager
+ .select_account(&account_id)
+ .map_err(|source| source.to_string())?;
+ return self.load_identity_state().map(Some);
+ }
+
+ #[cfg(not(target_os = "macos"))]
+ {
+ let _ = account_id;
+ Ok(None)
+ }
+ }
+
fn home_action_states(&self) -> Vec<HomeActionState> {
#[cfg(target_os = "macos")]
{
@@ -707,7 +768,6 @@ mod tests {
state,
radroots_app_core::IdentityGateState::Ready {
account_id: identity.id().to_string(),
- npub: identity.npub(),
}
);
assert_eq!(