perseus/template/core/
state_setters.rs1#[cfg(engine)]
2use super::super::fn_types::*;
3use super::TemplateInner;
4#[cfg(engine)]
5use crate::errors::*;
6use crate::{
7 reactor::Reactor,
8 state::{AnyFreeze, MakeRx, MakeUnrx, UnreactiveState},
9};
10#[cfg(engine)]
11use http::HeaderMap;
12use serde::{de::DeserializeOwned, Serialize};
13use sycamore::prelude::BoundedScope;
14use sycamore::prelude::{create_child_scope, create_ref};
15#[cfg(engine)]
16use sycamore::web::SsrNode;
17use sycamore::{prelude::Scope, view::View, web::Html};
18
19impl<G: Html> TemplateInner<G> {
20 pub fn view_with_state<I, F>(mut self, val: F) -> Self
31 where
32 F: for<'app, 'child> Fn(BoundedScope<'app, 'child>, &'child I) -> View<G>
34 + Send
35 + Sync
36 + 'static,
37 I: MakeUnrx + AnyFreeze + Clone,
38 I::Unrx: MakeRx<Rx = I> + Serialize + DeserializeOwned + Send + Sync + Clone + 'static,
39 {
40 self.view = Box::new(
41 #[allow(unused_variables)]
42 move |app_cx, preload_info, template_state, path| {
43 let reactor = Reactor::<G>::from_cx(app_cx);
44 let intermediate_state =
46 reactor.get_page_state::<I::Unrx>(&path, template_state)?;
47 let mut view = View::empty();
52 let disposer = ::sycamore::reactive::create_child_scope(app_cx, |child_cx| {
53 #[cfg(any(client, doc))]
55 intermediate_state.compute_suspense(child_cx);
56
57 view = val(child_cx, create_ref(child_cx, intermediate_state));
58 });
59 Ok((view, disposer))
60 },
61 );
62 self
63 }
64 pub fn view_with_unreactive_state<F, S>(mut self, val: F) -> Self
67 where
68 F: Fn(Scope, S) -> View<G> + Send + Sync + 'static,
69 S: MakeRx + Serialize + DeserializeOwned + UnreactiveState + 'static,
70 <S as MakeRx>::Rx: AnyFreeze + Clone + MakeUnrx<Unrx = S>,
71 {
72 self.view = Box::new(
73 #[allow(unused_variables)]
74 move |app_cx, preload_info, template_state, path| {
75 let reactor = Reactor::<G>::from_cx(app_cx);
76 let intermediate_state = reactor.get_page_state::<S>(&path, template_state)?;
78 let mut view = View::empty();
79 let disposer = create_child_scope(app_cx, |child_cx| {
80 view = val(child_cx, intermediate_state.make_unrx());
83 });
84 Ok((view, disposer))
85 },
86 );
87 self
88 }
89
90 pub fn view<F>(mut self, val: F) -> Self
94 where
95 F: Fn(Scope) -> View<G> + Send + Sync + 'static,
96 {
97 self.view = Box::new(move |app_cx, _preload_info, _template_state, path| {
98 let reactor = Reactor::<G>::from_cx(app_cx);
99 reactor.register_no_state(&path, false);
102
103 let mut view = View::empty();
106 let disposer = ::sycamore::reactive::create_child_scope(app_cx, |child_cx| {
107 view = val(child_cx);
108 });
109 Ok((view, disposer))
110 });
111 self
112 }
113
114 #[cfg(engine)]
121 pub fn head_with_state<S, V>(
122 mut self,
123 val: impl Fn(Scope, S) -> V + Send + Sync + 'static,
124 ) -> Self
125 where
126 S: Serialize + DeserializeOwned + MakeRx + 'static,
127 V: Into<GeneratorResult<View<SsrNode>>>,
128 {
129 let template_name = self.get_path();
130 self.head = Some(Box::new(move |cx, template_state| {
131 if template_state.is_empty() {
133 return Err(ClientError::InvariantError(ClientInvariantError::NoState).into());
134 }
135 let typed_state = template_state.change_type::<S>();
138
139 let state =
140 match typed_state.into_concrete() {
141 Ok(state) => state,
142 Err(err) => {
143 return Err(ClientError::InvariantError(
144 ClientInvariantError::InvalidState { source: err },
145 )
146 .into())
147 }
148 };
149
150 let template_name = template_name.clone();
151 val(cx, state)
152 .into()
153 .into_server_result("head", template_name)
154 }));
155 self
156 }
157 #[cfg(any(client, doc))]
164 pub fn head_with_state(self, _val: impl Fn() + 'static) -> Self {
165 self
166 }
167
168 #[cfg(engine)]
172 pub fn set_headers_with_state<S, V>(
173 mut self,
174 val: impl Fn(Scope, S) -> V + Send + Sync + 'static,
175 ) -> Self
176 where
177 S: Serialize + DeserializeOwned + MakeRx + 'static,
178 V: Into<GeneratorResult<HeaderMap>>,
179 {
180 let template_name = self.get_path();
181 self.set_headers = Some(Box::new(move |cx, template_state| {
182 if template_state.is_empty() {
184 return Err(ClientError::InvariantError(ClientInvariantError::NoState).into());
185 }
186 let typed_state = template_state.change_type::<S>();
189
190 let state =
191 match typed_state.into_concrete() {
192 Ok(state) => state,
193 Err(err) => {
194 return Err(ClientError::InvariantError(
195 ClientInvariantError::InvalidState { source: err },
196 )
197 .into())
198 }
199 };
200
201 let template_name = template_name.clone();
202 val(cx, state)
203 .into()
204 .into_server_result("set_headers", template_name)
205 }));
206 self
207 }
208 #[cfg(any(client, doc))]
212 pub fn set_headers_with_state(self, _val: impl Fn() + 'static) -> Self {
213 self
214 }
215}