fix scroll history

This commit is contained in:
Evan Almloff committed 2023-05-23 12:59:22 -05:00
1 parent dd48c5a163
commit f94dc85491
1 file changed
+41 -18
+41 -18
View File
@@ -1,7 +1,6 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use gloo::{events::EventListener, render::AnimationFrame}; use gloo::{console::error, events::EventListener, render::AnimationFrame};
use log::error;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use web_sys::{window, History, ScrollRestoration, Window}; use web_sys::{window, History, ScrollRestoration, Window};
@@ -13,6 +12,16 @@ use super::{
HistoryProvider, HistoryProvider,
}; };
fn update_scroll<R: Serialize + DeserializeOwned>(window: &Window, history: &History) {
if let Some(WebHistoryState { state, .. }) = get_current::<WebHistoryState<R>>(history) {
let scroll = ScrollPosition::of_window(window);
let state = WebHistoryState { state, scroll };
if let Err(err) = replace_state_with_url(history, &state, None) {
error!(err);
}
}
}
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
struct WebHistoryState<R> { struct WebHistoryState<R> {
state: R, state: R,
@@ -45,18 +54,24 @@ pub struct WebHistory<R: Serialize + DeserializeOwned> {
phantom: std::marker::PhantomData<R>, phantom: std::marker::PhantomData<R>,
} }
impl<R: Serialize + DeserializeOwned> Default for WebHistory<R> { impl<R: Serialize + DeserializeOwned + Routable> Default for WebHistory<R>
where
<R as std::str::FromStr>::Err: std::fmt::Display,
{
fn default() -> Self { fn default() -> Self {
Self::new(None, true) Self::new(None, true)
} }
} }
impl<R: Serialize + DeserializeOwned> WebHistory<R> { impl<R: Serialize + DeserializeOwned + Routable> WebHistory<R> {
/// Create a new [`WebHistory`]. /// Create a new [`WebHistory`].
/// ///
/// If `do_scroll_restoration` is [`true`], [`WebHistory`] will take control of the history /// If `do_scroll_restoration` is [`true`], [`WebHistory`] will take control of the history
/// state. It'll also set the browsers scroll restoration to `manual`. /// state. It'll also set the browsers scroll restoration to `manual`.
pub fn new(prefix: Option<String>, do_scroll_restoration: bool) -> Self { pub fn new(prefix: Option<String>, do_scroll_restoration: bool) -> Self
where
<R as std::str::FromStr>::Err: std::fmt::Display,
{
let window = window().expect("access to `window`"); let window = window().expect("access to `window`");
let history = window.history().expect("`window` has access to `history`"); let history = window.history().expect("`window` has access to `history`");
@@ -69,15 +84,14 @@ impl<R: Serialize + DeserializeOwned> WebHistory<R> {
let h = history.clone(); let h = history.clone();
let document = w.document().expect("`window` has access to `document`"); let document = w.document().expect("`window` has access to `document`");
// Some(EventListener::new(&document, "scroll", move |_| { Some(EventListener::new(&document, "scroll", move |_| {
// set_state(&w, &h); update_scroll::<R>(&w, &h);
// })) }))
None
} }
false => None, false => None,
}; };
Self { let myself = Self {
do_scroll_restoration, do_scroll_restoration,
history, history,
listener_navigation: None, listener_navigation: None,
@@ -86,7 +100,14 @@ impl<R: Serialize + DeserializeOwned> WebHistory<R> {
prefix, prefix,
window, window,
phantom: Default::default(), phantom: Default::default(),
} };
let state = myself.current_route();
let state = myself.create_state(state);
let _ = replace_state_with_url(&myself.history, &state, None);
myself
} }
fn create_state(&self, state: R) -> WebHistoryState<R> { fn create_state(&self, state: R) -> WebHistoryState<R> {
@@ -124,13 +145,13 @@ where
fn go_back(&mut self) { fn go_back(&mut self) {
if let Err(e) = self.history.back() { if let Err(e) = self.history.back() {
error!("failed to go back: {e:?}") error!("failed to go back: ", e)
} }
} }
fn go_forward(&mut self) { fn go_forward(&mut self) {
if let Err(e) = self.history.forward() { if let Err(e) = self.history.forward() {
error!("failed to go forward: {e:?}") error!("failed to go forward: ", e)
} }
} }
@@ -150,7 +171,7 @@ where
self.window.scroll_to_with_x_and_y(0.0, 0.0) self.window.scroll_to_with_x_and_y(0.0, 0.0)
} }
} }
Err(e) => error!("failed to push state: {e:?}"), Err(e) => error!("failed to push state: ", e),
} }
} }
@@ -170,7 +191,7 @@ where
self.window.scroll_to_with_x_and_y(0.0, 0.0) self.window.scroll_to_with_x_and_y(0.0, 0.0)
} }
} }
Err(e) => error!("failed to replace state: {e:?}"), Err(e) => error!("failed to replace state:", e),
} }
} }
@@ -178,7 +199,7 @@ where
match self.window.location().set_href(&url) { match self.window.location().set_href(&url) {
Ok(_) => true, Ok(_) => true,
Err(e) => { Err(e) => {
error!("failed to navigate to external url (`{url}): {e:?}"); error!("failed to navigate to external url (", url, "): ", e);
false false
} }
} }
@@ -193,8 +214,10 @@ where
self.listener_navigation = Some(EventListener::new(&self.window, "popstate", move |_| { self.listener_navigation = Some(EventListener::new(&self.window, "popstate", move |_| {
(*callback)(); (*callback)();
if d { if d {
// let mut s = s.lock().expect("unpoisoned scroll mutex"); let mut s = s.lock().expect("unpoisoned scroll mutex");
// *s = Some(update_scroll(&w, &h)); if let Some(current_state) = get_current::<WebHistoryState<R>>(&h) {
*s = Some(current_state.scroll.scroll_to(w.clone()));
}
} }
})); }));
} }