app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit d46c0d9003acf19bb42c69c259f7b01a00b0403b
parent c78040fecf4fc4352567d7a42e676911c1d0e1ea
Author: triesap <tyson@radroots.org>
Date:   Sat, 21 Mar 2026 17:35:56 +0000

web: add browser-signer disconnect action

- expose a disconnect action only when a NIP-07 browser signer is connected
- clear the app-owned browser signer session and return the web app to setup
- keep signer key custody external to the app instead of exposing native-only destructive actions
- validate the web lane with the wasm toolchain helper and trunk build

Diffstat:
Mcrates/web/src/lib.rs | 35++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/crates/web/src/lib.rs b/crates/web/src/lib.rs @@ -14,7 +14,10 @@ use nostr::signer::NostrSigner; #[cfg(target_arch = "wasm32")] use nostr_browser_signer::{BrowserSigner, Error as BrowserSignerError}; #[cfg(target_arch = "wasm32")] -use radroots_app_core::{IdentityGateState, RadrootsApp, RadrootsAppBackend, SetupActionState}; +use radroots_app_core::{ + HomeActionKind, HomeActionState, IdentityGateState, RadrootsApp, RadrootsAppBackend, + SetupActionState, +}; #[cfg(target_arch = "wasm32")] #[derive(Clone)] @@ -70,6 +73,13 @@ impl WebBackend { other => format!("Browser signer connection failed: {other}"), } } + + fn disconnect_signer(&self) -> IdentityGateState { + let mut state = self.state.borrow_mut(); + state.connection = WebConnectionState::Disconnected; + state.pending_result = None; + IdentityGateState::Missing + } } #[cfg(target_arch = "wasm32")] @@ -145,6 +155,29 @@ impl RadrootsAppBackend for WebBackend { Ok(None) } + fn home_action_states(&self) -> Vec<HomeActionState> { + let state = self.state.borrow(); + match &state.connection { + WebConnectionState::Ready(_) => vec![HomeActionState { + kind: HomeActionKind::DisconnectSigner, + label: "Disconnect Browser Signer".to_owned(), + enabled: true, + pending: false, + }], + WebConnectionState::Disconnected | WebConnectionState::Connecting => Vec::new(), + } + } + + fn request_home_action( + &self, + action: HomeActionKind, + ) -> Result<Option<IdentityGateState>, String> { + match action { + HomeActionKind::DisconnectSigner => Ok(Some(self.disconnect_signer())), + HomeActionKind::RemoveLocalKey | HomeActionKind::ResetDevice => Ok(None), + } + } + fn poll_identity_state(&self) -> Result<Option<IdentityGateState>, String> { let mut state = self.state.borrow_mut(); let Some(result) = state.pending_result.take() else {