Summary
Short overview of the proposal.
Motivation
Why are we doing this? What problems does it solve?
There's a JavaScript package history which provides a consistent API to access Session History in both browser and non-browser environment.
This is currently implemented in yew-router, but I think it might be a better fit for gloo. This issue proposes to upstream the history implementation to gloo.
Detailed Explanation
There're 2 traits that will be introduced:
- History (API similar to window.history)
- Location (API similar to window.location)
There're a couple flavours of History and Location:
BrowserHistory and BrowserLocation (history managed via window.history.pushState)
HashHistory and HashLocation (history managed via window.history.pushState, but path is stored in location.hash)
MemoryHistory and MemoryLocation (used in environment where Web API is not available)
AnyHistory and AnyLocation (a enum that helps to access history and location when it's variant is not known.)
pub trait History {
type Location: Location<History = Self> + 'static;
/// Returns the number of elements in [`History`].
fn len(&self) -> usize;
/// Returns true if the current [`History`] is empty.
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Moves back 1 page in [`History`].
fn back(&self) {
self.go(-1);
}
/// Moves forward 1 page in [`History`].
fn forward(&self) {
self.go(1);
}
/// Loads a specific page in [`History`] with a `delta` relative to current page.
///
/// See: <https://developer.mozilla.org/en-US/docs/Web/API/History/go>
fn go(&self, delta: isize);
/// Pushes a route with [`None`] being the state.
fn push(&self, route: impl Into<String>);
/// Replaces the current history entry with provided route and [`None`] state.
fn replace(&self, route: impl Into<String>);
/// Pushes a route entry with state.
///
/// The implementation of state serialization differs between [`History`] types.
///
/// For [`BrowserHistory`], it uses [`serde_wasm_bindgen`] where as other types uses
/// [`Any`](std::any::Any).
fn push_with_state<T>(&self, route: impl Into<String>, state: T) -> HistoryResult<()>
where
T: Serialize + 'static;
/// Replaces the current history entry with provided route and state.
///
/// The implementation of state serialization differs between [`History`] types.
///
/// For [`BrowserHistory`], it uses [`serde_wasm_bindgen`] where as other types uses
/// [`Any`](std::any::Any).
fn replace_with_state<T>(&self, route: impl Into<String>, state: T) -> HistoryResult<()>
where
T: Serialize + 'static;
/// Same as `.push()` but affix the queries to the end of the route.
fn push_with_query<Q>(&self, route: impl Into<String>, query: Q) -> HistoryResult<()>
where
Q: Serialize;
/// Same as `.replace()` but affix the queries to the end of the route.
fn replace_with_query<Q>(&self, route: impl Into<String>, query: Q) -> HistoryResult<()>
where
Q: Serialize;
/// Same as `.push_with_state()` but affix the queries to the end of the route.
fn push_with_query_and_state<Q, T>(
&self,
route: impl Into<String>,
query: Q,
state: T,
) -> HistoryResult<()>
where
Q: Serialize,
T: Serialize + 'static;
/// Same as `.replace_with_state()` but affix the queries to the end of the route.
fn replace_with_query_and_state<Q, T>(
&self,
route: impl Into<String>,
query: Q,
state: T,
) -> HistoryResult<()>
where
Q: Serialize,
T: Serialize + 'static;
/// Creates a Listener that will be notified when current state changes.
///
/// This method returns a [`HistoryListener`] that will automatically unregister the callback
/// when dropped.
fn listen<CB>(&self, callback: CB) -> HistoryListener
where
CB: Fn() + 'static;
/// Returns the associated [`Location`] of the current history.
fn location(&self) -> Self::Location;
fn into_any_history(self) -> AnyHistory;
/// Returns the State.
///
/// The implementation differs between [`History`] type.
///
/// For [`BrowserHistory`], it uses [`serde_wasm_bindgen`] where as other types uses
/// `downcast_ref()` on [`Any`](std::any::Any).
fn state<T>(&self) -> HistoryResult<T>
where
T: DeserializeOwned + 'static;
}
pub trait Location {
type History: History<Location = Self> + 'static;
/// Returns the `pathname` on the [`Location`] struct.
fn pathname(&self) -> String;
/// Returns the queries of current URL in [`String`]
fn search(&self) -> String;
/// Returns the queries of current URL parsed as `T`.
fn query<T>(&self) -> HistoryResult<T>
where
T: DeserializeOwned;
/// Returns the hash fragment of current URL.
fn hash(&self) -> String;
}
Full Implementation: https://github.com/yewstack/yew/blob/master/packages/yew-router/src/history.rs
Drawbacks, Rationale, and Alternatives
Does this design have drawbacks? Are there alternative approaches? Why is this
design the best of all designs available?
This is a design that is familiar to anyone that have used browser's native history management API and is also used by reacr-router.
What prior art exists? There are many good sources of inspiration: Ember, React,
Angular, Vue, Knockout, jQuery, Closure, Elm, Emscripten, ClojureScript,
Polymer, etc..
https://github.com/remix-run/history
Unresolved Questions
What is not clear yet? What do we expect to clarify through implementation
and/or usage experience?
Summary
Short overview of the proposal.
Motivation
Why are we doing this? What problems does it solve?
There's a JavaScript package
historywhich provides a consistent API to access Session History in both browser and non-browser environment.This is currently implemented in
yew-router, but I think it might be a better fit forgloo. This issue proposes to upstream the history implementation togloo.Detailed Explanation
There're 2 traits that will be introduced:
There're a couple flavours of
HistoryandLocation:BrowserHistoryandBrowserLocation(history managed viawindow.history.pushState)HashHistoryandHashLocation(history managed viawindow.history.pushState, but path is stored inlocation.hash)MemoryHistoryandMemoryLocation(used in environment where Web API is not available)AnyHistoryandAnyLocation(a enum that helps to access history and location when it's variant is not known.)Full Implementation: https://github.com/yewstack/yew/blob/master/packages/yew-router/src/history.rs
Drawbacks, Rationale, and Alternatives
Does this design have drawbacks? Are there alternative approaches? Why is this
design the best of all designs available?
This is a design that is familiar to anyone that have used browser's native history management API and is also used by
reacr-router.What prior art exists? There are many good sources of inspiration: Ember, React,
Angular, Vue, Knockout, jQuery, Closure, Elm, Emscripten, ClojureScript,
Polymer, etc..
https://github.com/remix-run/history
Unresolved Questions
What is not clear yet? What do we expect to clarify through implementation
and/or usage experience?