dioxus_fullstack_hooks/
history.rs1use std::cell::OnceCell;
4
5use dioxus_core::{
6 prelude::{generation, queue_effect},
7 schedule_update,
8};
9use dioxus_history::History;
10
11fn match_hydration<O>(
14 during_hydration: impl FnOnce() -> O,
15 after_hydration: impl FnOnce() -> O,
16) -> O {
17 if generation() == 0 {
18 let update = schedule_update();
19 queue_effect(move || update());
20 during_hydration()
21 } else {
22 after_hydration()
23 }
24}
25
26#[derive(Clone)]
28pub struct FullstackHistory<H> {
29 initial_route: OnceCell<String>,
30 #[cfg(feature = "server")]
31 in_hydration_context: std::cell::Cell<bool>,
32 history: H,
33}
34
35impl<H> FullstackHistory<H> {
36 pub fn new(history: H) -> Self {
38 Self {
39 initial_route: OnceCell::new(),
40 #[cfg(feature = "server")]
41 in_hydration_context: std::cell::Cell::new(false),
42 history,
43 }
44 }
45
46 pub fn new_server(history: H) -> Self
48 where
49 H: History,
50 {
51 let initial_route = history.current_route();
52 let history = Self::new(history);
53 history.initial_route.set(initial_route).unwrap();
54 history
55 }
56
57 fn initial_route(&self) -> String {
59 let entry = dioxus_fullstack_protocol::serialize_context().create_entry();
60 let route = self.initial_route.get_or_init(|| {
61 entry
62 .get()
63 .expect("Failed to get initial route from hydration context")
64 });
65 #[cfg(feature = "server")]
66 if !self.in_hydration_context.get() {
67 entry.insert(route, std::panic::Location::caller());
68 self.in_hydration_context.set(true);
69 }
70 route.clone()
71 }
72}
73
74impl<H: History> History for FullstackHistory<H> {
75 fn current_prefix(&self) -> Option<String> {
76 self.history.current_prefix()
77 }
78
79 fn can_go_back(&self) -> bool {
80 match_hydration(|| false, || self.history.can_go_back())
81 }
82
83 fn can_go_forward(&self) -> bool {
84 match_hydration(|| false, || self.history.can_go_forward())
85 }
86
87 fn external(&self, url: String) -> bool {
88 self.history.external(url)
89 }
90
91 fn updater(&self, callback: std::sync::Arc<dyn Fn() + Send + Sync>) {
92 self.history.updater(callback)
93 }
94
95 fn include_prevent_default(&self) -> bool {
96 self.history.include_prevent_default()
97 }
98
99 fn current_route(&self) -> String {
100 match_hydration(|| self.initial_route(), || self.history.current_route())
101 }
102
103 fn go_back(&self) {
104 self.history.go_back();
105 }
106
107 fn go_forward(&self) {
108 self.history.go_forward();
109 }
110
111 fn push(&self, route: String) {
112 self.history.push(route);
113 }
114
115 fn replace(&self, path: String) {
116 self.history.replace(path);
117 }
118}