router.rs
⎇
Raw
1//! Tiny hash-based router: `#/r/{root_id}/{segment/...}` (files) or `#/`
2//! (root picker). The URL hash is the single source of truth; the
3//! `Location` signal in the app mirrors it.
4
5#[derive(Clone, Debug, PartialEq, Eq)]
6pub struct Location {
7 /// None = the top-level "folders" view (root picker).
8 pub root_id: Option<i64>,
9 /// Path segments relative to the current root.
10 pub path: Vec<String>,
11}
12
13impl Location {
14 pub const fn root() -> Self {
15 Self {
16 root_id: None,
17 path: Vec::new(),
18 }
19 }
20}
21
22pub fn parse_location() -> Location {
23 let hash = web_sys::window()
24 .and_then(|w| w.location().hash().ok())
25 .unwrap_or_default();
26 let h = hash.trim_start_matches('#');
27 let h = h.trim_start_matches('/');
28 let mut parts = h.split('/').filter(|s| !s.is_empty());
29 match parts.next() {
30 Some("r") => match parts.next().and_then(|s| s.parse().ok()) {
31 Some(root_id) => Location {
32 root_id: Some(root_id),
33 path: parts.map(String::from).collect(),
34 },
35 None => Location::root(),
36 },
37 _ => Location::root(),
38 }
39}
40
41pub fn location_to_hash(loc: &Location) -> String {
42 match loc.root_id {
43 None => "#/".to_string(),
44 Some(id) => {
45 let mut s = format!("#/r/{id}");
46 for seg in &loc.path {
47 s.push('/');
48 s.push_str(seg);
49 }
50 s
51 }
52 }
53}
54
55/// Change the app location (updates the hash; the app listens for hashchange).
56pub fn navigate(loc: &Location) {
57 if let Some(w) = web_sys::window() {
58 let _ = w.location().set_hash(&location_to_hash(loc));
59 }
60}
61