hirola_core/templating/
suspense.rs1use crate::{
2 generic_node::GenericNode,
3 render::{Error, Render},
4 BoxedLocal,
5};
6use futures_util::future::FutureExt;
7use std::{cell::RefCell, future::Future, pin::Pin, rc::Rc};
8
9#[derive(Debug, Default)]
10pub enum SuspenseResult<Res> {
11 #[default]
12 Loading,
13 Ready(Res),
14}
15
16pub trait Suspend {
17 type Result;
18 fn suspend(self) -> BoxedLocal<SuspenseResult<Self::Result>>;
19}
20
21impl<F, Res> Suspend for F
22where
23 F: FutureExt<Output = Res> + 'static,
24{
25 type Result = Res;
26 fn suspend(self) -> BoxedLocal<SuspenseResult<Self::Result>> {
27 Box::pin(self.map(|res| SuspenseResult::Ready(res)))
28 }
29}
30
31pub struct Suspense<Res, G> {
32 pub template: Box<dyn Fn(Res) -> G>,
33 pub future: Pin<Box<dyn Future<Output = Res>>>,
34}
35
36impl<Res: Default + 'static, N: GenericNode> Render<N> for Suspense<Res, N> {
37 fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
38 let template = self.template;
39 struct State<N> {
40 holder: N,
41 current: Option<N>,
42 }
43
44 impl<N: GenericNode> State<N> {
45 fn new(parent: N) -> Rc<RefCell<Self>> {
46 Rc::new(RefCell::new(State {
47 holder: parent,
48 current: None,
49 }))
50 }
51
52 fn clear(&mut self) {
53 {
54 let node = &mut self.holder;
55 if let Some(frag) = &self.current {
56 for child in &frag.children().take() {
57 node.remove_child(child);
58 }
59 };
60 }
61 self.current = None;
62 }
63
64 fn apply(&mut self, dom: N) -> Result<(), Error> {
65 self.clear();
66 let node = &mut self.holder;
67 let frag = N::fragment();
68 frag.append_child(&dom);
69 node.append_child(&frag);
70 self.current = Some(frag);
71 Ok(())
72 }
73 }
74
75 let state = State::new(parent.clone());
76 let binding = Rc::<_>::clone(&state);
77 let mut binding = binding.borrow_mut();
78 binding.apply(template(Res::default()))?;
80 let future = self.future;
81 let fut = async move {
82 let new_dom = template(future.await);
83 let mut state = state.borrow_mut();
84 state.apply(new_dom).unwrap();
85 };
86 parent.effect(fut);
87 Ok(())
88 }
89}