use wasm_bindgen::UnwrapThrowExt;
pub trait Application: Sized + 'static {
type Message;
type Context: Context<Self>;
#[cfg(feature = "sub-apps")]
type Parent;
fn init() -> Self;
fn update(&mut self, m: Self::Message, c: &mut Self::Context);
fn render_initial_view(&self, main: &WeakMain<Self>) -> crate::dom::Node;
fn mounted(&mut self, _c: &mut Self::Context) {
}
}
pub trait Context<A: Application> {
#[cfg(not(feature = "sub-apps"))]
fn init(main: WeakMain<A>) -> Self;
#[cfg(feature = "sub-apps")]
fn init(main: WeakMain<A>, parent_main: A::Parent) -> Self;
}
impl<A: Application> Context<A> for () {
#[cfg(not(feature = "sub-apps"))]
fn init(_: WeakMain<A>) -> Self { }
#[cfg(feature = "sub-apps")]
fn init(_: WeakMain<A>, _: A::Parent) -> Self { }
}
pub struct Main<A: Application> {
app: A,
context: Option<A::Context>,
web_sys_root: web_sys::Element,
root_node: Option<crate::dom::Node>,
}
impl<A: Application> Main<A> {
fn update(&mut self, m: A::Message) {
let context = self
.context
.as_mut()
.expect_throw("self.context.as_mut()");
self.app.update(m, context);
}
}
pub struct RcMain<A: Application> {
rc: std::rc::Rc<std::cell::RefCell<Main<A>>>,
}
impl<A: Application> RcMain<A> {
fn new(web_sys_root: web_sys::Element) -> Self {
Self {
rc: std::rc::Rc::new(std::cell::RefCell::new(Main {
app: A::init(),
context: None,
web_sys_root,
root_node: None,
})),
}
}
fn weak(&self) -> WeakMain<A> {
WeakMain {
weak: std::rc::Rc::downgrade(&self.rc),
}
}
#[cfg(not(feature = "sub-apps"))]
pub fn start_in(web_sys_root: web_sys::Element) -> Self {
web_sys_root.set_text_content(None);
let main = RcMain::new(web_sys_root);
let weak_main = main.weak();
let mut context = A::Context::init(weak_main.clone());
match main.rc.try_borrow_mut() {
Err(e) => log::error!("Error borrowing: {}", e),
Ok(mut main) => {
let root_node = main.app.render_initial_view(&weak_main);
main.root_node = Some(root_node);
main.root_node
.as_ref()
.unwrap()
.append_to(&main.web_sys_root);
main.app.mounted(&mut context);
main.context = Some(context);
}
};
main
}
pub fn start_in_body(_: ()) -> Self {
let web_sys_root = crate::window()
.document()
.expect_throw("crate::window().document()")
.body()
.expect_throw("body()")
.into();
Self::start_in(web_sys_root)
}
}
pub struct WeakMain<A: Application> {
weak: std::rc::Weak<std::cell::RefCell<Main<A>>>,
}
impl<A: Application> WeakMain<A> {
pub fn send_message(&self, m: A::Message) {
match self.weak.upgrade() {
None => log::error!("Upgrade WeakMain to Rc"),
Some(main) => match main.try_borrow_mut() {
Err(e) => log::error!("Error borrowing: {}", e),
Ok(mut main) => main.update(m),
},
}
}
}
impl<A: Application> std::clone::Clone for WeakMain<A> {
fn clone(&self) -> Self {
Self {
weak: std::rc::Weak::clone(&self.weak),
}
}
}