1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
use crate::{Mailbox, Node, Text}; use std::cell::RefCell; use std::rc::Rc; use std::sync::Once; use web_sys as web; pub trait App: Sized + 'static { type Message; fn update(&mut self, _mailbox: &Mailbox<Self::Message>, _message: Self::Message) {} fn render(&self) -> Node<Self::Message>; } pub struct Instance<A: App> { inner: Rc<Inner<A>>, } struct Inner<A: App> { app: RefCell<A>, node: RefCell<web::Node>, vnode: RefCell<Node<A::Message>>, queue: RefCell<Vec<A::Message>>, is_updating: RefCell<bool>, } impl<A: App> Instance<A> { fn send(&self, message: A::Message) { if *self.inner.is_updating.borrow() { self.inner.queue.borrow_mut().push(message); return; } self.inner.is_updating.replace(true); let mailbox = self.mailbox(); self.inner.app.borrow_mut().update(&mailbox, message); while !self.inner.queue.borrow().is_empty() { let message = self.inner.queue.borrow_mut().remove(0); self.inner.app.borrow_mut().update(&mailbox, message); } self.inner.is_updating.replace(false); self.render(); } fn render(&self) { let mut new_vnode = self.inner.app.borrow().render(); let new_node = new_vnode.patch(&mut self.inner.vnode.borrow_mut(), self.mailbox()); self.inner.vnode.replace(new_vnode); self.inner.node.replace(new_node); } fn mailbox(&self) -> Mailbox<A::Message> { let cloned = self.clone(); Mailbox::new(move |message| { cloned.send(message); }) } } impl<A: App> std::clone::Clone for Instance<A> { fn clone(&self) -> Self { Instance { inner: Rc::clone(&self.inner), } } } impl<A: App> std::fmt::Debug for Instance<A> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_struct("Instance").finish() } } pub fn start<A: App>(app: A, node: web::Node) -> Mailbox<A::Message> { set_panic_hook(); let mut vnode = Text::new("!"); let new_node = vnode.create().into(); node.parent_node() .unwrap() .replace_child(&new_node, &node) .unwrap(); let instance = Instance { inner: Rc::new(Inner { app: RefCell::new(app), node: RefCell::new(new_node), vnode: RefCell::new(vnode.into()), is_updating: RefCell::new(false), queue: RefCell::new(Vec::new()), }), }; instance.render(); instance.mailbox() } fn set_panic_hook() { static PANIC_HOOK: Once = Once::new(); PANIC_HOOK.call_once(|| { std::panic::set_hook(Box::new(|panic| { crate::console::error(&panic.to_string()); })); }); }