use dom::{Dom, DomBuilder, Url}; use futures_signals::signal::{Mutable, Signal}; use stdweb::web::{window, HtmlElement, IEventTarget}; use stdweb::web::event::ClickEvent; use stdweb::traits::IEvent; use stdweb::Value; /*pub struct State { value: Mutable>, callback: Value, } impl State { pub fn new() -> Self { // TODO replace with stdweb function let value = Mutable::new(js!( return history.state; ).try_into().unwrap()); let callback = |state: Option| { value.set(state); }; Self { value, callback: js!( var callback = @{callback}; addEventListener("popstate", function (e) { callback(e.state); }, true); return callback; ), } } pub fn set(&self, value: A) { window().history().replace_state(value, "", None).unwrap(); self.value.set(value); } }*/ fn current_url_string() -> String { window().location().unwrap().href().unwrap() } // TODO inline ? fn change_url(mutable: &Mutable) { let mut lock = mutable.lock_mut(); let new_url = current_url_string(); // TODO test that this doesn't notify if the URLs are the same // TODO helper method for this // TODO can this be made more efficient ? if lock.href() != new_url { *lock = Url::new(&new_url); } } struct CurrentUrl { value: Mutable, listener: Value, } impl CurrentUrl { #[inline] fn new() -> Self { let value = Mutable::new(Url::new(¤t_url_string())); let callback = { let value = value.clone(); move || { change_url(&value); } }; Self { value, listener: js!( function listener(e) { @{callback}(); } addEventListener("popstate", listener, true); return listener; ), } } } impl Drop for CurrentUrl { fn drop(&mut self) { js! { @(no_return) removeEventListener("popstate", @{&self.listener}, true); } } } // TODO use thread_local instead ? lazy_static! { static ref URL: CurrentUrl = CurrentUrl::new(); } #[inline] pub fn current_url() -> Url { URL.value.get_cloned() } #[inline] pub fn url() -> impl Signal { URL.value.signal_cloned() } // TODO if URL hasn't been created yet, don't create it #[inline] pub fn go_to_url(new_url: &str) { // TODO replace with stdweb function js! { @(no_return) // TODO is this the best state object to use ? history.pushState(null, "", @{new_url}); } change_url(&URL.value); } // TODO somehow use &str rather than String #[inline] pub fn on_click_go_to_url(new_url: String) -> impl FnOnce(DomBuilder) -> DomBuilder where A: IEventTarget + Clone + 'static { #[inline] move |dom| { dom.event(move |e: ClickEvent| { e.prevent_default(); go_to_url(&new_url); }) } } #[inline] pub fn link(url: &str, f: F) -> Dom where F: FnOnce(DomBuilder) -> DomBuilder { html!("a", { .attribute("href", url) // TODO somehow avoid this allocation .apply(on_click_go_to_url(url.to_string())) .apply(f) }) }