router.rs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. use std::{
  2. collections::HashSet,
  3. sync::{Arc, RwLock, RwLockWriteGuard},
  4. };
  5. use dioxus::prelude::*;
  6. use crate::{
  7. history::HistoryProvider, navigation::NavigationTarget, routable::Routable,
  8. router_cfg::RouterConfig,
  9. };
  10. /// An error that can occur when navigating.
  11. #[derive(Debug, Clone)]
  12. pub struct ExternalNavigationFailure(String);
  13. /// A function the router will call after every routing update.
  14. pub(crate) type RoutingCallback<R> =
  15. Arc<dyn Fn(GenericRouterContext<R>) -> Option<NavigationTarget<R>>>;
  16. struct MutableRouterState<R>
  17. where
  18. R: Routable,
  19. {
  20. /// Whether there is a previous page to navigate back to.
  21. ///
  22. /// Even if this is [`true`], there might not be a previous page. However, it is nonetheless
  23. /// safe to tell the router to go back.
  24. can_go_back: bool,
  25. /// Whether there is a future page to navigate forward to.
  26. ///
  27. /// Even if this is [`true`], there might not be a future page. However, it is nonetheless safe
  28. /// to tell the router to go forward.
  29. can_go_forward: bool,
  30. /// The current prefix.
  31. prefix: Option<String>,
  32. history: Box<dyn HistoryProvider<R>>,
  33. unresolved_error: Option<ExternalNavigationFailure>,
  34. }
  35. /// A collection of router data that manages all routing functionality.
  36. pub struct GenericRouterContext<R>
  37. where
  38. R: Routable,
  39. {
  40. state: Arc<RwLock<MutableRouterState<R>>>,
  41. subscribers: Arc<RwLock<HashSet<ScopeId>>>,
  42. subscriber_update: Arc<dyn Fn(ScopeId)>,
  43. routing_callback: Option<RoutingCallback<R>>,
  44. failure_external_navigation: fn(Scope) -> Element,
  45. }
  46. impl<R: Routable> Clone for GenericRouterContext<R> {
  47. fn clone(&self) -> Self {
  48. Self {
  49. state: self.state.clone(),
  50. subscribers: self.subscribers.clone(),
  51. subscriber_update: self.subscriber_update.clone(),
  52. routing_callback: self.routing_callback.clone(),
  53. failure_external_navigation: self.failure_external_navigation,
  54. }
  55. }
  56. }
  57. impl<R> GenericRouterContext<R>
  58. where
  59. R: Routable,
  60. {
  61. pub(crate) fn new(cfg: RouterConfig<R>, mark_dirty: Arc<dyn Fn(ScopeId) + Sync + Send>) -> Self
  62. where
  63. R: Clone,
  64. {
  65. let state = Arc::new(RwLock::new(MutableRouterState {
  66. can_go_back: false,
  67. can_go_forward: false,
  68. prefix: Default::default(),
  69. history: cfg.history,
  70. unresolved_error: None,
  71. }));
  72. let subscriber_update = mark_dirty.clone();
  73. let subscribers = Arc::new(RwLock::new(HashSet::new()));
  74. let myself = Self {
  75. state,
  76. subscribers: subscribers.clone(),
  77. subscriber_update,
  78. routing_callback: cfg.on_update,
  79. failure_external_navigation: cfg.failure_external_navigation,
  80. };
  81. // set the updater
  82. {
  83. let mut state = myself.state.write().unwrap();
  84. state.history.updater(Arc::new(move || {
  85. for &id in subscribers.read().unwrap().iter() {
  86. (mark_dirty)(id);
  87. }
  88. }));
  89. }
  90. myself
  91. }
  92. /// Check whether there is a previous page to navigate back to.
  93. #[must_use]
  94. pub fn can_go_back(&self) -> bool {
  95. self.state.read().unwrap().can_go_back
  96. }
  97. /// Check whether there is a future page to navigate forward to.
  98. #[must_use]
  99. pub fn can_go_forward(&self) -> bool {
  100. self.state.read().unwrap().can_go_forward
  101. }
  102. /// Go back to the previous location.
  103. ///
  104. /// Will fail silently if there is no previous location to go to.
  105. pub fn go_back(&self) {
  106. {
  107. self.state.write().unwrap().history.go_back();
  108. }
  109. self.change_route();
  110. }
  111. /// Go back to the next location.
  112. ///
  113. /// Will fail silently if there is no next location to go to.
  114. pub fn go_forward(&self) {
  115. {
  116. self.state.write().unwrap().history.go_forward();
  117. }
  118. self.change_route();
  119. }
  120. /// Push a new location.
  121. ///
  122. /// The previous location will be available to go back to.
  123. pub fn push(
  124. &self,
  125. target: impl Into<NavigationTarget<R>>,
  126. ) -> Option<ExternalNavigationFailure> {
  127. let target = target.into();
  128. match target {
  129. NavigationTarget::Internal(p) => {
  130. let mut state = self.state_mut();
  131. state.history.push(p)
  132. }
  133. NavigationTarget::External(e) => return self.external(e),
  134. }
  135. self.change_route()
  136. }
  137. /// Replace the current location.
  138. ///
  139. /// The previous location will **not** be available to go back to.
  140. pub fn replace(
  141. &self,
  142. target: impl Into<NavigationTarget<R>>,
  143. ) -> Option<ExternalNavigationFailure> {
  144. let target = target.into();
  145. {
  146. let mut state = self.state_mut();
  147. match target {
  148. NavigationTarget::Internal(p) => state.history.replace(p),
  149. NavigationTarget::External(e) => return self.external(e),
  150. }
  151. }
  152. self.change_route()
  153. }
  154. /// The route that is currently active.
  155. pub fn current(&self) -> R
  156. where
  157. R: Clone,
  158. {
  159. self.state.read().unwrap().history.current_route()
  160. }
  161. /// The prefix that is currently active.
  162. pub fn prefix(&self) -> Option<String> {
  163. self.state.read().unwrap().prefix.clone()
  164. }
  165. fn external(&self, external: String) -> Option<ExternalNavigationFailure> {
  166. let mut state = self.state_mut();
  167. match state.history.external(external.clone()) {
  168. true => None,
  169. false => {
  170. let failure = ExternalNavigationFailure(external);
  171. state.unresolved_error = Some(failure.clone());
  172. self.update_subscribers();
  173. Some(failure)
  174. }
  175. }
  176. }
  177. fn state_mut(&self) -> RwLockWriteGuard<MutableRouterState<R>> {
  178. self.state.write().unwrap()
  179. }
  180. /// Manually subscribe to the current route
  181. pub fn subscribe(&self, id: ScopeId) {
  182. self.subscribers.write().unwrap().insert(id);
  183. }
  184. /// Manually unsubscribe from the current route
  185. pub fn unsubscribe(&self, id: ScopeId) {
  186. self.subscribers.write().unwrap().remove(&id);
  187. }
  188. fn update_subscribers(&self) {
  189. for &id in self.subscribers.read().unwrap().iter() {
  190. (self.subscriber_update)(id);
  191. }
  192. }
  193. /// Clear any unresolved errors
  194. pub fn clear_error(&self) {
  195. self.state.write().unwrap().unresolved_error = None;
  196. self.update_subscribers();
  197. }
  198. pub(crate) fn render_error<'a>(&self, cx: Scope<'a>) -> Element<'a> {
  199. self.state
  200. .read()
  201. .unwrap()
  202. .unresolved_error
  203. .as_ref()
  204. .and_then(|_| (self.failure_external_navigation)(cx))
  205. }
  206. fn change_route(&self) -> Option<ExternalNavigationFailure> {
  207. if let Some(callback) = &self.routing_callback {
  208. let myself = self.clone();
  209. if let Some(new) = callback(myself) {
  210. let mut state = self.state_mut();
  211. match new {
  212. NavigationTarget::Internal(p) => state.history.replace(p),
  213. NavigationTarget::External(e) => return self.external(e),
  214. }
  215. }
  216. }
  217. self.update_subscribers();
  218. None
  219. }
  220. }