app

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

commit ec5bec636538eea9c1ddcdb4210a1b90fcc02098
parent 9f105c3f27a4ce2da3d9a772173dbf8e84a27ff0
Author: triesap <tyson@radroots.org>
Date:   Wed, 28 Jan 2026 14:10:42 +0000

app: Add existing keypair step

- Add key add-existing setup step and transitions
- Render add-existing key input view with placeholder
- Track key add input state locally for UI
- Preserve key choice behavior with no-op callbacks

Diffstat:
Mapp/src/app.rs | 72+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mapp/src/setup.rs | 18+++++++++++++++---
2 files changed, 82 insertions(+), 8 deletions(-)

diff --git a/app/src/app.rs b/app/src/app.rs @@ -209,6 +209,7 @@ fn SetupPage() -> impl IntoView { let navigate_home = navigate.clone(); let setup_step = RwSignal::new_local(app_setup_step_default()); let setup_key_choice = RwSignal::new_local(None::<RadrootsAppSetupKeyChoice>); + let nostr_key_add = RwSignal::new_local(String::new()); let profile_name = RwSignal::new_local(String::new()); let profile_nip05 = RwSignal::new_local(true); let on_generate_key = setup_touch_callback("generate_key"); @@ -220,9 +221,25 @@ fn SetupPage() -> impl IntoView { }); let advance_step: Callback<MouseEvent> = { let setup_step = setup_step.clone(); + let setup_key_choice = setup_key_choice.clone(); Callback::new(move |_| { setup_step.update(|step| { - *step = step.next(); + *step = match *step { + RadrootsAppSetupStep::Intro => RadrootsAppSetupStep::KeyChoice, + RadrootsAppSetupStep::KeyChoice => { + match setup_key_choice.get() { + Some(RadrootsAppSetupKeyChoice::Generate) => { + RadrootsAppSetupStep::Profile + } + Some(RadrootsAppSetupKeyChoice::AddExisting) => { + RadrootsAppSetupStep::KeyAddExisting + } + None => RadrootsAppSetupStep::KeyChoice, + } + } + RadrootsAppSetupStep::KeyAddExisting => RadrootsAppSetupStep::Profile, + RadrootsAppSetupStep::Profile => RadrootsAppSetupStep::Profile, + }; }); }) }; @@ -230,10 +247,22 @@ fn SetupPage() -> impl IntoView { let setup_step = setup_step.clone(); let setup_key_choice = setup_key_choice.clone(); Callback::new(move |_| { - setup_step.update(|step| { - *step = step.prev(); - }); - setup_key_choice.set(None); + let current_step = setup_step.get(); + let next_step = match current_step { + RadrootsAppSetupStep::Intro => RadrootsAppSetupStep::Intro, + RadrootsAppSetupStep::KeyChoice => RadrootsAppSetupStep::Intro, + RadrootsAppSetupStep::KeyAddExisting => RadrootsAppSetupStep::KeyChoice, + RadrootsAppSetupStep::Profile => match setup_key_choice.get() { + Some(RadrootsAppSetupKeyChoice::AddExisting) => { + RadrootsAppSetupStep::KeyAddExisting + } + _ => RadrootsAppSetupStep::KeyChoice, + }, + }; + setup_step.set(next_step); + if matches!(next_step, RadrootsAppSetupStep::Intro) { + setup_key_choice.set(None); + } }) }; let on_generate_key = on_generate_key.clone(); @@ -378,6 +407,39 @@ fn SetupPage() -> impl IntoView { </div> </section> }.into_any(), + RadrootsAppSetupStep::KeyAddExisting => view! { + <section + id="app-setup-key-add-existing" + class="app-view app-view-enter flex flex-col w-full px-6 pt-10 pb-16" + > + <div + id="app-setup-key-add-existing-body" + class="flex flex-1 w-full flex-col justify-center items-center" + > + <div + id="app-setup-key-add-existing-card" + class="flex flex-col w-full gap-6 justify-center items-center" + > + <p + id="app-setup-key-add-existing-title" + class="font-sans font-[600] text-ly0-gl text-3xl capitalize" + > + "Add existing key" + </p> + <input + id="app-setup-key-add-existing-input" + class="input-base w-lo_ios0 ios1:w-lo_ios1 text-[1.25rem] text-center placeholder:opacity-60" + type="text" + placeholder="Enter nostr nsec/hex" + prop:value=move || nostr_key_add.get() + on:input=move |ev| { + nostr_key_add.set(event_target_value(&ev)); + } + /> + </div> + </div> + </section> + }.into_any(), RadrootsAppSetupStep::Profile => view! { <section id="app-setup-profile" diff --git a/app/src/setup.rs b/app/src/setup.rs @@ -46,6 +46,7 @@ pub fn app_setup_state_new(active_key: String, eula_date: String) -> RadrootsApp pub enum RadrootsAppSetupStep { Intro, KeyChoice, + KeyAddExisting, Profile, } @@ -53,7 +54,8 @@ impl RadrootsAppSetupStep { pub const fn next(self) -> Self { match self { RadrootsAppSetupStep::Intro => RadrootsAppSetupStep::KeyChoice, - RadrootsAppSetupStep::KeyChoice => RadrootsAppSetupStep::Profile, + RadrootsAppSetupStep::KeyChoice => RadrootsAppSetupStep::KeyAddExisting, + RadrootsAppSetupStep::KeyAddExisting => RadrootsAppSetupStep::Profile, RadrootsAppSetupStep::Profile => RadrootsAppSetupStep::Profile, } } @@ -62,7 +64,8 @@ impl RadrootsAppSetupStep { match self { RadrootsAppSetupStep::Intro => RadrootsAppSetupStep::Intro, RadrootsAppSetupStep::KeyChoice => RadrootsAppSetupStep::Intro, - RadrootsAppSetupStep::Profile => RadrootsAppSetupStep::KeyChoice, + RadrootsAppSetupStep::KeyAddExisting => RadrootsAppSetupStep::KeyChoice, + RadrootsAppSetupStep::Profile => RadrootsAppSetupStep::KeyAddExisting, } } @@ -322,6 +325,10 @@ mod tests { ); assert_eq!( RadrootsAppSetupStep::KeyChoice.next(), + RadrootsAppSetupStep::KeyAddExisting + ); + assert_eq!( + RadrootsAppSetupStep::KeyAddExisting.next(), RadrootsAppSetupStep::Profile ); assert_eq!( @@ -341,15 +348,20 @@ mod tests { RadrootsAppSetupStep::Intro ); assert_eq!( - RadrootsAppSetupStep::Profile.prev(), + RadrootsAppSetupStep::KeyAddExisting.prev(), RadrootsAppSetupStep::KeyChoice ); + assert_eq!( + RadrootsAppSetupStep::Profile.prev(), + RadrootsAppSetupStep::KeyAddExisting + ); } #[test] fn setup_step_terminal_matches_profile() { assert!(!RadrootsAppSetupStep::Intro.is_terminal()); assert!(!RadrootsAppSetupStep::KeyChoice.is_terminal()); + assert!(!RadrootsAppSetupStep::KeyAddExisting.is_terminal()); assert!(RadrootsAppSetupStep::Profile.is_terminal()); }