app

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

commit 7c1060cbb8ac5004cf85fb9a33649e5f5816560c
parent e257818f68174fb537741369704be17243790e4e
Author: triesap <triesap@radroots.dev>
Date:   Tue, 20 Jan 2026 16:22:20 +0000

app: abort logs auto-refresh on unmount

- replace interval forget with abortable refresh task

- add futures dependency for abort handle wiring

- register on_cleanup to stop the refresh loop

- keep refresh cadence and manual button behavior

Diffstat:
MCargo.lock | 2++
Mapp/Cargo.toml | 4++--
Mapp/src/logs.rs | 18+++++++++++++++---
3 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -748,6 +748,8 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" dependencies = [ + "futures-channel", + "futures-core", "js-sys", "wasm-bindgen", ] diff --git a/app/Cargo.toml b/app/Cargo.toml @@ -14,9 +14,10 @@ leptos = { workspace = true, features = ["csr"] } leptos_router = { workspace = true } wasm-bindgen.workspace = true wasm-bindgen-futures.workspace = true +futures.workspace = true js-sys.workspace = true web-sys.workspace = true -gloo-timers.workspace = true +gloo-timers = { workspace = true, features = ["futures"] } radroots-app-core = { path = "../crates/core" } radroots-log = { path = "../refs/crates/log", default-features = false } tracing-wasm = "0.2" @@ -26,7 +27,6 @@ serde_json.workspace = true uuid.workspace = true [dev-dependencies] -futures.workspace = true async-trait.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/app/src/logs.rs b/app/src/logs.rs @@ -1,6 +1,8 @@ #![forbid(unsafe_code)] -use gloo_timers::callback::Interval; +use futures::future::{AbortHandle, Abortable}; +use futures::StreamExt; +use gloo_timers::future::IntervalStream; use leptos::prelude::*; use leptos::task::spawn_local; use std::rc::Rc; @@ -120,8 +122,18 @@ pub fn LogsPage() -> impl IntoView { } interval_started.set(true); let refresh = Rc::clone(&interval_effect); - let interval = Interval::new(logs_auto_refresh_ms(), move || refresh()); - interval.forget(); + let (abort_handle, abort_reg) = AbortHandle::new_pair(); + let abort_handle_cleanup = abort_handle.clone(); + spawn_local(async move { + let mut ticks = IntervalStream::new(logs_auto_refresh_ms()); + let task = async move { + while ticks.next().await.is_some() { + refresh(); + } + }; + let _ = Abortable::new(task, abort_reg).await; + }); + on_cleanup(move || abort_handle_cleanup.abort()); }); let status_label = move || if loading.get() { "loading" } else { "idle" }; view! {