app

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

commit 19cae68be11aa50e6c8ad9cbac53abb3c0660e3b
parent 4a35f84694bcddd107f038405ab1037d329fdae4
Author: triesap <triesap@radroots.dev>
Date:   Thu, 22 Jan 2026 16:18:02 +0000

app: scaffold setup intro screen

- replace setup placeholder with structured intro layout
- add setup step signal and basic navigation between steps
- keep setup gating redirect intact
- preserve existing init and health logic paths

Diffstat:
Mapp/src/app.rs | 117+++++++++++++++++++++++++++++++++++---------------------------------------------
1 file changed, 51 insertions(+), 66 deletions(-)

diff --git a/app/src/app.rs b/app/src/app.rs @@ -29,7 +29,7 @@ use crate::{ app_datastore_read_state, app_state_notifications_permission_value, app_state_set_notifications_permission_value, - app_setup_initialize, + app_setup_step_default, app_health_check_all, RadrootsAppBackends, RadrootsAppConfig, @@ -42,6 +42,7 @@ use crate::{ RadrootsAppLogsPage, RadrootsAppSettingsPage, RadrootsAppUiDemoPage, + RadrootsAppSetupStep, RadrootsAppTangleClientStub, }; @@ -154,85 +155,69 @@ fn SplashPage() -> impl IntoView { #[component] fn SetupPage() -> impl IntoView { let context = app_context(); - let fallback_backends = RwSignal::new_local(None::<RadrootsAppBackends>); let fallback_setup_required = RwSignal::new_local(None::<bool>); - let backends = context - .as_ref() - .map(|value| value.backends) - .unwrap_or(fallback_backends); let setup_required = context .as_ref() .map(|value| value.setup_required) .unwrap_or(fallback_setup_required); - let setup_running = RwSignal::new_local(false); - let setup_status = RwSignal::new_local(None::<String>); let navigate = use_navigate(); let navigate_guard = navigate.clone(); + let setup_step = RwSignal::new_local(app_setup_step_default()); Effect::new(move || { if setup_required.get() == Some(false) { navigate_guard("/", Default::default()); } }); - let setup_label = move || { - if setup_running.get() { - "setup_running" - } else { - "setup_initialize" - } + let advance_step = move |_| { + setup_step.update(|step| { + *step = step.next(); + }); }; - let setup_status_label = - move || setup_status.get().unwrap_or_else(|| "setup_idle".to_string()); view! { - <main> - <div>"setup"</div> - <div style="margin-top: 12px; display: flex; align-items: center; gap: 8px;"> - <button - on:click=move |_| { - if setup_running.get_untracked() { - return; - } - let setup_ctx = backends.with_untracked(|value| { - value.as_ref().map(|backends| { - ( - backends.datastore.clone(), - backends.config.datastore.key_maps.clone(), - backends.nostr_keystore.get_config(), - ) - }) - }); - let Some((datastore, key_maps, keystore_config)) = setup_ctx else { - return; - }; - setup_running.set(true); - setup_status.set(Some("setup_start".to_string())); - let setup_required = setup_required.clone(); - let navigate = navigate.clone(); - spawn_local(async move { - let keystore = radroots_app_core::keystore::RadrootsClientWebKeystoreNostr::new( - Some(keystore_config), - ); - match app_setup_initialize(datastore.as_ref(), &keystore, &key_maps).await { - Ok(_) => { - setup_status.set(Some("setup_done".to_string())); - setup_required.set(Some(false)); - setup_running.set(false); - navigate("/", Default::default()); - } - Err(err) => { - let log_datastore = logs_datastore(); - let _ = app_log_error_store(&log_datastore, &key_maps, &err).await; - setup_status.set(Some(err.to_string())); - setup_running.set(false); - } - } - }); - } - disabled=move || setup_running.get() - > - {setup_label} - </button> - <span>{setup_status_label}</span> - </div> + <main class="min-h-[100dvh] w-full px-6 pt-10 pb-16"> + {move || match setup_step.get() { + RadrootsAppSetupStep::Intro => view! { + <section class="flex flex-col w-full gap-6"> + <header class="flex flex-col gap-3"> + <p class="font-sans text-sm uppercase tracking-[0.14em] text-ly0-gl-label"> + "Radroots" + </p> + <h1 class="font-sans font-[600] text-3xl text-ly0-gl"> + "Welcome to Radroots" + </h1> + <p class="font-sans text-line_d_e text-ly0-gl-label"> + "Set up your key and preferences to get started." + </p> + </header> + <div class="flex flex-col gap-3"> + <button + type="button" + class="button-submit rounded-touch px-6 py-3" + on:click=advance_step + > + <span class="font-sans font-[600] text-ly2-gl"> + "Get started" + </span> + </button> + </div> + </section> + }.into_any(), + RadrootsAppSetupStep::KeyChoice => view! { + <section class="flex flex-col w-full gap-4"> + <header class="flex flex-col gap-2"> + <p class="font-sans text-sm uppercase tracking-[0.14em] text-ly0-gl-label"> + "Setup" + </p> + <h2 class="font-sans font-[600] text-2xl text-ly0-gl"> + "Choose your key" + </h2> + <p class="font-sans text-line_d_e text-ly0-gl-label"> + "Select how you want to add your Nostr key." + </p> + </header> + </section> + }.into_any(), + }} </main> } }