1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
use std::cell::RefCell; use std::rc::Rc; use wasm_bindgen::{JsCast, UnwrapThrowExt}; pub trait Routes<C> where C: crate::component::Component, { fn url(&self) -> String; fn routing(location: web_sys::Location, comp: &crate::component::Comp<C>); fn router(comp: &crate::component::Comp<C>) -> Option<Router> { Some(Router::new(comp)) } } impl<C> Routes<C> for () where C: crate::component::Component, { fn url(&self) -> String { String::new() } fn routing(_: web_sys::Location, _: &crate::component::Comp<C>) {} fn router(_: &crate::component::Comp<C>) -> Option<Router> { None } } pub struct Router { _current_url: Rc<RefCell<String>>, _pop_state_closure: wasm_bindgen::closure::Closure<dyn Fn(web_sys::PopStateEvent)>, } impl Router { fn new<C>(comp: &crate::component::Comp<C>) -> Self where C: crate::component::Component, { let _current_url = Rc::new(RefCell::new(String::new())); let location = get_new_location(&_current_url).expect_throw("Why the first url not invalid?"); C::Routes::routing(location, comp); let _pop_state_closure = register_pop_state_event(_current_url.clone(), comp.clone()); Self { _pop_state_closure, _current_url, } } } fn get_new_location(current_url: &Rc<RefCell<String>>) -> Option<web_sys::Location> { let location = crate::utils::window().location(); let new_url = location .href() .expect_throw("Unable to get window.location.href"); let mut current_url = current_url .try_borrow_mut() .expect_throw("Multiple mutable borrow on current_url"); if *current_url != new_url { *current_url = new_url; Some(location) } else { None } } fn register_event_listener_on_window(event: &str, listener: &js_sys::Function) { let window = crate::utils::window(); let window: &web_sys::EventTarget = window.as_ref(); window .add_event_listener_with_callback(event, listener) .expect_throw("Unable to register event listener on window"); } fn register_pop_state_event<C>( current_url: Rc<RefCell<String>>, comp: crate::component::Comp<C>, ) -> wasm_bindgen::closure::Closure<dyn Fn(web_sys::PopStateEvent)> where C: crate::component::Component, { let closure = move |_: web_sys::PopStateEvent| { if let Some(location) = get_new_location(¤t_url) { C::Routes::routing(location, &comp); } }; let closure = wasm_bindgen::closure::Closure::wrap( Box::new(closure) as Box<dyn Fn(web_sys::PopStateEvent)> ); register_event_listener_on_window("popstate", closure.as_ref().unchecked_ref()); closure }