use std::cell::RefCell;
use crate::wit_platform::{self, WitPlatform};
thread_local! {
static INSTANCE: RefCell<Option<RouterServiceInner>> = RefCell::new(None);
static CURRENT_PATH: RefCell<String> = RefCell::new("/".to_string());
}
struct RouterServiceInner {
platform: WitPlatform,
render_fn: Box<dyn Fn(&str) -> tairitsu_vdom::VNode + Send + Sync>,
}
pub fn init_router(
platform: WitPlatform,
render_fn: impl Fn(&str) -> tairitsu_vdom::VNode + Send + Sync + 'static,
) {
let inner = RouterServiceInner {
platform,
render_fn: Box::new(render_fn),
};
let path = wit_platform::get_pathname();
do_render(&inner, &path);
INSTANCE.with(|i| *i.borrow_mut() = Some(inner));
set_current_path(&path);
schedule_poll();
}
pub fn navigate(path: &str) {
let normalized = normalize_path(path);
if normalized == current_path() {
return;
}
wit_platform::push_state(&normalized);
do_navigate(&normalized);
}
pub fn on_popstate(new_path: &str) {
let normalized = normalize_path(new_path);
if normalized == current_path() {
return;
}
do_navigate(&normalized);
}
fn do_navigate(path: &str) {
INSTANCE.with(|i| {
if let Some(ref inner) = *i.borrow() {
do_render(inner, path);
}
});
set_current_path(path);
}
fn do_render(inner: &RouterServiceInner, path: &str) {
let vnode = (inner.render_fn)(path);
if let Err(e) = inner.platform.mount_vnode_to_app(vnode) {
tracing::error!("Failed to mount route '{}': {}", path, e);
}
}
fn schedule_poll() {
INSTANCE.with(|i| {
if let Some(ref inner) = *i.borrow() {
let _ = tairitsu_vdom::Platform::set_timeout(
&inner.platform,
Box::new(|| {
poll_url_change();
}),
200,
);
}
});
}
fn poll_url_change() {
let current = wit_platform::get_pathname();
if current != current_path() {
let normalized = normalize_path(¤t);
set_current_path(&normalized);
INSTANCE.with(|i| {
if let Some(ref inner) = *i.borrow() {
do_render(inner, &normalized);
}
});
}
schedule_poll();
}
fn normalize_path(path: &str) -> String {
let mut p = path.to_string();
if p.ends_with('/') && p.len() > 1 {
p.pop();
}
if p.is_empty() { "/".to_string() } else { p }
}
pub fn current_path() -> String {
CURRENT_PATH.with(|p| p.borrow().clone())
}
fn set_current_path(path: &str) {
CURRENT_PATH.with(|p| *p.borrow_mut() = path.to_string());
}