use super::super::{util, Url};
use crate::{
app::{subs, Notification},
browser::json,
};
use std::rc::Rc;
use wasm_bindgen::{closure::Closure, JsCast};
pub fn push_route<U: Into<Url>>(url: U) -> Url {
let url = url.into();
let data = json::to_js_value(&url).expect("Problem serializing route data");
util::history()
.push_state_with_url(&data, "", Some(&url.to_string()))
.expect("Problem pushing state");
url
}
pub fn setup_popstate_listener(
updated_listener: impl Fn(Closure<dyn FnMut(web_sys::Event)>) + 'static,
notify: impl Fn(Notification) + 'static,
base_path: Rc<[String]>,
) {
let closure = Closure::new(move |ev: web_sys::Event| {
let ev = ev
.dyn_ref::<web_sys::PopStateEvent>()
.expect("Problem casting as Popstate event");
let url = match json::from_js_value(&ev.state()) {
Ok(url) => url,
Err(_) => Url::current(),
};
notify(Notification::new(subs::UrlChanged(
url.skip_base_path(&base_path),
)));
});
(util::window().as_ref() as &web_sys::EventTarget)
.add_event_listener_with_callback("popstate", closure.as_ref().unchecked_ref())
.expect("Problem adding popstate listener");
updated_listener(closure);
}
#[allow(clippy::needless_pass_by_value)]
pub fn url_request_handler(
sub_data: subs::UrlRequested,
base_path: Rc<[String]>,
notify: impl Fn(Notification) + 'static,
) {
let subs::UrlRequested(url, request) = sub_data;
match request.status() {
subs::url_requested::UrlRequestStatus::Unhandled => {
if let Some(event) = request.event.borrow_mut().take() {
event.prevent_default(); }
let url = url.skip_base_path(&base_path).skip_hash_base_path(&[]);
push_route(url.clone());
notify(Notification::new(subs::UrlChanged(url)));
}
subs::url_requested::UrlRequestStatus::Handled(prevent_default) => {
if prevent_default {
if let Some(event) = request.event.borrow_mut().take() {
event.prevent_default(); }
}
}
}
}
#[allow(clippy::option_map_unit_fn)]
pub fn setup_link_listener<F>(notify: F)
where
F: Fn(Notification) + 'static,
{
let closure: Closure<dyn Fn(web_sys::Event) -> _> =
Closure::new(move |event: web_sys::Event| {
event.target()
.and_then(|et| et.dyn_into::<web_sys::Element>().ok())
.and_then(|el| el.closest("a[href]").ok().flatten())
.and_then(|href_el| {
if href_el.has_attribute("download") {
None
} else {
href_el.get_attribute("href")
}
})
.and_then(|href| {
if href.is_empty() || href.starts_with('/') {
Some(href)
} else {
None
}
})
.map(|href| {
if href.is_empty() {
event.prevent_default(); } else {
let url: Url = href.parse().expect("cast link href to `Url`");
notify(Notification::new(subs::UrlRequested(
url,
subs::url_requested::UrlRequest::new(
subs::url_requested::UrlRequestStatus::default(),
Some(event.clone()),
),
)));
}
});
});
(util::document().as_ref() as &web_sys::EventTarget)
.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())
.expect("Problem setting up link interceptor");
closure.forget(); }