1use crate::{innerlude::*, scope_context::SuspenseLocation};
2
3#[allow(non_camel_case_types)]
5pub struct SuspenseBoundaryProps {
6 fallback: Callback<SuspenseContext, Element>,
7 children: LastRenderedNode,
9}
10
11impl Clone for SuspenseBoundaryProps {
12 fn clone(&self) -> Self {
13 Self {
14 fallback: self.fallback,
15 children: self.children.clone(),
16 }
17 }
18}
19
20impl SuspenseBoundaryProps {
21 #[allow(dead_code, clippy::type_complexity)]
27 fn builder() -> SuspenseBoundaryPropsBuilder<((), ())> {
28 SuspenseBoundaryPropsBuilder {
29 owner: Owner::default(),
30 fields: ((), ()),
31 _phantom: ::core::default::Default::default(),
32 }
33 }
34}
35#[must_use]
36#[doc(hidden)]
37#[allow(dead_code, non_camel_case_types, non_snake_case)]
38pub struct SuspenseBoundaryPropsBuilder<TypedBuilderFields> {
39 owner: Owner,
40 fields: TypedBuilderFields,
41 _phantom: (),
42}
43impl Properties for SuspenseBoundaryProps
44where
45 Self: Clone,
46{
47 type Builder = SuspenseBoundaryPropsBuilder<((), ())>;
48 fn builder() -> Self::Builder {
49 SuspenseBoundaryProps::builder()
50 }
51 fn memoize(&mut self, new: &Self) -> bool {
52 let equal = self == new;
53 self.fallback.__point_to(&new.fallback);
54 if !equal {
55 let new_clone = new.clone();
56 self.children = new_clone.children;
57 }
58 equal
59 }
60}
61#[doc(hidden)]
62#[allow(dead_code, non_camel_case_types, non_snake_case)]
63pub trait SuspenseBoundaryPropsBuilder_Optional<T> {
64 fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
65}
66impl<T> SuspenseBoundaryPropsBuilder_Optional<T> for () {
67 fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
68 default()
69 }
70}
71impl<T> SuspenseBoundaryPropsBuilder_Optional<T> for (T,) {
72 fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
73 self.0
74 }
75}
76#[allow(dead_code, non_camel_case_types, missing_docs)]
77impl<__children> SuspenseBoundaryPropsBuilder<((), __children)> {
78 #[allow(clippy::type_complexity)]
79 pub fn fallback<__Marker>(
80 self,
81 fallback: impl SuperInto<Callback<SuspenseContext, Element>, __Marker>,
82 ) -> SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)> {
83 let fallback = (with_owner(self.owner.clone(), move || {
84 SuperInto::super_into(fallback)
85 }),);
86 let (_, children) = self.fields;
87 SuspenseBoundaryPropsBuilder {
88 owner: self.owner,
89 fields: (fallback, children),
90 _phantom: self._phantom,
91 }
92 }
93}
94#[doc(hidden)]
95#[allow(dead_code, non_camel_case_types, non_snake_case)]
96pub enum SuspenseBoundaryPropsBuilder_Error_Repeated_field_fallback {}
97#[doc(hidden)]
98#[allow(dead_code, non_camel_case_types, missing_docs)]
99impl<__children> SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)> {
100 #[deprecated(note = "Repeated field fallback")]
101 #[allow(clippy::type_complexity)]
102 pub fn fallback(
103 self,
104 _: SuspenseBoundaryPropsBuilder_Error_Repeated_field_fallback,
105 ) -> SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)> {
106 self
107 }
108}
109#[allow(dead_code, non_camel_case_types, missing_docs)]
110impl<__fallback> SuspenseBoundaryPropsBuilder<(__fallback, ())> {
111 #[allow(clippy::type_complexity)]
112 pub fn children(
113 self,
114 children: Element,
115 ) -> SuspenseBoundaryPropsBuilder<(__fallback, (Element,))> {
116 let children = (children,);
117 let (fallback, _) = self.fields;
118 SuspenseBoundaryPropsBuilder {
119 owner: self.owner,
120 fields: (fallback, children),
121 _phantom: self._phantom,
122 }
123 }
124}
125#[doc(hidden)]
126#[allow(dead_code, non_camel_case_types, non_snake_case)]
127pub enum SuspenseBoundaryPropsBuilder_Error_Repeated_field_children {}
128#[doc(hidden)]
129#[allow(dead_code, non_camel_case_types, missing_docs)]
130impl<__fallback> SuspenseBoundaryPropsBuilder<(__fallback, (Element,))> {
131 #[deprecated(note = "Repeated field children")]
132 #[allow(clippy::type_complexity)]
133 pub fn children(
134 self,
135 _: SuspenseBoundaryPropsBuilder_Error_Repeated_field_children,
136 ) -> SuspenseBoundaryPropsBuilder<(__fallback, (Element,))> {
137 self
138 }
139}
140#[doc(hidden)]
141#[allow(dead_code, non_camel_case_types, non_snake_case)]
142pub enum SuspenseBoundaryPropsBuilder_Error_Missing_required_field_fallback {}
143#[doc(hidden)]
144#[allow(dead_code, non_camel_case_types, missing_docs, clippy::panic)]
145impl<__children> SuspenseBoundaryPropsBuilder<((), __children)> {
146 #[deprecated(note = "Missing required field fallback")]
147 pub fn build(
148 self,
149 _: SuspenseBoundaryPropsBuilder_Error_Missing_required_field_fallback,
150 ) -> SuspenseBoundaryProps {
151 panic!()
152 }
153}
154#[doc(hidden)]
155#[allow(dead_code, non_camel_case_types, missing_docs)]
156pub struct SuspenseBoundaryPropsWithOwner {
157 inner: SuspenseBoundaryProps,
158 owner: Owner,
159}
160#[automatically_derived]
161#[allow(dead_code, non_camel_case_types, missing_docs)]
162impl ::core::clone::Clone for SuspenseBoundaryPropsWithOwner {
163 #[inline]
164 fn clone(&self) -> SuspenseBoundaryPropsWithOwner {
165 SuspenseBoundaryPropsWithOwner {
166 inner: ::core::clone::Clone::clone(&self.inner),
167 owner: ::core::clone::Clone::clone(&self.owner),
168 }
169 }
170}
171impl PartialEq for SuspenseBoundaryPropsWithOwner {
172 fn eq(&self, other: &Self) -> bool {
173 self.inner.eq(&other.inner)
174 }
175}
176impl SuspenseBoundaryPropsWithOwner {
177 pub fn into_vcomponent<M: 'static>(
179 self,
180 render_fn: impl ComponentFunction<SuspenseBoundaryProps, M>,
181 ) -> VComponent {
182 let component_name = std::any::type_name_of_val(&render_fn);
183 VComponent::new(
184 move |wrapper: Self| render_fn.rebuild(wrapper.inner),
185 self,
186 component_name,
187 )
188 }
189}
190impl Properties for SuspenseBoundaryPropsWithOwner {
191 type Builder = ();
192 fn builder() -> Self::Builder {
193 unreachable!()
194 }
195 fn memoize(&mut self, new: &Self) -> bool {
196 self.inner.memoize(&new.inner)
197 }
198}
199#[allow(dead_code, non_camel_case_types, missing_docs)]
200impl<__children: SuspenseBoundaryPropsBuilder_Optional<Element>>
201 SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)>
202{
203 pub fn build(self) -> SuspenseBoundaryPropsWithOwner {
204 let (fallback, children) = self.fields;
205 let fallback = fallback.0;
206 let children = SuspenseBoundaryPropsBuilder_Optional::into_value(children, VNode::empty);
207 SuspenseBoundaryPropsWithOwner {
208 inner: SuspenseBoundaryProps {
209 fallback,
210 children: LastRenderedNode::new(children),
211 },
212 owner: self.owner,
213 }
214 }
215}
216#[automatically_derived]
217#[allow(non_camel_case_types)]
218impl ::core::cmp::PartialEq for SuspenseBoundaryProps {
219 #[inline]
220 fn eq(&self, other: &SuspenseBoundaryProps) -> bool {
221 self.fallback == other.fallback && self.children == other.children
222 }
223}
224
225#[allow(non_snake_case)]
242pub fn SuspenseBoundary(mut __props: SuspenseBoundaryProps) -> Element {
243 unreachable!("SuspenseBoundary should not be called directly")
244}
245#[allow(non_snake_case)]
246#[doc(hidden)]
247mod SuspenseBoundary_completions {
248 #[doc(hidden)]
249 #[allow(non_camel_case_types)]
250 pub enum Component {
252 SuspenseBoundary {},
253 }
254}
255use generational_box::Owner;
256#[allow(unused)]
257pub use SuspenseBoundary_completions::Component::SuspenseBoundary;
258
259impl SuspenseBoundaryProps {
261 pub(crate) fn downcast_from_props(props: &mut dyn AnyProps) -> Option<&mut Self> {
263 let inner: Option<&mut SuspenseBoundaryPropsWithOwner> = props.props_mut().downcast_mut();
264 inner.map(|inner| &mut inner.inner)
265 }
266
267 pub(crate) fn create<M: WriteMutations>(
268 mount: MountId,
269 idx: usize,
270 component: &VComponent,
271 parent: Option<ElementRef>,
272 dom: &mut VirtualDom,
273 to: Option<&mut M>,
274 ) -> usize {
275 let mut scope_id = ScopeId(dom.get_mounted_dyn_node(mount, idx));
276 if scope_id.is_placeholder() {
278 {
279 let suspense_context = SuspenseContext::new();
280
281 let suspense_boundary_location =
282 crate::scope_context::SuspenseLocation::SuspenseBoundary(
283 suspense_context.clone(),
284 );
285 dom.runtime
286 .clone()
287 .with_suspense_location(suspense_boundary_location, || {
288 let scope_state = dom
289 .new_scope(component.props.duplicate(), component.name)
290 .state();
291 suspense_context.mount(scope_state.id);
292 scope_id = scope_state.id;
293 });
294 }
295
296 dom.set_mounted_dyn_node(mount, idx, scope_id.0);
298 }
299 dom.runtime.clone().with_scope_on_stack(scope_id, || {
300 let scope_state = &mut dom.scopes[scope_id.0];
301 let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
302 let suspense_context =
303 SuspenseContext::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
304 .unwrap();
305
306 let children = props.children.clone();
307
308 suspense_context.under_suspense_boundary(&dom.runtime(), || {
310 children.create(dom, parent, None::<&mut M>);
311 });
312
313 let scope_state = &mut dom.scopes[scope_id.0];
315 let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
316 props.children.clone_from(&children);
317
318 let scope_state = &mut dom.scopes[scope_id.0];
319 let suspense_context = scope_state
320 .state()
321 .suspense_location()
322 .suspense_context()
323 .unwrap()
324 .clone();
325
326 let nodes_created = if !suspense_context.suspended_futures().is_empty() {
328 let (node, nodes_created) =
329 suspense_context.in_suspense_placeholder(&dom.runtime(), || {
330 let scope_state = &mut dom.scopes[scope_id.0];
331 let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
332 let suspense_context =
333 SuspenseContext::downcast_suspense_boundary_from_scope(
334 &dom.runtime,
335 scope_id,
336 )
337 .unwrap();
338 suspense_context.set_suspended_nodes(children.as_vnode().clone());
339 let suspense_placeholder =
340 LastRenderedNode::new(props.fallback.call(suspense_context));
341 let nodes_created = suspense_placeholder.create(dom, parent, to);
342 (suspense_placeholder, nodes_created)
343 });
344
345 let scope_state = &mut dom.scopes[scope_id.0];
346 scope_state.last_rendered_node = Some(node);
347
348 nodes_created
349 } else {
350 debug_assert!(children.mount.get().mounted());
352 let nodes_created = suspense_context
353 .under_suspense_boundary(&dom.runtime(), || children.create(dom, parent, to));
354 let scope_state = &mut dom.scopes[scope_id.0];
355 scope_state.last_rendered_node = children.into();
356 let suspense_context =
357 SuspenseContext::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
358 .unwrap();
359 suspense_context.take_suspended_nodes();
360 mark_suspense_resolved(&suspense_context, dom, scope_id);
361
362 nodes_created
363 };
364 nodes_created
365 })
366 }
367
368 #[doc(hidden)]
369 pub fn resolve_suspense<M: WriteMutations>(
373 scope_id: ScopeId,
374 dom: &mut VirtualDom,
375 to: &mut M,
376 only_write_templates: impl FnOnce(&mut M),
377 replace_with: usize,
378 ) {
379 dom.runtime.clone().with_scope_on_stack(scope_id, || {
380 let _runtime = RuntimeGuard::new(dom.runtime());
381 let Some(scope_state) = dom.scopes.get_mut(scope_id.0) else {
382 return;
383 };
384
385 let suspense_context = scope_state
387 .state()
388 .suspense_location()
389 .suspense_context()
390 .unwrap()
391 .clone();
392 suspense_context.inner.suspended_tasks.borrow_mut().clear();
393
394 let currently_rendered = scope_state.last_rendered_node.clone().unwrap();
396 let mount = currently_rendered.mount.get();
397 let parent = {
398 let mounts = dom.runtime.mounts.borrow();
399 mounts
400 .get(mount.0)
401 .expect("suspense placeholder is not mounted")
402 .parent
403 };
404
405 let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
406
407 let children = props.children.clone();
409 let suspense_context =
410 SuspenseContext::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
411 .unwrap();
412
413 let suspended = suspense_context.take_suspended_nodes();
415 if let Some(node) = suspended {
416 node.remove_node(&mut *dom, None::<&mut M>, None);
417 }
418
419 currently_rendered.remove_node(&mut *dom, Some(to), Some(replace_with));
421
422 only_write_templates(to);
424
425 children.mount.take();
426
427 suspense_context.under_suspense_boundary(&dom.runtime(), || {
429 children.create(dom, parent, Some(to));
430 });
431
432 let scope_state = &mut dom.scopes[scope_id.0];
434 let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
435 props.children.clone_from(&children);
436 scope_state.last_rendered_node = Some(children);
437
438 suspense_context.run_resolved_closures(&dom.runtime);
440 })
441 }
442
443 pub(crate) fn diff<M: WriteMutations>(
444 scope_id: ScopeId,
445 dom: &mut VirtualDom,
446 to: Option<&mut M>,
447 ) {
448 dom.runtime.clone().with_scope_on_stack(scope_id, || {
449 let scope = &mut dom.scopes[scope_id.0];
450 let myself = Self::downcast_from_props(&mut *scope.props)
451 .unwrap()
452 .clone();
453
454 let last_rendered_node = scope.last_rendered_node.clone().unwrap();
455
456 let Self {
457 fallback, children, ..
458 } = myself;
459
460 let suspense_context = scope.state().suspense_boundary().unwrap().clone();
461 let suspended_nodes = suspense_context.suspended_nodes();
462 let suspended = !suspense_context.suspended_futures().is_empty();
463 match (suspended_nodes, suspended) {
464 (Some(suspended_nodes), true) => {
467 let new_suspended_nodes: VNode = children.as_vnode().clone();
468
469 let new_placeholder =
471 suspense_context.in_suspense_placeholder(&dom.runtime(), || {
472 let old_placeholder = last_rendered_node;
473 let new_placeholder =
474 LastRenderedNode::new(fallback.call(suspense_context.clone()));
475
476 old_placeholder.diff_node(&new_placeholder, dom, to);
477 new_placeholder
478 });
479
480 dom.scopes[scope_id.0].last_rendered_node = Some(new_placeholder);
482
483 suspense_context.under_suspense_boundary(&dom.runtime(), || {
485 suspended_nodes.diff_node(&new_suspended_nodes, dom, None::<&mut M>);
486 });
487
488 let suspense_context = SuspenseContext::downcast_suspense_boundary_from_scope(
489 &dom.runtime,
490 scope_id,
491 )
492 .unwrap();
493 suspense_context.set_suspended_nodes(new_suspended_nodes);
494 }
495 (None, false) => {
497 let old_children = last_rendered_node;
498 let new_children = children;
499
500 suspense_context.under_suspense_boundary(&dom.runtime(), || {
501 old_children.diff_node(&new_children, dom, to);
502 });
503
504 dom.scopes[scope_id.0].last_rendered_node = new_children.into();
506 }
507 (None, true) => {
509 let old_children = last_rendered_node;
510 let new_children: VNode = children.as_vnode().clone();
511
512 let new_placeholder =
513 LastRenderedNode::new(fallback.call(suspense_context.clone()));
514
515 let mount = old_children.mount.get();
517 let parent = dom.get_mounted_parent(mount);
518
519 suspense_context.in_suspense_placeholder(&dom.runtime(), || {
520 old_children.move_node_to_background(
521 std::slice::from_ref(&new_placeholder),
522 parent,
523 dom,
524 to,
525 );
526 });
527
528 suspense_context.under_suspense_boundary(&dom.runtime(), || {
530 old_children.diff_node(&new_children, dom, None::<&mut M>);
531 });
532
533 dom.scopes[scope_id.0].last_rendered_node = Some(new_placeholder);
535
536 let suspense_context = SuspenseContext::downcast_suspense_boundary_from_scope(
537 &dom.runtime,
538 scope_id,
539 )
540 .unwrap();
541 suspense_context.set_suspended_nodes(new_children);
542
543 un_resolve_suspense(dom, scope_id);
544 }
545 (Some(_), false) => {
547 let old_suspended_nodes = suspense_context.take_suspended_nodes().unwrap();
549 let old_placeholder = last_rendered_node;
550 let new_children = children;
551
552 suspense_context.under_suspense_boundary(&dom.runtime(), || {
554 old_suspended_nodes.diff_node(&new_children, dom, None::<&mut M>);
555
556 let mount = old_placeholder.mount.get();
558 let parent = dom.get_mounted_parent(mount);
559 old_placeholder.replace(
560 std::slice::from_ref(&new_children),
561 parent,
562 dom,
563 to,
564 );
565 });
566
567 dom.scopes[scope_id.0].last_rendered_node = Some(new_children);
569
570 mark_suspense_resolved(&suspense_context, dom, scope_id);
571 }
572 }
573 })
574 }
575}
576
577fn mark_suspense_resolved(
579 suspense_context: &SuspenseContext,
580 dom: &mut VirtualDom,
581 scope_id: ScopeId,
582) {
583 dom.resolved_scopes.push(scope_id);
584 suspense_context.run_resolved_closures(&dom.runtime);
586}
587
588fn un_resolve_suspense(dom: &mut VirtualDom, scope_id: ScopeId) {
590 dom.resolved_scopes.retain(|&id| id != scope_id);
591}
592
593impl SuspenseContext {
594 pub(crate) fn under_suspense_boundary<O>(&self, runtime: &Runtime, f: impl FnOnce() -> O) -> O {
596 runtime.with_suspense_location(SuspenseLocation::UnderSuspense(self.clone()), f)
597 }
598
599 pub(crate) fn in_suspense_placeholder<O>(&self, runtime: &Runtime, f: impl FnOnce() -> O) -> O {
601 runtime.with_suspense_location(SuspenseLocation::InSuspensePlaceholder(self.clone()), f)
602 }
603
604 pub fn downcast_suspense_boundary_from_scope(
606 runtime: &Runtime,
607 scope_id: ScopeId,
608 ) -> Option<Self> {
609 runtime
610 .try_get_state(scope_id)
611 .and_then(|scope| scope.suspense_boundary())
612 }
613
614 pub(crate) fn remove_suspended_nodes<M: WriteMutations>(
615 dom: &mut VirtualDom,
616 scope_id: ScopeId,
617 destroy_component_state: bool,
618 ) {
619 let Some(scope) = Self::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
620 else {
621 return;
622 };
623 if let Some(node) = scope.take_suspended_nodes() {
625 node.remove_node_inner(dom, None::<&mut M>, destroy_component_state, None)
626 }
627 }
628}