app

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

commit 72616938aa47b3f61603a2e228c85adb3d1d0e0e
parent 453c7a9ff4a4501b6a49d201d931898d9eb17894
Author: triesap <tyson@radroots.org>
Date:   Mon,  2 Feb 2026 16:46:26 +0000

app: fix wasm build helpers

- handle response array_buffer and cache cloning
- update number format and random values usage
- fix file picker closure sender lifetime
- normalize navigator locale option handling

Diffstat:
Mcrates/app-lib/src/file.rs | 6+++++-
Mcrates/app-lib/src/locale.rs | 2+-
Mcrates/utils/src/async/mod.rs | 1+
Mcrates/utils/src/cache/mod.rs | 16++++++++++------
Mcrates/utils/src/currency/mod.rs | 7++++++-
Mcrates/utils/src/numbers/mod.rs | 6++----
6 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/crates/app-lib/src/file.rs b/crates/app-lib/src/file.rs @@ -112,14 +112,18 @@ pub async fn select_file() -> Result<Option<web_sys::File>, FileError> { input.set_accept("*/*"); let (sender, receiver) = futures::channel::oneshot::channel(); + let sender = Rc::new(RefCell::new(Some(sender))); let closure_holder: Rc<RefCell<Option<wasm_bindgen::closure::Closure<dyn FnMut(_)>>>> = Rc::new(RefCell::new(None)); let closure_ref = closure_holder.clone(); + let sender_ref = Rc::clone(&sender); let input_clone = input.clone(); *closure_holder.borrow_mut() = Some(wasm_bindgen::closure::Closure::wrap(Box::new( move |_event: web_sys::Event| { let file = input_clone.files().and_then(|list| list.get(0)); - let _ = sender.send(file); + if let Some(sender) = sender_ref.borrow_mut().take() { + let _ = sender.send(file); + } closure_ref.borrow_mut().take(); }, ) as Box<dyn FnMut(_)>)); diff --git a/crates/app-lib/src/locale.rs b/crates/app-lib/src/locale.rs @@ -29,7 +29,7 @@ pub fn resolve_locale(locales: &[&str], navigator_locale: Option<&str>) -> Strin pub fn get_locale(locales: &[&str]) -> String { #[cfg(target_arch = "wasm32")] { - let navigator_locale = web_sys::window().map(|window| window.navigator().language()); + let navigator_locale = web_sys::window().and_then(|window| window.navigator().language()); resolve_locale(locales, navigator_locale.as_deref()) } #[cfg(not(target_arch = "wasm32"))] diff --git a/crates/utils/src/async/mod.rs b/crates/utils/src/async/mod.rs @@ -2,6 +2,7 @@ use crate::error::RadrootsAppUtilsError; use std::future::Future; +#[cfg(not(target_arch = "wasm32"))] use std::time::Duration; pub async fn exe_iter<F, Fut>( diff --git a/crates/utils/src/cache/mod.rs b/crates/utils/src/cache/mod.rs @@ -51,7 +51,10 @@ pub async fn asset_cache_fetch_bytes( if !response.ok() { return Ok(None); } - let buffer = wasm_bindgen_futures::JsFuture::from(response.array_buffer()) + let promise = response + .array_buffer() + .map_err(|_| RadrootsAppUtilsError::Unavailable)?; + let buffer = wasm_bindgen_futures::JsFuture::from(promise) .await .map_err(|_| RadrootsAppUtilsError::Unavailable)?; let array = js_sys::Uint8Array::new(&buffer); @@ -84,8 +87,6 @@ async fn asset_cache_fetch_impl( url: &str, config: Option<&AssetCacheFetchConfig>, ) -> Result<AssetResponse, RadrootsAppUtilsError> { - use wasm_bindgen::JsCast; - let cache_name = cache_name_resolve(config); let cache_key = cache_key_resolve(url); if let Some(cached) = cache_read(&cache_name, &cache_key).await? { @@ -93,7 +94,10 @@ async fn asset_cache_fetch_impl( } let response = fetch_with_init(url, config).await?; if response.ok() || response.type_() == web_sys::ResponseType::Opaque { - cache_write(&cache_name, &cache_key, response.clone()).await?; + let response = response + .clone() + .map_err(|_| RadrootsAppUtilsError::Unavailable)?; + cache_write(&cache_name, &cache_key, response).await?; } Ok(response) } @@ -114,10 +118,10 @@ async fn fetch_with_init( use wasm_bindgen::JsCast; let window = web_sys::window().ok_or(RadrootsAppUtilsError::Unavailable)?; - let mut init = web_sys::RequestInit::new(); + let init = web_sys::RequestInit::new(); if let Some(request_init) = config.and_then(|config| config.request_init.as_ref()) { if let Some(cache_mode) = request_init.cache { - init.cache(cache_mode.to_request_cache()); + init.set_cache(cache_mode.to_request_cache()); } } let response = wasm_bindgen_futures::JsFuture::from(window.fetch_with_str_and_init(url, &init)) diff --git a/crates/utils/src/currency/mod.rs b/crates/utils/src/currency/mod.rs @@ -88,7 +88,12 @@ fn fmt_price_value(locale: &str, value: f64, currency: FiatCurrency) -> String { &JsValue::from_f64(2.0), ); let formatter = js_sys::Intl::NumberFormat::new(&locales, &options); - formatter.format(value).into() + let formatted = formatter + .format() + .call1(&JsValue::NULL, &JsValue::from_f64(value)) + .ok() + .and_then(|value| value.as_string()); + formatted.unwrap_or_else(|| format!("{} {:.2}", currency.as_upper(), value)) } #[cfg(not(target_arch = "wasm32"))] diff --git a/crates/utils/src/numbers/mod.rs b/crates/utils/src/numbers/mod.rs @@ -44,12 +44,10 @@ fn random_u64() -> Result<u64, RadrootsAppUtilsError> { let crypto = window .crypto() .map_err(|_| RadrootsAppUtilsError::Unavailable)?; - let array = js_sys::Uint8Array::new_with_length(8); + let mut bytes = [0u8; 8]; crypto - .get_random_values_with_u8_array(&array) + .get_random_values_with_u8_array(&mut bytes) .map_err(|_| RadrootsAppUtilsError::Unavailable)?; - let mut bytes = [0u8; 8]; - array.copy_to(&mut bytes); Ok(u64::from_le_bytes(bytes)) }