mod.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. //! History Integration
  2. //!
  3. //! dioxus-router-core relies on [`HistoryProvider`]s to store the current Route, and possibly a
  4. //! history (i.e. a browsers back button) and future (i.e. a browsers forward button).
  5. //!
  6. //! To integrate dioxus-router with a any type of history, all you have to do is implement the
  7. //! [`HistoryProvider`] trait.
  8. //!
  9. //! dioxus-router contains two built in history providers:
  10. //! 1) [`MemoryHistory`] for desktop/mobile/ssr platforms
  11. //! 2) [`WebHistory`] for web platforms
  12. use std::{any::Any, rc::Rc, sync::Arc};
  13. mod memory;
  14. pub use memory::*;
  15. #[cfg(feature = "web")]
  16. mod web;
  17. #[cfg(feature = "web")]
  18. pub use web::*;
  19. #[cfg(feature = "web")]
  20. pub(crate) mod web_history;
  21. // #[cfg(feature = "web")]
  22. // mod web_hash;
  23. // #[cfg(feature = "web")]
  24. // pub use web_hash::*;
  25. use crate::routable::Routable;
  26. #[cfg(feature = "web")]
  27. pub(crate) mod web_scroll;
  28. /// An integration with some kind of navigation history.
  29. ///
  30. /// Depending on your use case, your implementation may deviate from the described procedure. This
  31. /// is fine, as long as both `current_route` and `current_query` match the described format.
  32. ///
  33. /// However, you should document all deviations. Also, make sure the navigation is user-friendly.
  34. /// The described behaviors are designed to mimic a web browser, which most users should already
  35. /// know. Deviations might confuse them.
  36. pub trait HistoryProvider<R: Routable> {
  37. /// Get the path of the current URL.
  38. ///
  39. /// **Must start** with `/`. **Must _not_ contain** the prefix.
  40. ///
  41. /// ```rust
  42. /// # use dioxus_router::prelude::*;
  43. /// # use dioxus::prelude::*;
  44. /// # #[inline_props]
  45. /// # fn Index(cx: Scope) -> Element { todo!() }
  46. /// # #[inline_props]
  47. /// # fn OtherPage(cx: Scope) -> Element { todo!() }
  48. /// #[derive(Clone, Routable, Debug, PartialEq)]
  49. /// enum Route {
  50. /// #[route("/")]
  51. /// Index {},
  52. /// #[route("/some-other-page")]
  53. /// OtherPage {},
  54. /// }
  55. /// let mut history = MemoryHistory::<Route>::default();
  56. /// assert_eq!(history.current_route().to_string(), "/");
  57. ///
  58. /// history.push(Route::OtherPage {});
  59. /// assert_eq!(history.current_route().to_string(), "/some-other-page");
  60. /// ```
  61. #[must_use]
  62. fn current_route(&self) -> R;
  63. /// Get the current path prefix of the URL.
  64. ///
  65. /// Not all [`HistoryProvider`]s need a prefix feature. It is meant for environments where a
  66. /// dioxus-router-core-routed application is not running on `/`. The [`HistoryProvider`] is responsible
  67. /// for removing the prefix from the dioxus-router-core-internal path, and also for adding it back in
  68. /// during navigation. This functions value is only used for creating `href`s (e.g. for SSR or
  69. /// display (but not navigation) in a web app).
  70. fn current_prefix(&self) -> Option<String> {
  71. None
  72. }
  73. /// Check whether there is a previous page to navigate back to.
  74. ///
  75. /// If a [`HistoryProvider`] cannot know this, it should return [`true`].
  76. ///
  77. /// ```rust
  78. /// # use dioxus_router::prelude::*;
  79. /// # use dioxus::prelude::*;
  80. /// # #[inline_props]
  81. /// # fn Index(cx: Scope) -> Element { todo!() }
  82. /// #[derive(Clone, Routable, Debug, PartialEq)]
  83. /// enum Route {
  84. /// #[route("/")]
  85. /// Index {},
  86. /// }
  87. /// let mut history = MemoryHistory::<Route>::default();
  88. /// assert_eq!(history.can_go_back(), false);
  89. ///
  90. /// history.push(Route::Index {});
  91. /// assert_eq!(history.can_go_back(), true);
  92. /// ```
  93. #[must_use]
  94. fn can_go_back(&self) -> bool {
  95. true
  96. }
  97. /// Go back to a previous page.
  98. ///
  99. /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method
  100. /// might be called, even if `can_go_back` returns [`false`].
  101. ///
  102. /// ```rust
  103. /// # use dioxus_router::prelude::*;
  104. /// # use dioxus::prelude::*;
  105. /// # #[inline_props]
  106. /// # fn Index(cx: Scope) -> Element { todo!() }
  107. /// # #[inline_props]
  108. /// # fn OtherPage(cx: Scope) -> Element { todo!() }
  109. /// #[derive(Clone, Routable, Debug, PartialEq)]
  110. /// enum Route {
  111. /// #[route("/")]
  112. /// Index {},
  113. /// #[route("/some-other-page")]
  114. /// OtherPage {},
  115. /// }
  116. /// let mut history = MemoryHistory::<Route>::default();
  117. /// assert_eq!(history.current_route().to_string(), "/");
  118. ///
  119. /// history.go_back();
  120. /// assert_eq!(history.current_route().to_string(), "/");
  121. ///
  122. /// history.push(Route::OtherPage {});
  123. /// assert_eq!(history.current_route().to_string(), "/some-other-page");
  124. ///
  125. /// history.go_back();
  126. /// assert_eq!(history.current_route().to_string(), "/");
  127. /// ```
  128. fn go_back(&mut self);
  129. /// Check whether there is a future page to navigate forward to.
  130. ///
  131. /// If a [`HistoryProvider`] cannot know this, it should return [`true`].
  132. ///
  133. /// ```rust
  134. /// # use dioxus_router::prelude::*;
  135. /// # use dioxus::prelude::*;
  136. /// # #[inline_props]
  137. /// # fn Index(cx: Scope) -> Element { todo!() }
  138. /// # #[inline_props]
  139. /// # fn OtherPage(cx: Scope) -> Element { todo!() }
  140. /// #[derive(Clone, Routable, Debug, PartialEq)]
  141. /// enum Route {
  142. /// #[route("/")]
  143. /// Index {},
  144. /// #[route("/some-other-page")]
  145. /// OtherPage {},
  146. /// }
  147. /// let mut history = MemoryHistory::<Route>::default();
  148. /// assert_eq!(history.can_go_forward(), false);
  149. ///
  150. /// history.push(Route::OtherPage {});
  151. /// assert_eq!(history.can_go_forward(), false);
  152. ///
  153. /// history.go_back();
  154. /// assert_eq!(history.can_go_forward(), true);
  155. /// ```
  156. #[must_use]
  157. fn can_go_forward(&self) -> bool {
  158. true
  159. }
  160. /// Go forward to a future page.
  161. ///
  162. /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method
  163. /// might be called, even if `can_go_forward` returns [`false`].
  164. ///
  165. /// ```rust
  166. /// # use dioxus_router::prelude::*;
  167. /// # use dioxus::prelude::*;
  168. /// # #[inline_props]
  169. /// # fn Index(cx: Scope) -> Element { todo!() }
  170. /// # #[inline_props]
  171. /// # fn OtherPage(cx: Scope) -> Element { todo!() }
  172. /// #[derive(Clone, Routable, Debug, PartialEq)]
  173. /// enum Route {
  174. /// #[route("/")]
  175. /// Index {},
  176. /// #[route("/some-other-page")]
  177. /// OtherPage {},
  178. /// }
  179. /// let mut history = MemoryHistory::<Route>::default();
  180. /// history.push(Route::OtherPage {});
  181. /// assert_eq!(history.current_route(), Route::OtherPage {});
  182. ///
  183. /// history.go_back();
  184. /// assert_eq!(history.current_route(), Route::Index {});
  185. ///
  186. /// history.go_forward();
  187. /// assert_eq!(history.current_route(), Route::OtherPage {});
  188. /// ```
  189. fn go_forward(&mut self);
  190. /// Go to another page.
  191. ///
  192. /// This should do three things:
  193. /// 1. Merge the current URL with the `path` parameter (which may also include a query part).
  194. /// 2. Remove the previous URL to the navigation history.
  195. /// 3. Clear the navigation future.
  196. ///
  197. /// ```rust
  198. /// # use dioxus_router::prelude::*;
  199. /// # use dioxus::prelude::*;
  200. /// # #[inline_props]
  201. /// # fn Index(cx: Scope) -> Element { todo!() }
  202. /// # #[inline_props]
  203. /// # fn OtherPage(cx: Scope) -> Element { todo!() }
  204. /// #[derive(Clone, Routable, Debug, PartialEq)]
  205. /// enum Route {
  206. /// #[route("/")]
  207. /// Index {},
  208. /// #[route("/some-other-page")]
  209. /// OtherPage {},
  210. /// }
  211. /// let mut history = MemoryHistory::<Route>::default();
  212. /// assert_eq!(history.current_route(), Route::Index {});
  213. ///
  214. /// history.push(Route::OtherPage {});
  215. /// assert_eq!(history.current_route(), Route::OtherPage {});
  216. /// assert!(history.can_go_back());
  217. /// ```
  218. fn push(&mut self, route: R);
  219. /// Replace the current page with another one.
  220. ///
  221. /// This should merge the current URL with the `path` parameter (which may also include a query
  222. /// part). In contrast to the `push` function, the navigation history and future should stay
  223. /// untouched.
  224. ///
  225. /// ```rust
  226. /// # use dioxus_router::prelude::*;
  227. /// # use dioxus::prelude::*;
  228. /// # #[inline_props]
  229. /// # fn Index(cx: Scope) -> Element { todo!() }
  230. /// # #[inline_props]
  231. /// # fn OtherPage(cx: Scope) -> Element { todo!() }
  232. /// #[derive(Clone, Routable, Debug, PartialEq)]
  233. /// enum Route {
  234. /// #[route("/")]
  235. /// Index {},
  236. /// #[route("/some-other-page")]
  237. /// OtherPage {},
  238. /// }
  239. /// let mut history = MemoryHistory::<Route>::default();
  240. /// assert_eq!(history.current_route(), Route::Index {});
  241. ///
  242. /// history.replace(Route::OtherPage {});
  243. /// assert_eq!(history.current_route(), Route::OtherPage {});
  244. /// assert!(!history.can_go_back());
  245. /// ```
  246. fn replace(&mut self, path: R);
  247. /// Navigate to an external URL.
  248. ///
  249. /// This should navigate to an external URL, which isn't controlled by the router. If a
  250. /// [`HistoryProvider`] cannot do that, it should return [`false`], otherwise [`true`].
  251. ///
  252. /// Returning [`false`] will cause the router to handle the external navigation failure.
  253. #[allow(unused_variables)]
  254. fn external(&mut self, url: String) -> bool {
  255. false
  256. }
  257. /// Provide the [`HistoryProvider`] with an update callback.
  258. ///
  259. /// Some [`HistoryProvider`]s may receive URL updates from outside the router. When such
  260. /// updates are received, they should call `callback`, which will cause the router to update.
  261. #[allow(unused_variables)]
  262. fn updater(&mut self, callback: Arc<dyn Fn() + Send + Sync>) {}
  263. }
  264. pub(crate) trait AnyHistoryProvider {
  265. fn parse_route(&self, route: &str) -> Result<Rc<dyn Any>, String>;
  266. #[must_use]
  267. fn accepts_type_id(&self, type_id: &std::any::TypeId) -> bool;
  268. #[must_use]
  269. fn current_route(&self) -> Rc<dyn Any>;
  270. #[must_use]
  271. fn current_prefix(&self) -> Option<String> {
  272. None
  273. }
  274. #[must_use]
  275. fn can_go_back(&self) -> bool {
  276. true
  277. }
  278. fn go_back(&mut self);
  279. #[must_use]
  280. fn can_go_forward(&self) -> bool {
  281. true
  282. }
  283. fn go_forward(&mut self);
  284. fn push(&mut self, route: Rc<dyn Any>);
  285. fn replace(&mut self, path: Rc<dyn Any>);
  286. #[allow(unused_variables)]
  287. fn external(&mut self, url: String) -> bool {
  288. false
  289. }
  290. #[allow(unused_variables)]
  291. fn updater(&mut self, callback: Arc<dyn Fn() + Send + Sync>) {}
  292. }
  293. pub(crate) struct AnyHistoryProviderImplWrapper<R, H> {
  294. inner: H,
  295. _marker: std::marker::PhantomData<R>,
  296. }
  297. impl<R, H> AnyHistoryProviderImplWrapper<R, H> {
  298. pub fn new(inner: H) -> Self {
  299. Self {
  300. inner,
  301. _marker: std::marker::PhantomData,
  302. }
  303. }
  304. }
  305. impl<R, H: Default> Default for AnyHistoryProviderImplWrapper<R, H> {
  306. fn default() -> Self {
  307. Self::new(H::default())
  308. }
  309. }
  310. impl<R, H> AnyHistoryProvider for AnyHistoryProviderImplWrapper<R, H>
  311. where
  312. R: Routable,
  313. <R as std::str::FromStr>::Err: std::fmt::Display,
  314. H: HistoryProvider<R>,
  315. {
  316. fn parse_route(&self, route: &str) -> Result<Rc<dyn Any>, String> {
  317. R::from_str(route)
  318. .map_err(|err| err.to_string())
  319. .map(|route| Rc::new(route) as Rc<dyn Any>)
  320. }
  321. fn accepts_type_id(&self, type_id: &std::any::TypeId) -> bool {
  322. type_id == &std::any::TypeId::of::<R>()
  323. }
  324. fn current_route(&self) -> Rc<dyn Any> {
  325. let route = self.inner.current_route();
  326. Rc::new(route)
  327. }
  328. fn current_prefix(&self) -> Option<String> {
  329. self.inner.current_prefix()
  330. }
  331. fn can_go_back(&self) -> bool {
  332. self.inner.can_go_back()
  333. }
  334. fn go_back(&mut self) {
  335. self.inner.go_back()
  336. }
  337. fn can_go_forward(&self) -> bool {
  338. self.inner.can_go_forward()
  339. }
  340. fn go_forward(&mut self) {
  341. self.inner.go_forward()
  342. }
  343. fn push(&mut self, route: Rc<dyn Any>) {
  344. self.inner
  345. .push(route.downcast::<R>().unwrap().as_ref().clone())
  346. }
  347. fn replace(&mut self, route: Rc<dyn Any>) {
  348. self.inner
  349. .replace(route.downcast::<R>().unwrap().as_ref().clone())
  350. }
  351. fn external(&mut self, url: String) -> bool {
  352. self.inner.external(url)
  353. }
  354. fn updater(&mut self, callback: Arc<dyn Fn() + Send + Sync>) {
  355. self.inner.updater(callback)
  356. }
  357. }