//! Formatting + small browser helpers. use leptos::prelude::*; use wasm_bindgen::JsValue; /// A window event listener that is removed when the current owner is disposed. /// /// Leptos' `window_event_listener` returns a `WindowListenerHandle` with no /// `Drop` impl — if the owning component is unmounted without an explicit /// `remove()`, the listener keeps firing on `window` and panics by reading /// the component's now-disposed signals. pub fn owned_window_listener( event: E, cb: impl Fn(E::EventType) + 'static, ) where E::EventType: wasm_bindgen::JsCast, { let handle = window_event_listener(event, cb); on_cleanup(move || handle.remove()); } pub fn format_size(bytes: u64) -> String { const UNITS: [&str; 5] = ["B", "KB", "MB", "GB", "TB"]; let mut v = bytes as f64; let mut i = 0; while v >= 1024.0 && i < UNITS.len() - 1 { v /= 1024.0; i += 1; } if i == 0 { format!("{bytes} B") } else { format!("{v:.1} {}", UNITS[i]) } } /// Format an RFC 3339 timestamp as "YYYY-MM-DD HH:MM" (UTC) for display. /// Falls back to the raw string when it cannot be parsed. pub fn format_date(rfc3339: &str) -> String { let d = js_sys::Date::new(&JsValue::from_str(rfc3339)); if d.get_time().is_nan() { return rfc3339.to_string(); } fn pad(n: u32) -> String { if n < 10 { format!("0{n}") } else { n.to_string() } } format!( "{:04}-{}-{} {} {}", d.get_utc_full_year(), pad(d.get_utc_month() + 1), pad(d.get_utc_date()), pad(d.get_utc_hours()), pad(d.get_utc_minutes()), ) } pub fn storage_get(key: &str) -> Option { let w = web_sys::window()?; let storage_opt = w.local_storage().ok()?; let storage = storage_opt.as_ref()?; storage.get_item(key).ok().flatten() } pub fn storage_set(key: &str, value: &str) { if let Some(s) = web_sys::window().and_then(|w| w.local_storage().ok().flatten()) { let _ = s.set_item(key, value); } } #[derive(Clone, Copy, PartialEq, Debug)] pub enum ViewMode { Grid, List, } impl ViewMode { const KEY: &'static str = "fbng.view"; pub fn load() -> Self { match storage_get(Self::KEY).as_deref() { Some("list") => ViewMode::List, _ => ViewMode::Grid, } } pub fn save(self) { storage_set( Self::KEY, if self == ViewMode::List { "list" } else { "grid" }, ); } }