144 lines
3.4 KiB
Rust
144 lines
3.4 KiB
Rust
use wasm_bindgen::{JsValue, UnwrapThrowExt};
|
|
use web_sys::{window, Url, EventTarget, HtmlElement};
|
|
use futures_signals::signal::{Mutable, Signal};
|
|
use gloo::events::EventListener;
|
|
|
|
use crate::dom::{Dom, DomBuilder};
|
|
use crate::events;
|
|
|
|
|
|
/*pub struct State<A> {
|
|
value: Mutable<Option<A>>,
|
|
callback: Value,
|
|
}
|
|
|
|
impl<A> State<A> {
|
|
pub fn new() -> Self {
|
|
// TODO replace with stdweb function
|
|
let value = Mutable::new(js!( return history.state; ).try_into().unwrap_throw());
|
|
|
|
let callback = |state: Option<A>| {
|
|
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_throw();
|
|
self.value.set(value);
|
|
}
|
|
}*/
|
|
|
|
|
|
fn current_url_string() -> String {
|
|
window().unwrap_throw().location().href().unwrap_throw()
|
|
}
|
|
|
|
// TODO inline ?
|
|
fn change_url(mutable: &Mutable<Url>) {
|
|
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).unwrap_throw();
|
|
}
|
|
}
|
|
|
|
|
|
struct CurrentUrl {
|
|
value: Mutable<Url>,
|
|
_listener: EventListener,
|
|
}
|
|
|
|
impl CurrentUrl {
|
|
fn new() -> Self {
|
|
// TODO can this be made more efficient ?
|
|
let value = Mutable::new(Url::new(¤t_url_string()).unwrap_throw());
|
|
|
|
Self {
|
|
_listener: EventListener::new(&window().unwrap_throw(), "popstate", {
|
|
let value = value.clone();
|
|
move |_| {
|
|
change_url(&value);
|
|
}
|
|
}),
|
|
value,
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO somehow share this safely between threads ?
|
|
thread_local! {
|
|
static URL: CurrentUrl = CurrentUrl::new();
|
|
}
|
|
|
|
|
|
#[inline]
|
|
pub fn current_url() -> Url {
|
|
URL.with(|url| url.value.get_cloned())
|
|
}
|
|
|
|
|
|
#[inline]
|
|
pub fn url() -> impl Signal<Item = Url> {
|
|
URL.with(|url| 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) {
|
|
window()
|
|
.unwrap_throw()
|
|
.history()
|
|
.unwrap_throw()
|
|
// TODO is this the best state object to use ?
|
|
.push_state_with_url(&JsValue::NULL, "", Some(new_url))
|
|
.unwrap_throw();
|
|
|
|
URL.with(|url| {
|
|
change_url(&url.value);
|
|
});
|
|
}
|
|
|
|
|
|
// TODO somehow use &str rather than String, maybe Cow ?
|
|
#[inline]
|
|
pub fn on_click_go_to_url<A>(new_url: String) -> impl FnOnce(DomBuilder<A>) -> DomBuilder<A> where A: AsRef<EventTarget> {
|
|
#[inline]
|
|
move |dom| {
|
|
dom.event_preventable(move |e: events::Click| {
|
|
e.prevent_default();
|
|
go_to_url(&new_url);
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
// TODO better type than HtmlElement
|
|
#[inline]
|
|
pub fn link<F>(url: &str, f: F) -> Dom where F: FnOnce(DomBuilder<HtmlElement>) -> DomBuilder<HtmlElement> {
|
|
html!("a", {
|
|
.attribute("href", url)
|
|
// TODO somehow avoid this allocation
|
|
.apply(on_click_go_to_url(url.to_string()))
|
|
.apply(f)
|
|
})
|
|
}
|