app

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

commit 4a74758b7fcd3c6eda3aad55f4fc51fe837e8ab3
parent 68cadf85871d7649027a2c91d8be08e29a78856d
Author: triesap <triesap@radroots.dev>
Date:   Wed, 21 Jan 2026 16:19:44 +0000

app: gate uninitialized routes to setup

- add setup page placeholder and route

- navigate to /setup when init check fails

- reuse setup checker after backends ready

- expose /setup link for test flow

Diffstat:
Mapp/src/app.rs | 32++++++++++++++++++++++++++++++++
Mapp/src/lib.rs | 1+
2 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/app/src/app.rs b/app/src/app.rs @@ -2,12 +2,14 @@ use gloo_timers::future::TimeoutFuture; use leptos::prelude::*; use leptos::task::spawn_local; use leptos_router::components::{A, Route, Router, Routes}; +use leptos_router::hooks::use_navigate; use leptos_router::path; use crate::{ app_init_assets, app_init_backends, app_init_has_completed, + app_init_needs_setup, app_init_state_default, app_init_mark_completed, app_init_reset, @@ -125,6 +127,15 @@ fn app_health_check_delay_ms() -> u32 { } #[component] +fn SetupPage() -> impl IntoView { + view! { + <main> + <div>"setup"</div> + </main> + } +} + +#[component] fn HomePage() -> impl IntoView { let context = app_context(); let fallback_backends = RwSignal::new_local(None::<RadrootsAppBackends>); @@ -439,10 +450,12 @@ pub fn RadrootsApp() -> impl IntoView { let backends = RwSignal::new_local(None::<RadrootsAppBackends>); let init_error = RwSignal::new_local(None::<RadrootsAppInitError>); let init_state = RwSignal::new_local(app_init_state_default()); + let navigate = use_navigate(); provide_context(backends); provide_context(init_error); provide_context(init_state); Effect::new(move || { + let navigate = navigate.clone(); spawn_local(async move { let stage = RadrootsAppInitStage::Storage; init_state.update(|state| app_init_stage_set(state, stage)); @@ -485,11 +498,28 @@ pub fn RadrootsApp() -> impl IntoView { } match app_init_backends(config).await { Ok(value) => { + let key_maps = value.config.datastore.key_maps.clone(); + let datastore = value.datastore.clone(); + let keystore_config = value.nostr_keystore.get_config(); backends.set(Some(value)); app_init_mark_completed(); let stage = RadrootsAppInitStage::Ready; init_state.update(|state| app_init_stage_set(state, stage)); log_init_stage(stage); + let navigate = navigate.clone(); + spawn_local(async move { + let keystore = radroots_app_core::keystore::RadrootsClientWebKeystoreNostr::new( + Some(keystore_config), + ); + match app_init_needs_setup(datastore.as_ref(), &keystore, &key_maps).await { + Ok(true) => navigate("/setup", Default::default()), + Ok(false) => {} + Err(err) => { + let _ = app_log_error_emit(&err); + navigate("/setup", Default::default()); + } + } + }); let flush_ctx = backends.with_untracked(|value| { value.as_ref().map(|backends| { ( @@ -524,10 +554,12 @@ pub fn RadrootsApp() -> impl IntoView { <nav style="display:flex;gap:12px;margin-bottom:12px;"> <A href="/" exact=true>"home"</A> <A href="/logs">"logs"</A> + <A href="/setup">"setup"</A> </nav> <Routes fallback=|| view! { <div>"not_found"</div> }> <Route path=path!("") view=HomePage /> <Route path=path!("logs") view=RadrootsAppLogsPage /> + <Route path=path!("setup") view=SetupPage /> </Routes> </Router> } diff --git a/app/src/lib.rs b/app/src/lib.rs @@ -137,6 +137,7 @@ pub use init::{ app_init_backends, app_init_fetch_asset, app_init_has_completed, + app_init_needs_setup, app_init_mark_completed, app_init_progress_add, app_init_reset,