duid_core/core/apps/
duid.rs

1use std::rc::Rc;
2use std::cell::RefCell;
3use crate::{
4    core::{
5        duid_events::{Dispatch, Cmd},
6        util::{window, request_animation_frame_for_closure},
7        v_node::{ViewBuilder, VirtualNode},
8        apps::UserApp
9    },
10    dom::Dom,
11    effects::Effects
12};
13use wasm_bindgen::{closure::Closure};
14use wasm_bindgen::JsCast;
15use std::collections::HashMap;
16use indextree::Arena;
17
18
19
20pub struct Duid<MDL, MSG> 
21where
22    MSG: std::fmt::Debug + Clone + 'static,
23    MDL: Clone + 'static,
24{
25    user_app: Rc<RefCell<UserApp<MDL, MSG>>>,
26    dom: Rc<RefCell<Dom<MSG>>>,
27    effects: Rc<RefCell<Effects>>
28}
29
30
31impl<MDL, MSG> Clone for Duid<MDL, MSG> 
32where
33    MSG: std::fmt::Debug + Clone + 'static,
34    MDL: Clone + 'static,
35{
36    fn clone(&self) -> Self {
37        Duid {
38            user_app: Rc::clone(&self.user_app),
39            dom: Rc::clone(&self.dom),
40            effects: Rc::clone(&self.effects),
41        }
42    }
43}
44
45
46impl<MDL, MSG> Duid<MDL, MSG> 
47where
48    MSG: std::fmt::Debug + Clone + 'static,
49    MDL: Clone + 'static,
50{
51    pub fn new(
52        mount_node: &str,
53        virtual_dom: UserApp<MDL, MSG>,
54        base_styles: HashMap<String, String>,
55        styles: HashMap<String, String>,
56        replace: bool,
57        use_shadow: bool
58    ) -> Self {
59
60        Duid {
61            user_app: Rc::new(RefCell::new(virtual_dom)),
62            dom: Rc::new(RefCell::new(Dom::new::<Self>(mount_node, replace, use_shadow, base_styles, styles))),
63            effects: Rc::new(RefCell::new(Effects::new()))
64        }
65    }
66
67    pub fn start(
68        mount_node: &str,
69        virtual_dom: UserApp<MDL, MSG>,
70        base_styles: HashMap<String, String>,
71        styles: HashMap<String, String>
72    ) -> Self
73    {
74        console_log::init_with_level(tracing::log::Level::Debug).unwrap();
75        std::panic::set_hook(Box::new(|info| {
76            tracing::error!("{:?}", info);
77        }));
78        
79        let program = Self::new(mount_node, virtual_dom, base_styles, styles, true, false);
80        program.mount();
81        program
82    }
83    
84    pub fn mount(&self) {
85        let view = self.user_app.borrow().render();
86        let mut arena = Arena::<VirtualNode<MSG>>::new();
87        let root_node_id = ViewBuilder::build(view, &mut arena);
88        self.dom.borrow_mut().mount(self, arena, root_node_id);
89    }
90
91    pub fn render(&self) {
92        let view = self.user_app.borrow().render();
93        let mut arena = Arena::<VirtualNode<MSG>>::new();
94        let root_node_id = ViewBuilder::build(view, &mut arena);
95        self.dom.borrow_mut().render(self, arena, root_node_id);
96    }
97
98    pub fn dispatch_inner(&mut self, msgs: Vec<MSG>) {
99        let mut cmd_msgs: Vec<_> = msgs.iter().map(|msg| self.user_app.borrow_mut().update(msg.to_owned())).collect();
100        let sub_msgs = self.user_app.borrow().subscription();
101        cmd_msgs.push(sub_msgs.into());
102        let cmd_merged = Cmd::merge_all(cmd_msgs);
103
104        if !cmd_merged.messages.is_empty() {
105            self.effects.borrow().effects(self, cmd_merged);
106        }
107        else {
108            self.render();
109        }
110
111    }
112}
113
114
115impl<MDL, MSG> Dispatch<MSG> for Duid<MDL, MSG>
116where
117    MSG: std::fmt::Debug + Clone + 'static,
118    MDL: Clone + 'static,
119{
120    #[cfg(feature = "with-request-animation-frame")]
121    fn dispatch_multiple(&self, msgs: Vec<MSG>) {
122        let mut program_clone = self.clone();
123        let closure_raf: Closure<dyn FnMut() + 'static> =
124            Closure::once(move || {
125                program_clone.dispatch_inner(msgs);
126            });
127        request_animation_frame_for_closure(&closure_raf);
128        closure_raf.forget();
129    }
130
131    #[cfg(not(feature = "with-request-animation-frame"))]
132    fn dispatch_multiple(&self, msgs: Vec<MSG>) {
133        self.dispatch_inner(msgs)
134    }
135
136    fn dispatch(&self, msg: MSG) {
137        self.dispatch_multiple(vec![msg])
138    }
139
140    fn dispatch_with_delay(&self, msg: MSG, timeout: i32) -> Option<i32> {
141        let program_clone = self.clone();
142        let window = window();
143        let closure_delay: Closure<dyn FnMut() + 'static> =
144            Closure::once(move || {
145                program_clone.dispatch(msg);
146            });
147
148        let timeout_id = window
149            .set_timeout_with_callback_and_timeout_and_arguments_0(
150                closure_delay.as_ref().unchecked_ref(),
151                timeout,
152            )
153            .expect("should register the setTimeout call");
154
155        closure_delay.forget();
156        Some(timeout_id)
157    }
158}