router.rs
⎇
Raw
1//! Tiny hash-based router.
2//!
3//! - `#/` — root picker / single-root browser
4//! - `#/r/{root_id}/{segment/...}` — a user's folder
5//! - `#/s/{token}/{segment/...}` — a public share (no login)
6//!
7//! The URL hash is the single source of truth; the `Location` signal in the
8//! app mirrors it.
9
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct Location {
12 /// None = the top-level "folders" view (root picker).
13 pub root_id: Option<i64>,
14 /// Path segments relative to the current root (or share target).
15 pub path: Vec<String>,
16 /// Present when the location is a public share (`#/s/{token}/...`).
17 pub share_token: Option<String>,
18}
19
20impl Location {
21 pub const fn root() -> Self {
22 Self {
23 root_id: None,
24 path: Vec::new(),
25 share_token: None,
26 }
27 }
28}
29
30pub fn parse_location() -> Location {
31 let hash = web_sys::window()
32 .and_then(|w| w.location().hash().ok())
33 .unwrap_or_default();
34 let h = hash.trim_start_matches('#');
35 let h = h.trim_start_matches('/');
36 let mut parts = h.split('/').filter(|s| !s.is_empty());
37 match parts.next() {
38 Some("r") => match parts.next().and_then(|s| s.parse().ok()) {
39 Some(root_id) => Location {
40 root_id: Some(root_id),
41 path: parts.map(String::from).collect(),
42 share_token: None,
43 },
44 None => Location::root(),
45 },
46 Some("s") => match parts.next() {
47 Some(token) => Location {
48 root_id: None,
49 path: parts.map(String::from).collect(),
50 share_token: Some(token.to_string()),
51 },
52 None => Location::root(),
53 },
54 _ => Location::root(),
55 }
56}
57
58pub fn location_to_hash(loc: &Location) -> String {
59 if let Some(token) = &loc.share_token {
60 let mut s = format!("#/s/{token}");
61 for seg in &loc.path {
62 s.push('/');
63 s.push_str(seg);
64 }
65 return s;
66 }
67 match loc.root_id {
68 None => "#/".to_string(),
69 Some(id) => {
70 let mut s = format!("#/r/{id}");
71 for seg in &loc.path {
72 s.push('/');
73 s.push_str(seg);
74 }
75 s
76 }
77 }
78}
79
80/// Change the app location (updates the hash; the app listens for hashchange).
81pub fn navigate(loc: &Location) {
82 if let Some(w) = web_sys::window() {
83 let _ = w.location().set_hash(&location_to_hash(loc));
84 }
85}
86
87/// Build the share link for a token, relative to the current page origin.
88pub fn share_url(token: &str) -> String {
89 let base = web_sys::window()
90 .and_then(|w| w.location().href().ok())
91 .unwrap_or_default();
92 // Cut off any existing hash, then append the share hash.
93 let cut = base.find('#').unwrap_or(base.len());
94 format!("{}#/s/{token}", &base[..cut])
95}
96