hirola_core/templating/
suspense.rs

1use 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        // Apply loading
79        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}