From 9df12375e1808c97162be124d0b57d9cc02edb22 Mon Sep 17 00:00:00 2001 From: AlterionX Date: Wed, 20 Nov 2019 00:11:05 -0600 Subject: [PATCH 1/7] Shuffling to better encapsulate building behaviors. --- src/vdom.rs | 4 +- src/vdom/builder.rs | 112 +++++-------------------------- src/vdom/builder/after_mount.rs | 20 ++++++ src/vdom/builder/before_mount.rs | 51 ++++++++++++++ src/vdom/builder/init.rs | 73 ++++++++++++++++++++ 5 files changed, 163 insertions(+), 97 deletions(-) create mode 100644 src/vdom/builder/after_mount.rs create mode 100644 src/vdom/builder/before_mount.rs create mode 100644 src/vdom/builder/init.rs diff --git a/src/vdom.rs b/src/vdom.rs index 154240fb2..5240eeaab 100644 --- a/src/vdom.rs +++ b/src/vdom.rs @@ -14,8 +14,10 @@ use next_tick::NextTick; pub mod alias; pub use alias::*; + +// Building process. pub mod builder; -pub use builder::{Builder as AppBuilder, Init, MountType, UrlHandling}; +pub use builder::{Builder as AppBuilder, MountPoint, MountType, UrlHandling, Init}; use crate::{ dom_types::{self, El, MessageMapper, Namespace, Node, View}, diff --git a/src/vdom/builder.rs b/src/vdom/builder.rs index 96d535f16..0a5639b5e 100644 --- a/src/vdom/builder.rs +++ b/src/vdom/builder.rs @@ -1,101 +1,21 @@ use web_sys::Element; -use super::{alias::*, App}; - -use crate::{dom_types::View, orders::OrdersContainer, routing, util}; - -pub type InitFn = - Box) -> Init>; - -pub trait MountPoint { - fn element(self) -> Element; -} - -impl MountPoint for &str { - fn element(self) -> Element { - util::document().get_element_by_id(self).unwrap_or_else(|| { - panic!( - "Can't find element with id={:?} - app cannot be mounted!\n\ - (Id defaults to \"app\", or can be set with the .mount() method)", - self - ) - }) - } -} - -impl MountPoint for Element { - fn element(self) -> Element { - self - } -} - -impl MountPoint for web_sys::HtmlElement { - fn element(self) -> Element { - self.into() - } -} - -/// Used for handling initial routing. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum UrlHandling { - PassToRoutes, - None, - // todo: Expand later, as-required -} - -/// Describes the handling of elements already present in the mount element. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum MountType { - /// Take control of previously existing elements in the mount. This does not make guarantees of - /// elements added after the [`App`] has been mounted. - /// - /// Note that existing elements in the DOM will be recreated. This can be dangerous for script - /// tags and other, similar tags. - Takeover, - /// Leave the previously existing elements in the mount alone. This does not make guarantees of - /// elements added after the [`App`] has been mounted. - Append, -} - -/// Used as a flexible wrapper for the init function. -pub struct Init { - /// Initial model to be used when the app begins. - pub model: Mdl, - /// How to handle initial url routing. Defaults to [`UrlHandling::PassToRoutes`] in the - /// constructors. - pub url_handling: UrlHandling, - /// How to handle elements already present in the mount. Defaults to [`MountType::Append`] - /// in the constructors. - pub mount_type: MountType, -} - -impl Init { - pub const fn new(model: Mdl) -> Self { - Self { - model, - url_handling: UrlHandling::PassToRoutes, - mount_type: MountType::Append, - } - } - - pub const fn new_with_url_handling(model: Mdl, url_handling: UrlHandling) -> Self { - Self { - model, - url_handling, - mount_type: MountType::Append, - } - } -} - -impl Default for Init { - fn default() -> Self { - Self { - model: Mdl::default(), - url_handling: UrlHandling::PassToRoutes, - mount_type: MountType::Append, - } - } -} +use crate::{ + dom_types::View, + orders::OrdersContainer, + routing, util, + vdom::{ + alias::*, + App, + }, +}; + +pub mod init; +pub use init::{Init, InitFn}; +pub mod before_mount; +pub use before_mount::{MountPoint, MountType}; +pub mod after_mount; +pub use after_mount::UrlHandling; /// Used to create and store initial app configuration, ie items passed by the app creator pub struct Builder, GMs> { diff --git a/src/vdom/builder/after_mount.rs b/src/vdom/builder/after_mount.rs new file mode 100644 index 000000000..a48fba88a --- /dev/null +++ b/src/vdom/builder/after_mount.rs @@ -0,0 +1,20 @@ +use web_sys::Element; + +use crate::{ + dom_types::View, + orders::OrdersContainer, + routing, util, + vdom::{ + alias::*, + App, + }, +}; + +/// Used for handling initial routing. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum UrlHandling { + PassToRoutes, + None, + // todo: Expand later, as-required +} + diff --git a/src/vdom/builder/before_mount.rs b/src/vdom/builder/before_mount.rs new file mode 100644 index 000000000..4bcc8cee5 --- /dev/null +++ b/src/vdom/builder/before_mount.rs @@ -0,0 +1,51 @@ +use web_sys::Element; + +use crate::{ + util, + vdom::{ + alias::*, + App, + }, +}; + +pub trait MountPoint { + fn element(self) -> Element; +} + +impl MountPoint for &str { + fn element(self) -> Element { + util::document().get_element_by_id(self).unwrap_or_else(|| { + panic!( + "Can't find element with id={:?} - app cannot be mounted!\n\ + (Id defaults to \"app\", or can be set with the .mount() method)", + self + ) + }) + } +} + +impl MountPoint for Element { + fn element(self) -> Element { + self + } +} + +impl MountPoint for web_sys::HtmlElement { + fn element(self) -> Element { + self.into() + } +} + +/// Describes the handling of elements already present in the mount element. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum MountType { + /// Take control of previously existing elements in the mount. This does not make guarantees of + /// elements added after the [`App`] has been mounted. + /// + /// Note that existing elements in the DOM will be recreated. This can be dangerous for script + /// tags and other, similar tags. + Takeover, + /// Leave the previously existing elements in the mount alone. This does not make guarantees of + /// elements added after the [`App`] has been mounted. + Append, +} diff --git a/src/vdom/builder/init.rs b/src/vdom/builder/init.rs new file mode 100644 index 000000000..7084cc454 --- /dev/null +++ b/src/vdom/builder/init.rs @@ -0,0 +1,73 @@ +use web_sys::Element; + +use crate::{ + dom_types::View, + orders::OrdersContainer, + routing::Url, util, + vdom::{ + alias::*, + App, + builder::{ + before_mount::{MountPoint, MountType}, + after_mount::{UrlHandling}, + }, + }, +}; + + +/// Used as a flexible wrapper for the init function. +pub struct Init { + /// Initial model to be used when the app begins. + pub model: Mdl, + /// How to handle initial url routing. Defaults to [`UrlHandling::PassToRoutes`] in the + /// constructors. + pub url_handling: UrlHandling, + /// How to handle elements already present in the mount. Defaults to [`MountType::Append`] + /// in the constructors. + pub mount_type: MountType, +} + +impl Init { + pub const fn new(model: Mdl) -> Self { + Self { + model, + url_handling: UrlHandling::PassToRoutes, + mount_type: MountType::Append, + } + } + + pub const fn new_with_url_handling(model: Mdl, url_handling: UrlHandling) -> Self { + Self { + model, + url_handling, + mount_type: MountType::Append, + } + } +} + +impl Default for Init { + fn default() -> Self { + Self { + model: Mdl::default(), + url_handling: UrlHandling::PassToRoutes, + mount_type: MountType::Append, + } + } +} + +pub type InitFn = + Box) -> Init>; + +pub trait IntoInit, GMs> { + fn into_init(self, init_url: Url, ord: &mut OrdersContainer) -> Init; +} + +impl, GMs, F> IntoInit + for F + where + F: FnOnce(Url, &mut OrdersContainer) -> Init, +{ + fn into_init(self, init_url: Url, ord: &mut OrdersContainer) -> Init { + self(init_url, ord) + } +} From 0017b57d0d5cb9992d7eb5bff1b773b7b935d4ac Mon Sep 17 00:00:00 2001 From: AlterionX Date: Wed, 20 Nov 2019 02:57:35 -0600 Subject: [PATCH 2/7] Implement new API - Implement API (slight use of generics). - Convert examples to `BeforeMount` and `AfterMount` from `Init`. - Isolate one of the bug fixes in a method that's always called: `build`. --- examples/counter/src/lib.rs | 2 +- src/vdom.rs | 100 +++++-- src/vdom/builder.rs | 484 ++++++++++++++++++++++++++----- src/vdom/builder/after_mount.rs | 78 ++++- src/vdom/builder/before_mount.rs | 69 ++++- src/vdom/builder/init.rs | 55 ++-- 6 files changed, 654 insertions(+), 134 deletions(-) diff --git a/examples/counter/src/lib.rs b/examples/counter/src/lib.rs index 222a3a8c3..7579e0fd9 100644 --- a/examples/counter/src/lib.rs +++ b/examples/counter/src/lib.rs @@ -105,5 +105,5 @@ fn view(model: &Model) -> impl View { #[wasm_bindgen(start)] pub fn render() { - seed::App::build(|_, _| Init::new(Model::default()), update, view).build_and_start(); + seed::App::builder(update, view).build_and_start(); } diff --git a/src/vdom.rs b/src/vdom.rs index 5240eeaab..f988f8faa 100644 --- a/src/vdom.rs +++ b/src/vdom.rs @@ -1,6 +1,7 @@ use std::{ cell::{Cell, RefCell}, collections::{vec_deque::VecDeque, HashMap}, + marker::PhantomData, rc::Rc, }; @@ -17,7 +18,7 @@ pub use alias::*; // Building process. pub mod builder; -pub use builder::{Builder as AppBuilder, MountPoint, MountType, UrlHandling, Init}; +pub use builder::{Builder as AppBuilder, Init, InitFn, MountPoint, MountType, UrlHandling}; use crate::{ dom_types::{self, El, MessageMapper, Namespace, Node, View}, @@ -121,6 +122,21 @@ pub struct AppData { pub render_timestamp: Cell>, } +type OptDynRunCfg = + Option>>; + +pub struct AppRunCfg +where + Ms: 'static, + Mdl: 'static, + ElC: View, + IAM: builder::IntoAfterMount, +{ + mount_type: MountType, + into_after_mount: Box, + phantom: PhantomData<(Ms, Mdl, ElC, GMs)>, +} + pub struct AppCfg where Ms: 'static, @@ -129,12 +145,10 @@ where { document: web_sys::Document, mount_point: web_sys::Element, - mount_type: RefCell>, pub update: UpdateFn, pub sink: Option>, view: ViewFn, window_events: Option>, - initial_orders: RefCell>>, } pub struct App @@ -143,6 +157,8 @@ where Mdl: 'static, ElC: View, { + /// State that is removed after app begins running. + pub run_cfg: OptDynRunCfg, /// Stateless app configuration pub cfg: Rc>, /// Mutable app state @@ -155,6 +171,9 @@ impl, GMs> ::std::fmt::Debug for App = + AppBuilder>>; + /// We use a struct instead of series of functions, in order to avoid passing /// repetitive sequences of parameters. impl + 'static, GMs: 'static> App { @@ -162,11 +181,23 @@ impl + 'static, GMs: 'static> App { init: impl FnOnce(routing::Url, &mut OrdersContainer) -> Init + 'static, update: UpdateFn, view: ViewFn, - ) -> AppBuilder { + ) -> InitAppBuilder { + Self::builder(update, view).init(Box::new(init)) + } + + pub fn builder( + update: UpdateFn, + view: ViewFn, + ) -> AppBuilder { + // @TODO: Remove as soon as Webkit is fixed and older browsers are no longer in use. + // https://github.com/David-OConnor/seed/issues/241 + // https://bugs.webkit.org/show_bug.cgi?id=202881 + let _ = util::document().query_selector("html"); + // Allows panic messages to output to the browser console.error. console_error_panic_hook::set_once(); - AppBuilder::new(Box::new(init), update, view) + AppBuilder::new(update, view) } #[allow(clippy::too_many_arguments)] @@ -177,11 +208,13 @@ impl + 'static, GMs: 'static> App { mount_point: Element, routes: Option>, window_events: Option>, + run_cfg: OptDynRunCfg, ) -> Self { let window = util::window(); let document = window.document().expect("Can't find the window's document"); Self { + run_cfg, cfg: Rc::new(AppCfg { document, mount_point, @@ -189,8 +222,6 @@ impl + 'static, GMs: 'static> App { sink, view, window_events, - initial_orders: RefCell::new(None), - mount_type: RefCell::new(None), }), data: Rc::new(AppData { model: RefCell::new(None), @@ -224,8 +255,7 @@ impl + 'static, GMs: 'static> App { /// Bootstrap the dom with the vdom by taking over all children of the mount point and /// replacing them with the vdom if requested. Will otherwise ignore the original children of /// the mount point. - fn bootstrap_vdom(&self) -> El { - let mount_type = self.cfg.mount_type.borrow().unwrap_or(MountType::Append); + fn bootstrap_vdom(&self, mount_type: MountType) -> El { // "new" name is for consistency with `update` function. // this section parent is a placeholder, so we can iterate over children // in a way consistent with patching code. @@ -290,9 +320,48 @@ impl + 'static, GMs: 'static> App { since = "0.4.2", note = "Please use `AppBuilder.build_and_start` instead" )] - pub fn run(self) -> Self { + pub fn run(mut self) -> Self { + let AppRunCfg { + mount_type, + into_after_mount, + .. + } = self + .run_cfg + .take() + .expect("run_cfg should be set in App::new which is called from AppBuilder"); + // Bootstrap the virtual DOM. - self.data.main_el_vdom.replace(Some(self.bootstrap_vdom())); + self.data + .main_el_vdom + .replace(Some(self.bootstrap_vdom(mount_type))); + + // Can this simply be `self.update`? + let mut orders = OrdersContainer::new(self.clone()); + let builder::AfterMount { + model, + url_handling, + } = into_after_mount.into_after_mount(routing::current_url(), &mut orders); + + self.data.model.replace(Some(model)); + + // TODO: Does this go before or after setting up the routes listener? + // TODO: Does this go before or after the initial render? The effects are created before + // TODO: the render, but after the orders. But old behavior let this run before the orders. + match url_handling { + UrlHandling::PassToRoutes => { + let url = routing::current_url(); + if let Some(routes) = self.data.routes.borrow().as_ref() { + if let Some(routing_msg) = routes(url) { + (self.cfg.update)( + routing_msg, + &mut self.data.model.borrow_mut().as_mut().unwrap(), + &mut orders, + ); + } + } + } + UrlHandling::None => (), + }; // Update the state on page load, based // on the starting URL. Must be set up on the server as well. @@ -315,13 +384,7 @@ impl + 'static, GMs: 'static> App { } // Our initial render. Can't initialize in new due to mailbox() requiring self. - self.process_cmd_and_msg_queue( - self.cfg - .initial_orders - .replace(None) - .expect("initial_orders should be set in AppBuilder::finish") - .effects, - ); + self.process_cmd_and_msg_queue(orders.effects); // TODO: In the future, only run the following line if the above statement does not // TODO: call `rerender_vdom` for efficiency. self.rerender_vdom(); @@ -552,6 +615,7 @@ impl + 'static, GMs: 'static> App { impl, GMs> Clone for App { fn clone(&self) -> Self { Self { + run_cfg: None, cfg: Rc::clone(&self.cfg), data: Rc::clone(&self.data), } diff --git a/src/vdom/builder.rs b/src/vdom/builder.rs index 0a5639b5e..55c00fd28 100644 --- a/src/vdom/builder.rs +++ b/src/vdom/builder.rs @@ -1,48 +1,368 @@ -use web_sys::Element; +use std::marker::PhantomData; use crate::{ dom_types::View, orders::OrdersContainer, - routing, util, - vdom::{ - alias::*, - App, - }, + routing, + vdom::{alias::*, App, AppRunCfg}, }; pub mod init; -pub use init::{Init, InitFn}; +pub use init::{Fn as InitFn, Init, Into as IntoInit}; pub mod before_mount; -pub use before_mount::{MountPoint, MountType}; +pub use before_mount::{BeforeMount, Into as IntoBeforeMount, MountPoint, MountType}; pub mod after_mount; -pub use after_mount::UrlHandling; +pub use after_mount::{AfterMount, Into as IntoAfterMount, UrlHandling}; + +pub struct MountPointInitInitAPI { + mount_point: MP, + into_init: II, +} +pub struct BeforeAfterInitAPI { + into_before_mount: IBM, + into_after_mount: IAM, +} +impl Default for BeforeAfterInitAPI<(), ()> { + fn default() -> Self { + BeforeAfterInitAPI { + into_before_mount: (), + into_after_mount: (), + } + } +} + +pub trait InitAPI, GMs> { + type Builder; + fn build(builder: Self::Builder) -> App; +} +pub trait InitAPIData { + type IntoBeforeMount; + type IntoAfterMount; + type IntoInit; + type MountPoint; + + fn before_mount( + self, + into_before_mount: NewIBM, + ) -> BeforeAfterInitAPI; + fn after_mount< + Ms: 'static, + Mdl, + ElC: View, + GMs, + NewIAM: IntoAfterMount, + >( + self, + into_after_mount: NewIAM, + ) -> BeforeAfterInitAPI; + + fn init, GMs, NewII: IntoInit>( + self, + into_init: NewII, + ) -> MountPointInitInitAPI; + fn mount( + self, + mount_point: NewMP, + ) -> MountPointInitInitAPI; +} + +impl< + Ms: 'static, + Mdl: 'static, + ElC: 'static + View, + GMs: 'static, + MP: MountPoint, + II: IntoInit, + > InitAPI for MountPointInitInitAPI +{ + type Builder = Builder; + fn build(builder: Self::Builder) -> App { + let MountPointInitInitAPI { + into_init, + mount_point, + } = builder.init_api; + + let mut app = App::new( + builder.update, + builder.sink, + builder.view, + mount_point.element(), + builder.routes, + builder.window_events, + None, + ); + + let mut initial_orders = OrdersContainer::new(app.clone()); + let init = into_init.into_init(routing::current_url(), &mut initial_orders); + + app.run_cfg.replace(AppRunCfg { + mount_type: init.mount_type, + into_after_mount: Box::new((init, initial_orders)), + phantom: PhantomData, + }); + + app + } +} +impl< + Ms: 'static, + Mdl: 'static, + ElC: 'static + View, + GMs: 'static, + IBM: IntoBeforeMount, + IAM: 'static + IntoAfterMount, + > InitAPI for BeforeAfterInitAPI +{ + type Builder = Builder; + fn build(builder: Self::Builder) -> App { + let BeforeAfterInitAPI { + into_before_mount, + into_after_mount, + } = builder.init_api; + + let BeforeMount { + mount_point, + mount_type, + } = into_before_mount.into_before_mount(routing::current_url()); + + App::new( + builder.update, + builder.sink, + builder.view, + mount_point.element(), + builder.routes, + builder.window_events, + Some(AppRunCfg { + mount_type, + into_after_mount: Box::new(into_after_mount), + phantom: PhantomData, + }), + ) + } +} +impl, GMs: 'static> + InitAPI for () +{ + type Builder = Builder; + fn build(builder: Self::Builder) -> App { + BeforeAfterInitAPI::build(Builder { + update: builder.update, + view: builder.view, + + routes: builder.routes, + window_events: builder.window_events, + sink: builder.sink, + + init_api: BeforeAfterInitAPI::default(), + }) + } +} + +impl InitAPIData for MountPointInitInitAPI { + type IntoBeforeMount = (); + type IntoAfterMount = (); + type IntoInit = II; + type MountPoint = MP; + + fn before_mount( + self, + into_before_mount: NewIBM, + ) -> BeforeAfterInitAPI { + BeforeAfterInitAPI { + into_before_mount, + into_after_mount: (), + } + } + fn after_mount< + Ms: 'static, + Mdl, + ElC: View, + GMs, + NewIAM: IntoAfterMount, + >( + self, + into_after_mount: NewIAM, + ) -> BeforeAfterInitAPI { + BeforeAfterInitAPI { + into_after_mount, + into_before_mount: (), + } + } + + fn init, GMs, NewII: IntoInit>( + self, + into_init: NewII, + ) -> MountPointInitInitAPI { + MountPointInitInitAPI { + into_init, + mount_point: self.mount_point, + } + } + fn mount( + self, + mount_point: NewMP, + ) -> MountPointInitInitAPI { + MountPointInitInitAPI { + mount_point, + into_init: self.into_init, + } + } +} +impl InitAPIData for BeforeAfterInitAPI { + type IntoBeforeMount = IBM; + type IntoAfterMount = IAM; + type IntoInit = (); + type MountPoint = (); + + fn before_mount( + self, + into_before_mount: NewIBM, + ) -> BeforeAfterInitAPI { + BeforeAfterInitAPI { + into_before_mount, + into_after_mount: self.into_after_mount, + } + } + fn after_mount< + Ms: 'static, + Mdl, + ElC: View, + GMs, + NewIAM: IntoAfterMount, + >( + self, + into_after_mount: NewIAM, + ) -> BeforeAfterInitAPI { + BeforeAfterInitAPI { + into_after_mount, + into_before_mount: self.into_before_mount, + } + } + + fn init, GMs, NewII: IntoInit>( + self, + into_init: NewII, + ) -> MountPointInitInitAPI { + MountPointInitInitAPI { + into_init, + mount_point: (), + } + } + fn mount( + self, + mount_point: NewMP, + ) -> MountPointInitInitAPI { + MountPointInitInitAPI { + mount_point, + into_init: (), + } + } +} +impl InitAPIData for () { + type IntoBeforeMount = (); + type IntoAfterMount = (); + type IntoInit = (); + type MountPoint = (); + + fn before_mount( + self, + into_before_mount: NewIBM, + ) -> BeforeAfterInitAPI { + BeforeAfterInitAPI { + into_before_mount, + into_after_mount: (), + } + } + fn after_mount< + Ms: 'static, + Mdl, + ElC: View, + GMs, + NewIAM: IntoAfterMount, + >( + self, + into_after_mount: NewIAM, + ) -> BeforeAfterInitAPI { + BeforeAfterInitAPI { + into_after_mount, + into_before_mount: (), + } + } + + fn init, GMs, NewII: IntoInit>( + self, + into_init: NewII, + ) -> MountPointInitInitAPI { + MountPointInitInitAPI { + into_init, + mount_point: (), + } + } + fn mount( + self, + mount_point: NewMP, + ) -> MountPointInitInitAPI { + MountPointInitInitAPI { + mount_point, + into_init: (), + } + } +} /// Used to create and store initial app configuration, ie items passed by the app creator -pub struct Builder, GMs> { - init: InitFn, +pub struct Builder, GMs, InitAPIType> { update: UpdateFn, - sink: Option>, view: ViewFn, - mount_point: Option, + routes: Option>, window_events: Option>, + sink: Option>, + + // TODO: Remove when removing legacy init fields. + init_api: InitAPIType, } -impl + 'static, GMs: 'static> Builder { +impl + 'static, GMs: 'static> Builder { /// Constructs the Builder. - pub(super) fn new( - init: InitFn, - update: UpdateFn, - view: ViewFn, - ) -> Self { - Self { - init, + pub(super) fn new(update: UpdateFn, view: ViewFn) -> Self { + Builder { update, - sink: None, view, - mount_point: None, + routes: None, window_events: None, + sink: None, + + init_api: (), + } + } +} + +impl< + Ms, + Mdl, + ElC: View + 'static, + GMs: 'static, + IBM, + IAM: 'static, + MP, + II, + InitAPIType: InitAPIData, + > Builder +{ + pub fn init>( + self, + new_init: NewII, + ) -> Builder> { + Builder { + update: self.update, + view: self.view, + + routes: self.routes, + window_events: self.window_events, + sink: self.sink, + + init_api: self.init_api.init(new_init), } } @@ -63,14 +383,52 @@ impl + 'static, GMs: 'static> Builder /// // argument is `Element` /// mount(seed::body().querySelector("section").unwrap().unwrap()) /// ``` - pub fn mount(mut self, mount_point: impl MountPoint) -> Self { - // @TODO: Remove as soon as Webkit is fixed and older browsers are no longer in use. - // https://github.com/seed-rs/seed/issues/241 - // https://bugs.webkit.org/show_bug.cgi?id=202881 - let _ = util::document().query_selector("html"); + pub fn mount( + self, + new_mount_point: NewMP, + ) -> Builder> { + Builder { + update: self.update, + view: self.view, - self.mount_point = Some(mount_point.element()); - self + routes: self.routes, + window_events: self.window_events, + sink: self.sink, + + init_api: self.init_api.mount(new_mount_point), + } + } + + pub fn before_mount( + self, + new_before_mount: NewIBM, + ) -> Builder> { + Builder { + update: self.update, + view: self.view, + + routes: self.routes, + window_events: self.window_events, + sink: self.sink, + + init_api: self.init_api.before_mount(new_before_mount), + } + } + + pub fn after_mount>( + self, + new_after_mount: NewIAM, + ) -> Builder> { + Builder { + update: self.update, + view: self.view, + + routes: self.routes, + window_events: self.window_events, + sink: self.sink, + + init_api: self.init_api.after_mount(new_after_mount), + } } /// Registers a function which maps URLs to messages. @@ -94,51 +452,37 @@ impl + 'static, GMs: 'static> Builder self.sink = Some(sink); self } +} + +impl< + Ms: 'static, + Mdl, + ElC: View + 'static, + GMs: 'static, + InitAPIType: InitAPI, + > Builder +{ + /// Build and run the app. + pub fn build_and_start(self) -> App { + InitAPIType::build(self).run() + } +} +impl< + Ms: 'static, + Mdl, + ElC: View + 'static, + GMs: 'static, + MP: MountPoint, + II: IntoInit, + > Builder> +{ /// Turn this [`Builder`] into an [`App`] which is ready to run. /// /// [`Builder`]: struct.Builder.html /// [`App`]: struct.App.html #[deprecated(since = "0.4.2", note = "Please use `.build_and_start` instead")] - pub fn finish(mut self) -> App { - if self.mount_point.is_none() { - self = self.mount("app") - } - - let app = App::new( - self.update, - self.sink, - self.view, - self.mount_point.unwrap(), - self.routes, - self.window_events, - ); - - let mut initial_orders = OrdersContainer::new(app.clone()); - let mut init = (self.init)(routing::current_url(), &mut initial_orders); - - match init.url_handling { - UrlHandling::PassToRoutes => { - let url = routing::current_url(); - if let Some(r) = self.routes { - if let Some(u) = r(url) { - (self.update)(u, &mut init.model, &mut initial_orders); - } - } - } - UrlHandling::None => (), - }; - - app.cfg.initial_orders.replace(Some(initial_orders)); - app.cfg.mount_type.replace(Some(init.mount_type)); - app.data.model.replace(Some(init.model)); - - app - } - - /// Build and run the app. - pub fn build_and_start(self) -> App { - let app = self.finish(); - app.run() + pub fn finish(self) -> App { + MountPointInitInitAPI::build(self) } } diff --git a/src/vdom/builder/after_mount.rs b/src/vdom/builder/after_mount.rs index a48fba88a..b78f4d203 100644 --- a/src/vdom/builder/after_mount.rs +++ b/src/vdom/builder/after_mount.rs @@ -1,14 +1,4 @@ -use web_sys::Element; - -use crate::{ - dom_types::View, - orders::OrdersContainer, - routing, util, - vdom::{ - alias::*, - App, - }, -}; +use crate::{dom_types::View, orders::OrdersContainer, routing::Url}; /// Used for handling initial routing. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -18,3 +8,69 @@ pub enum UrlHandling { // todo: Expand later, as-required } +impl Default for UrlHandling { + fn default() -> Self { + Self::PassToRoutes + } +} + +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct AfterMount { + pub model: Mdl, + pub url_handling: UrlHandling, +} + +impl AfterMount { + pub fn new(model: Mdl) -> Self { + Self { + model, + url_handling: UrlHandling::default(), + } + } + + // TODO: Change to const fn when possible. + // TODO: Relevant issue: https://github.com/rust-lang/rust/issues/60964 + #[allow(clippy::missing_const_for_fn)] + pub fn model(self, model: NewMdl) -> AfterMount { + AfterMount { + model, + url_handling: self.url_handling, + } + } + + pub const fn url_handling(mut self, url_handling: UrlHandling) -> Self { + self.url_handling = url_handling; + self + } +} + +pub trait Into, GMs> { + fn into_after_mount( + self: Box, + init_url: Url, + orders: &mut OrdersContainer, + ) -> AfterMount; +} + +impl, GMs, F> Into for F +where + F: FnOnce(Url, &mut OrdersContainer) -> AfterMount, +{ + fn into_after_mount( + self: Box, + init_url: Url, + orders: &mut OrdersContainer, + ) -> AfterMount { + self(init_url, orders) + } +} + +impl, GMs> Into for () { + fn into_after_mount( + self: Box, + _: Url, + _: &mut OrdersContainer, + ) -> AfterMount { + AfterMount::default() + } +} diff --git a/src/vdom/builder/before_mount.rs b/src/vdom/builder/before_mount.rs index 4bcc8cee5..950307141 100644 --- a/src/vdom/builder/before_mount.rs +++ b/src/vdom/builder/before_mount.rs @@ -1,12 +1,6 @@ use web_sys::Element; -use crate::{ - util, - vdom::{ - alias::*, - App, - }, -}; +use crate::{routing::Url, util}; pub trait MountPoint { fn element(self) -> Element; @@ -36,6 +30,12 @@ impl MountPoint for web_sys::HtmlElement { } } +impl MountPoint for () { + fn element(self) -> Element { + "app".element() + } +} + /// Describes the handling of elements already present in the mount element. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum MountType { @@ -49,3 +49,58 @@ pub enum MountType { /// elements added after the [`App`] has been mounted. Append, } + +impl Default for MountType { + fn default() -> Self { + Self::Append + } +} + +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct BeforeMount { + pub mount_point: MP, + pub mount_type: MountType, +} + +impl BeforeMount { + pub fn new(mp: MP) -> Self { + Self { + mount_point: mp, + mount_type: MountType::default(), + } + } + + pub fn mount_point(self, new_mp: NewMP) -> BeforeMount { + BeforeMount { + mount_point: new_mp, + mount_type: self.mount_type, + } + } + + pub fn mount_type(mut self, new_mt: MountType) -> Self { + self.mount_type = new_mt; + self + } +} + +pub trait Into { + type MP: MountPoint; + fn into_before_mount(self, init_url: Url) -> BeforeMount; +} + +impl Into for F +where + F: FnOnce(Url) -> BeforeMount, +{ + type MP = MP; + fn into_before_mount(self, init_url: Url) -> BeforeMount { + self(init_url) + } +} + +impl Into for () { + type MP = (); + fn into_before_mount(self, _: Url) -> BeforeMount { + BeforeMount::default() + } +} diff --git a/src/vdom/builder/init.rs b/src/vdom/builder/init.rs index 7084cc454..b10e96966 100644 --- a/src/vdom/builder/init.rs +++ b/src/vdom/builder/init.rs @@ -1,21 +1,15 @@ -use web_sys::Element; - use crate::{ dom_types::View, orders::OrdersContainer, - routing::Url, util, - vdom::{ - alias::*, - App, - builder::{ - before_mount::{MountPoint, MountType}, - after_mount::{UrlHandling}, - }, + routing::Url, + vdom::builder::{ + after_mount::{AfterMount, Into as IntoAfterMount, UrlHandling}, + before_mount::MountType, }, }; - /// Used as a flexible wrapper for the init function. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Init { /// Initial model to be used when the app begins. pub model: Mdl, @@ -45,29 +39,36 @@ impl Init { } } -impl Default for Init { - fn default() -> Self { - Self { - model: Mdl::default(), - url_handling: UrlHandling::PassToRoutes, - mount_type: MountType::Append, - } - } -} - -pub type InitFn = +pub type Fn = Box) -> Init>; -pub trait IntoInit, GMs> { +pub trait Into, GMs> { fn into_init(self, init_url: Url, ord: &mut OrdersContainer) -> Init; } -impl, GMs, F> IntoInit - for F - where - F: FnOnce(Url, &mut OrdersContainer) -> Init, +impl, GMs, F> Into for F +where + F: FnOnce(Url, &mut OrdersContainer) -> Init, { fn into_init(self, init_url: Url, ord: &mut OrdersContainer) -> Init { self(init_url, ord) } } + +impl, GMs> IntoAfterMount + for (Init, OrdersContainer) +{ + fn into_after_mount( + self: Box, + _: Url, + ord: &mut OrdersContainer, + ) -> AfterMount { + let (init, old_ord) = *self; + ord.effects = old_ord.effects; + ord.should_render = old_ord.should_render; + AfterMount { + model: init.model, + url_handling: init.url_handling, + } + } +} From 8e44d4b014911c6f931fb9acf4356c629c69a95a Mon Sep 17 00:00:00 2001 From: AlterionX Date: Wed, 20 Nov 2019 18:11:35 -0600 Subject: [PATCH 3/7] Clean up documentation. - Add deprecation notices to parts of old API. - Convert examples to new API. Add two additional conversions for AfterMount and BeforeMount. - Ensure window listeners are set after model is set. - Fix user_media example. --- examples/animation_frame/src/lib.rs | 9 ++-- examples/canvas/src/lib.rs | 10 +++-- examples/drop/README.md | 4 +- examples/drop/src/lib.rs | 10 +++-- examples/mathjax/src/lib.rs | 8 +--- examples/orders/src/lib.rs | 2 +- examples/server_integration/client/src/lib.rs | 2 +- examples/server_interaction/src/lib.rs | 8 ++-- .../reports/src/lib.rs | 4 +- examples/todomvc/src/lib.rs | 2 +- examples/update_from_js/src/lib.rs | 8 +--- examples/user_media/src/lib.rs | 10 +++-- examples/websocket/src/client.rs | 8 ++-- examples/window_events/src/lib.rs | 2 +- src/lib.rs | 2 +- src/vdom.rs | 28 ++++++++---- src/vdom/builder.rs | 45 +++++++++++++++++++ src/vdom/builder/after_mount.rs | 13 ++++++ src/vdom/builder/before_mount.rs | 17 ++++++- src/vdom/builder/init.rs | 32 +++++++++++++ 20 files changed, 168 insertions(+), 56 deletions(-) diff --git a/examples/animation_frame/src/lib.rs b/examples/animation_frame/src/lib.rs index f73ddbed6..241d5b29a 100644 --- a/examples/animation_frame/src/lib.rs +++ b/examples/animation_frame/src/lib.rs @@ -51,13 +51,13 @@ struct Model { car: Car, } -// Init +// AfterMount -fn init(_: Url, orders: &mut impl Orders) -> Init { +fn after_mount(_: Url, orders: &mut impl Orders) -> AfterMount { orders .send_msg(Msg::SetViewportWidth) .after_next_render(Msg::Rendered); - Init::new(Model::default()) + AfterMount::default() } // Update @@ -162,7 +162,8 @@ fn view_wheel(wheel_x: f64, car: &Car) -> Node { #[wasm_bindgen(start)] pub fn render() { - seed::App::build(init, update, view) + seed::App::builder(update, view) + .after_mount(after_mount) .window_events(|_| vec![simple_ev(Ev::Resize, Msg::SetViewportWidth)]) .build_and_start(); } diff --git a/examples/canvas/src/lib.rs b/examples/canvas/src/lib.rs index 9cc48ad43..d31b906ab 100644 --- a/examples/canvas/src/lib.rs +++ b/examples/canvas/src/lib.rs @@ -18,11 +18,11 @@ struct Model { fill_color: Color, } -// Init +// AfterMount -fn init(_: Url, orders: &mut impl Orders) -> Init { +fn after_mount(_: Url, orders: &mut impl Orders) -> AfterMount { orders.after_next_render(|_| Msg::Rendered); - Init::new(Model { + AfterMount::new(Model { fill_color: COLOR_A, }) } @@ -87,5 +87,7 @@ fn view(_model: &Model) -> impl View { #[wasm_bindgen(start)] pub fn render() { - seed::App::build(init, update, view).build_and_start(); + seed::App::builder(update, view) + .after_mount(after_mount) + .build_and_start(); } diff --git a/examples/drop/README.md b/examples/drop/README.md index 3af5f6fd9..8a4db658d 100644 --- a/examples/drop/README.md +++ b/examples/drop/README.md @@ -1,6 +1,6 @@ ## Drop example -How to crate a drop-zone. +How to create a drop-zone. --- @@ -8,4 +8,4 @@ How to crate a drop-zone. cargo make start ``` -Open [127.0.0.1:8000](http://127.0.0.1:8000) in your browser. \ No newline at end of file +Open [127.0.0.1:8000](http://127.0.0.1:8000) in your browser. diff --git a/examples/drop/src/lib.rs b/examples/drop/src/lib.rs index db32dd66a..31d515d64 100644 --- a/examples/drop/src/lib.rs +++ b/examples/drop/src/lib.rs @@ -11,10 +11,10 @@ struct Model { drop_zone_content: Vec>, } -// Init +// AfterMount -fn init(_: Url, _: &mut impl Orders) -> Init { - Init::new(Model { +fn after_mount(_: Url, _: &mut impl Orders) -> AfterMount { + AfterMount::new(Model { drop_zone_active: false, drop_zone_content: vec![div!["Drop files here"]], }) @@ -117,5 +117,7 @@ fn view(model: &Model) -> impl View { #[wasm_bindgen(start)] pub fn start() { - seed::App::build(init, update, view).build_and_start(); + seed::App::builder(update, view) + .after_mount(after_mount) + .build_and_start(); } diff --git a/examples/mathjax/src/lib.rs b/examples/mathjax/src/lib.rs index d08195450..b7880e1f8 100644 --- a/examples/mathjax/src/lib.rs +++ b/examples/mathjax/src/lib.rs @@ -226,13 +226,7 @@ fn view(model: &Model) -> impl View { ] } -// Init - -fn init(_: Url, _: &mut impl Orders) -> Init { - Init::new(Model::default()) -} - #[wasm_bindgen(start)] pub fn render() { - seed::App::build(init, update, view).build_and_start(); + seed::App::builder(update, view).build_and_start(); } diff --git a/examples/orders/src/lib.rs b/examples/orders/src/lib.rs index 9b85d1cda..d7a8d2cc8 100644 --- a/examples/orders/src/lib.rs +++ b/examples/orders/src/lib.rs @@ -93,5 +93,5 @@ fn view(model: &Model) -> impl View { #[wasm_bindgen(start)] pub fn start() { - seed::App::build(|_, _| Init::new(Model::default()), update, view).build_and_start(); + seed::App::builder(update, view).build_and_start(); } diff --git a/examples/server_integration/client/src/lib.rs b/examples/server_integration/client/src/lib.rs index 8c431f2f2..e7f3b4808 100644 --- a/examples/server_integration/client/src/lib.rs +++ b/examples/server_integration/client/src/lib.rs @@ -107,5 +107,5 @@ fn view_example_introduction(title: &str, description: &str) -> Vec> { #[wasm_bindgen(start)] pub fn start() { - seed::App::build(|_, _| Init::new(Model::default()), update, view).build_and_start(); + seed::App::builder(update, view).build_and_start(); } diff --git a/examples/server_interaction/src/lib.rs b/examples/server_interaction/src/lib.rs index 1956fae07..24500c8f5 100644 --- a/examples/server_interaction/src/lib.rs +++ b/examples/server_interaction/src/lib.rs @@ -128,12 +128,14 @@ fn view(model: &Model) -> Vec> { // Init -fn init(_: Url, orders: &mut impl Orders) -> Init { +fn after_mount(_: Url, orders: &mut impl Orders) -> AfterMount { orders.perform_cmd(fetch_repository_info()); - Init::new(Model::default()) + AfterMount::default() } #[wasm_bindgen(start)] pub fn render() { - seed::App::build(init, update, view).build_and_start(); + seed::App::builder(update, view) + .after_mount(after_mount) + .build_and_start(); } diff --git a/examples/server_interaction_detailed/reports/src/lib.rs b/examples/server_interaction_detailed/reports/src/lib.rs index db71883ad..27594b318 100644 --- a/examples/server_interaction_detailed/reports/src/lib.rs +++ b/examples/server_interaction_detailed/reports/src/lib.rs @@ -304,9 +304,7 @@ fn view(model: &Model) -> Vec> { #[wasm_bindgen] pub fn render() { - let state = seed::App::build(Model::default(), update, view) - .finish() - .run(); + let state = seed::App::builder(update, view).build_and_start(); state.update(Msg::GetData) } diff --git a/examples/todomvc/src/lib.rs b/examples/todomvc/src/lib.rs index d9b411737..0bc0d6db6 100644 --- a/examples/todomvc/src/lib.rs +++ b/examples/todomvc/src/lib.rs @@ -361,7 +361,7 @@ fn routes(url: seed::Url) -> Option { #[wasm_bindgen(start)] pub fn render() { - seed::App::build(|_, _| Init::new(Model::default()), update, view) + seed::App::builder(update, view) .routes(routes) .build_and_start(); } diff --git a/examples/update_from_js/src/lib.rs b/examples/update_from_js/src/lib.rs index 37f945f7d..8e24a08ff 100644 --- a/examples/update_from_js/src/lib.rs +++ b/examples/update_from_js/src/lib.rs @@ -12,12 +12,6 @@ struct Model { time_from_js: Option, } -// Init - -fn init(_: Url, _: &mut impl Orders) -> Init { - Init::new(Model::default()) -} - // Update #[derive(Clone)] @@ -76,7 +70,7 @@ fn view(model: &Model) -> Node { #[wasm_bindgen] // `wasm-bindgen` cannot transfer struct with public closures to JS (yet) so we have to send slice. pub fn start() -> Box<[JsValue]> { - let app = seed::App::build(init, update, view).build_and_start(); + let app = seed::App::builder(update, view).build_and_start(); create_closures_for_js(&app) } diff --git a/examples/user_media/src/lib.rs b/examples/user_media/src/lib.rs index 19137c51f..023245760 100644 --- a/examples/user_media/src/lib.rs +++ b/examples/user_media/src/lib.rs @@ -12,11 +12,11 @@ use web_sys::{HtmlMediaElement, MediaStream, MediaStreamConstraints}; struct Model {} -// Init +// AfterMount -fn init(_: Url, orders: &mut impl Orders) -> Init { +fn after_mount(_: Url, orders: &mut impl Orders) -> AfterMount { orders.perform_cmd(user_media()); - Init::new(Model {}) + AfterMount::new(Model {}) } fn user_media() -> impl Future { @@ -74,5 +74,7 @@ fn view(_: &Model) -> impl View { #[wasm_bindgen(start)] pub fn start() { - seed::App::build(init, update, view).build_and_start(); + seed::App::builder(update, view) + .after_mount(after_mount) + .build_and_start(); } diff --git a/examples/websocket/src/client.rs b/examples/websocket/src/client.rs index 8abdaab05..7340c0715 100644 --- a/examples/websocket/src/client.rs +++ b/examples/websocket/src/client.rs @@ -23,7 +23,7 @@ struct Model { // Init -fn init(_: Url, orders: &mut impl Orders) -> Init { +fn after_mount(_: Url, orders: &mut impl Orders) -> AfterMount { let ws = WebSocket::new(WS_URL).unwrap(); register_ws_handler(WebSocket::set_onopen, Msg::Connected, &ws, orders); @@ -31,7 +31,7 @@ fn init(_: Url, orders: &mut impl Orders) -> Init { register_ws_handler(WebSocket::set_onmessage, Msg::ServerMessage, &ws, orders); register_ws_handler(WebSocket::set_onerror, Msg::Error, &ws, orders); - Init::new(Model { + AfterMount::new(Model { ws, connected: false, msg_rx_cnt: 0, @@ -154,5 +154,7 @@ fn view(model: &Model) -> impl View { #[wasm_bindgen(start)] pub fn start() { - App::build(init, update, view).build_and_start(); + App::builder(update, view) + .after_mount(after_mount) + .build_and_start(); } diff --git a/examples/window_events/src/lib.rs b/examples/window_events/src/lib.rs index 3f9c8b946..48cfa911f 100644 --- a/examples/window_events/src/lib.rs +++ b/examples/window_events/src/lib.rs @@ -83,7 +83,7 @@ fn window_events(model: &Model) -> Vec> { #[wasm_bindgen(start)] pub fn render() { - seed::App::build(|_, _| Init::new(Model::default()), update, view) + seed::App::builder(update, view) .window_events(window_events) .build_and_start(); } diff --git a/src/lib.rs b/src/lib.rs index 902257106..0f605ddb9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,7 +100,7 @@ pub mod prelude { request_animation_frame, ClosureNew, RequestAnimationFrameHandle, RequestAnimationFrameTime, }, - vdom::{Init, MountType, RenderTimestampDelta, UrlHandling}, + vdom::{AfterMount, BeforeMount, Init, MountType, RenderTimestampDelta, UrlHandling}, }; pub use indexmap::IndexMap; // for attrs and style to work. pub use wasm_bindgen::prelude::*; diff --git a/src/vdom.rs b/src/vdom.rs index f988f8faa..acd4b80ee 100644 --- a/src/vdom.rs +++ b/src/vdom.rs @@ -18,7 +18,10 @@ pub use alias::*; // Building process. pub mod builder; -pub use builder::{Builder as AppBuilder, Init, InitFn, MountPoint, MountType, UrlHandling}; +pub use builder::{ + AfterMount, BeforeMount, Builder as AppBuilder, Init, InitFn, MountPoint, MountType, + UrlHandling, +}; use crate::{ dom_types::{self, El, MessageMapper, Namespace, Node, View}, @@ -171,12 +174,17 @@ impl, GMs> ::std::fmt::Debug for App = AppBuilder>>; /// We use a struct instead of series of functions, in order to avoid passing /// repetitive sequences of parameters. impl + 'static, GMs: 'static> App { + #[deprecated( + since = "0.4.3", + note = "Use `builder` with `AppBuilder::{after_mount, before_mount}` instead." + )] pub fn build( init: impl FnOnce(routing::Url, &mut OrdersContainer) -> Init + 'static, update: UpdateFn, @@ -275,10 +283,6 @@ impl + 'static, GMs: 'static> App { new.children = dom_nodes.children; } - self.setup_window_listeners(); - patch::setup_input_listeners(&mut new); - patch::attach_listeners(&mut new, &self.mailbox()); - // Recreate the needed nodes. Only do this if requested to takeover the mount point since // it should only be needed here. if mount_type == MountType::Takeover { @@ -325,10 +329,9 @@ impl + 'static, GMs: 'static> App { mount_type, into_after_mount, .. - } = self - .run_cfg - .take() - .expect("run_cfg should be set in App::new which is called from AppBuilder"); + } = self.run_cfg.take().expect( + "run_cfg should be set in App::new which is called from AppBuilder::build_and_start", + ); // Bootstrap the virtual DOM. self.data @@ -363,6 +366,13 @@ impl + 'static, GMs: 'static> App { UrlHandling::None => (), }; + self.setup_window_listeners(); + patch::setup_input_listeners(&mut self.data.main_el_vdom.borrow_mut().as_mut().unwrap()); + patch::attach_listeners( + self.data.main_el_vdom.borrow_mut().as_mut().unwrap(), + &self.mailbox(), + ); + // Update the state on page load, based // on the starting URL. Must be set up on the server as well. if let Some(routes) = *self.data.routes.borrow() { diff --git a/src/vdom/builder.rs b/src/vdom/builder.rs index 55c00fd28..18abd6778 100644 --- a/src/vdom/builder.rs +++ b/src/vdom/builder.rs @@ -14,14 +14,20 @@ pub use before_mount::{BeforeMount, Into as IntoBeforeMount, MountPoint, MountTy pub mod after_mount; pub use after_mount::{AfterMount, Into as IntoAfterMount, UrlHandling}; +#[deprecated( + since = "0.4.3", + note = "Used for compatability with old Init API. Use `BeforeAfterInitAPI` together with `BeforeMount` and `AfterMount` instead." +)] pub struct MountPointInitInitAPI { mount_point: MP, into_init: II, } +// TODO Remove when removing the other `InitAPI`s. pub struct BeforeAfterInitAPI { into_before_mount: IBM, into_after_mount: IAM, } +// TODO Remove when removing the other `InitAPI`s. impl Default for BeforeAfterInitAPI<(), ()> { fn default() -> Self { BeforeAfterInitAPI { @@ -31,14 +37,24 @@ impl Default for BeforeAfterInitAPI<(), ()> { } } +// TODO Remove when removing the other `InitAPI`s. pub trait InitAPI, GMs> { type Builder; fn build(builder: Self::Builder) -> App; } +// TODO Remove when removing the other `InitAPI`s. pub trait InitAPIData { type IntoBeforeMount; type IntoAfterMount; + #[deprecated( + since = "0.4.3", + note = "Used for compatability with old Init API. Use `IntoBeforeMount` and `IntoAfterMount` instead." + )] type IntoInit; + #[deprecated( + since = "0.4.3", + note = "Used for compatability with old Init API. Use `IntoBeforeMount` and `IntoAfterMount` instead." + )] type MountPoint; fn before_mount( @@ -56,16 +72,29 @@ pub trait InitAPIData { into_after_mount: NewIAM, ) -> BeforeAfterInitAPI; + #[deprecated( + since = "0.4.3", + note = "Used for compatability with old Init API. Use `before_mount` and `after_mount` instead." + )] fn init, GMs, NewII: IntoInit>( self, into_init: NewII, ) -> MountPointInitInitAPI; + #[deprecated( + since = "0.4.3", + note = "Used for compatability with old Init API. Use `before_mount` and `after_mount` instead." + )] fn mount( self, mount_point: NewMP, ) -> MountPointInitInitAPI; } +// TODO Remove when removing the other `InitAPI`s. +#[deprecated( + since = "0.4.3", + note = "Used for compatability with old Init API. Use `BeforeAfterInitAPI` together with `BeforeMount` and `AfterMount` instead." +)] impl< Ms: 'static, Mdl: 'static, @@ -104,6 +133,7 @@ impl< app } } +// TODO Remove when removing the other `InitAPI`s. impl< Ms: 'static, Mdl: 'static, @@ -140,6 +170,7 @@ impl< ) } } +// TODO Remove when removing the other `InitAPI`s. impl, GMs: 'static> InitAPI for () { @@ -158,6 +189,10 @@ impl, GMs: 'static> } } +#[deprecated( + since = "0.4.3", + note = "Used for compatability with old Init API. Use `BeforeAfterInitAPI` together with `BeforeMount` and `AfterMount` instead." +)] impl InitAPIData for MountPointInitInitAPI { type IntoBeforeMount = (); type IntoAfterMount = (); @@ -208,6 +243,7 @@ impl InitAPIData for MountPointInitInitAPI { } } } +// TODO Remove when removing the other `InitAPI`s. impl InitAPIData for BeforeAfterInitAPI { type IntoBeforeMount = IBM; type IntoAfterMount = IAM; @@ -258,6 +294,7 @@ impl InitAPIData for BeforeAfterInitAPI { } } } +// TODO Remove when removing the other `InitAPI`s. impl InitAPIData for () { type IntoBeforeMount = (); type IntoAfterMount = (); @@ -350,6 +387,10 @@ impl< InitAPIType: InitAPIData, > Builder { + #[deprecated( + since = "0.4.3", + note = "Used for compatability with old Init API. Use `before_mount` and `after_mount` instead." + )] pub fn init>( self, new_init: NewII, @@ -383,6 +424,10 @@ impl< /// // argument is `Element` /// mount(seed::body().querySelector("section").unwrap().unwrap()) /// ``` + #[deprecated( + since = "0.4.3", + note = "Used for compatability with old Init API. Use `before_mount` and `after_mount` instead." + )] pub fn mount( self, new_mount_point: NewMP, diff --git a/src/vdom/builder/after_mount.rs b/src/vdom/builder/after_mount.rs index b78f4d203..7ab2507d8 100644 --- a/src/vdom/builder/after_mount.rs +++ b/src/vdom/builder/after_mount.rs @@ -16,7 +16,10 @@ impl Default for UrlHandling { #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct AfterMount { + /// Initial model to be used when the app begins. pub model: Mdl, + /// How to handle initial url routing. Defaults to [`UrlHandling::PassToRoutes`] in the + /// constructors. pub url_handling: UrlHandling, } @@ -52,6 +55,16 @@ pub trait Into, GMs> { ) -> AfterMount; } +impl, GMs> Into for AfterMount { + fn into_after_mount( + self: Box, + _: Url, + _: &mut OrdersContainer, + ) -> AfterMount { + *self + } +} + impl, GMs, F> Into for F where F: FnOnce(Url, &mut OrdersContainer) -> AfterMount, diff --git a/src/vdom/builder/before_mount.rs b/src/vdom/builder/before_mount.rs index 950307141..262c58958 100644 --- a/src/vdom/builder/before_mount.rs +++ b/src/vdom/builder/before_mount.rs @@ -56,9 +56,11 @@ impl Default for MountType { } } -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct BeforeMount { pub mount_point: MP, + /// How to handle elements already present in the mount. Defaults to [`MountType::Append`] + /// in the constructors. pub mount_type: MountType, } @@ -83,11 +85,24 @@ impl BeforeMount { } } +impl Default for BeforeMount<()> { + fn default() -> Self { + Self::new(()) + } +} + pub trait Into { type MP: MountPoint; fn into_before_mount(self, init_url: Url) -> BeforeMount; } +impl Into for BeforeMount { + type MP = MP; + fn into_before_mount(self, _: Url) -> BeforeMount { + self + } +} + impl Into for F where F: FnOnce(Url) -> BeforeMount, diff --git a/src/vdom/builder/init.rs b/src/vdom/builder/init.rs index b10e96966..362c7c7ee 100644 --- a/src/vdom/builder/init.rs +++ b/src/vdom/builder/init.rs @@ -10,18 +10,38 @@ use crate::{ /// Used as a flexible wrapper for the init function. #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[deprecated( + since = "0.4.3", + note = "Part of old Init API. Use a combination of `BeforeMount` and `AfterMount` instead." +)] pub struct Init { /// Initial model to be used when the app begins. + #[deprecated( + since = "0.4.3", + note = "Part of old Init API. Use `AfterMount` instead." + )] pub model: Mdl, /// How to handle initial url routing. Defaults to [`UrlHandling::PassToRoutes`] in the /// constructors. + #[deprecated( + since = "0.4.3", + note = "Part of old Init API. Use `AfterMount` instead." + )] pub url_handling: UrlHandling, /// How to handle elements already present in the mount. Defaults to [`MountType::Append`] /// in the constructors. + #[deprecated( + since = "0.4.3", + note = "Part of old Init API. Use `BeforeMount` instead." + )] pub mount_type: MountType, } impl Init { + #[deprecated( + since = "0.4.3", + note = "Part of old Init API. Use `AfterMount` instead." + )] pub const fn new(model: Mdl) -> Self { Self { model, @@ -30,6 +50,10 @@ impl Init { } } + #[deprecated( + since = "0.4.3", + note = "Part of old Init API. Use `AfterMount` instead." + )] pub const fn new_with_url_handling(model: Mdl, url_handling: UrlHandling) -> Self { Self { model, @@ -39,9 +63,17 @@ impl Init { } } +#[deprecated( + since = "0.4.3", + note = "Part of old Init API. Use `AfterMount` instead." +)] pub type Fn = Box) -> Init>; +#[deprecated( + since = "0.4.3", + note = "Part of old Init API. Use `IntoAfterMount` and `IntoBeforeMount` instead." +)] pub trait Into, GMs> { fn into_init(self, init_url: Url, ord: &mut OrdersContainer) -> Init; } From 8c22855af0104117c9f1458d66d3f993ac4d8967 Mon Sep 17 00:00:00 2001 From: AlterionX Date: Fri, 22 Nov 2019 01:13:44 -0600 Subject: [PATCH 4/7] Added to Changelog and various edits to comments. --- CHANGELOG.md | 7 +++++++ src/vdom.rs | 21 +++++++++------------ src/vdom/builder.rs | 36 ++++++++++++++++++------------------ src/vdom/builder/init.rs | 16 ++++++++-------- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94d2d4ab0..803711bd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ - Fixed jumping cursor in inputs (#158) - Added method `orders.after_next_render(Option)` (#207) - Fixed a bug with back/forward routing to the landing page (#296) +- [BREAKING] Deprecated `Init` struct, replacing it with `BeforeMount` and `AfterMount` structs to +better denote state before and after mounting the `App` occurs. +- [BREAKING] Added a new function `builder` which replaces `build` as part of deprecating `Init`. +- [BREAKING] Added a new function `build` which replaces `finish` as part of deprecating `Init`. +- Added `IntoInit`, `IntoAfterMount`, and `IntoBeforeMount` traits. It is possible to use these +in place of a closure or function to produce the corresponding `Init`, `AfterMount`, and +`BeforeMount` structs. ## v0.4.2 - Added an `Init` struct, which can help with initial routing (Breaking) diff --git a/src/vdom.rs b/src/vdom.rs index acd4b80ee..a90534516 100644 --- a/src/vdom.rs +++ b/src/vdom.rs @@ -160,9 +160,9 @@ where Mdl: 'static, ElC: View, { - /// State that is removed after app begins running. + /// Temporary app configuration that is removed after app begins running. pub run_cfg: OptDynRunCfg, - /// Stateless app configuration + /// App configuration available for the entire application lifetime. pub cfg: Rc>, /// Mutable app state pub data: Rc>, @@ -174,7 +174,7 @@ impl, GMs> ::std::fmt::Debug for App = AppBuilder>>; @@ -182,7 +182,7 @@ type InitAppBuilder = /// repetitive sequences of parameters. impl + 'static, GMs: 'static> App { #[deprecated( - since = "0.4.3", + since = "0.5.0", note = "Use `builder` with `AppBuilder::{after_mount, before_mount}` instead." )] pub fn build( @@ -330,7 +330,7 @@ impl + 'static, GMs: 'static> App { into_after_mount, .. } = self.run_cfg.take().expect( - "run_cfg should be set in App::new which is called from AppBuilder::build_and_start", + "`run_cfg` should be set in `App::new` which is called from `AppBuilder::build_and_start`", ); // Bootstrap the virtual DOM. @@ -338,7 +338,6 @@ impl + 'static, GMs: 'static> App { .main_el_vdom .replace(Some(self.bootstrap_vdom(mount_type))); - // Can this simply be `self.update`? let mut orders = OrdersContainer::new(self.clone()); let builder::AfterMount { model, @@ -347,9 +346,6 @@ impl + 'static, GMs: 'static> App { self.data.model.replace(Some(model)); - // TODO: Does this go before or after setting up the routes listener? - // TODO: Does this go before or after the initial render? The effects are created before - // TODO: the render, but after the orders. But old behavior let this run before the orders. match url_handling { UrlHandling::PassToRoutes => { let url = routing::current_url(); @@ -393,10 +389,11 @@ impl + 'static, GMs: 'static> App { routing::setup_link_listener(enclose!((self => s) move |msg| s.update(msg)), routes); } - // Our initial render. Can't initialize in new due to mailbox() requiring self. self.process_cmd_and_msg_queue(orders.effects); - // TODO: In the future, only run the following line if the above statement does not - // TODO: call `rerender_vdom` for efficiency. + // TODO: In the future, only run the following line if the above statement: + // - didn't force-rerender vdom + // - didn't schedule render + // - doesn't want to skip render self.rerender_vdom(); self diff --git a/src/vdom/builder.rs b/src/vdom/builder.rs index 18abd6778..1fdd595f9 100644 --- a/src/vdom/builder.rs +++ b/src/vdom/builder.rs @@ -15,8 +15,8 @@ pub mod after_mount; pub use after_mount::{AfterMount, Into as IntoAfterMount, UrlHandling}; #[deprecated( - since = "0.4.3", - note = "Used for compatability with old Init API. Use `BeforeAfterInitAPI` together with `BeforeMount` and `AfterMount` instead." + since = "0.5.0", + note = "Used for compatibility with old Init API. Use `BeforeAfterInitAPI` together with `BeforeMount` and `AfterMount` instead." )] pub struct MountPointInitInitAPI { mount_point: MP, @@ -47,13 +47,13 @@ pub trait InitAPIData { type IntoBeforeMount; type IntoAfterMount; #[deprecated( - since = "0.4.3", - note = "Used for compatability with old Init API. Use `IntoBeforeMount` and `IntoAfterMount` instead." + since = "0.5.0", + note = "Used for compatibility with old Init API. Use `IntoBeforeMount` and `IntoAfterMount` instead." )] type IntoInit; #[deprecated( - since = "0.4.3", - note = "Used for compatability with old Init API. Use `IntoBeforeMount` and `IntoAfterMount` instead." + since = "0.5.0", + note = "Used for compatibility with old Init API. Use `IntoBeforeMount` and `IntoAfterMount` instead." )] type MountPoint; @@ -73,16 +73,16 @@ pub trait InitAPIData { ) -> BeforeAfterInitAPI; #[deprecated( - since = "0.4.3", - note = "Used for compatability with old Init API. Use `before_mount` and `after_mount` instead." + since = "0.5.0", + note = "Used for compatibility with old Init API. Use `before_mount` and `after_mount` instead." )] fn init, GMs, NewII: IntoInit>( self, into_init: NewII, ) -> MountPointInitInitAPI; #[deprecated( - since = "0.4.3", - note = "Used for compatability with old Init API. Use `before_mount` and `after_mount` instead." + since = "0.5.0", + note = "Used for compatibility with old Init API. Use `before_mount` and `after_mount` instead." )] fn mount( self, @@ -92,8 +92,8 @@ pub trait InitAPIData { // TODO Remove when removing the other `InitAPI`s. #[deprecated( - since = "0.4.3", - note = "Used for compatability with old Init API. Use `BeforeAfterInitAPI` together with `BeforeMount` and `AfterMount` instead." + since = "0.5.0", + note = "Used for compatibility with old Init API. Use `BeforeAfterInitAPI` together with `BeforeMount` and `AfterMount` instead." )] impl< Ms: 'static, @@ -190,8 +190,8 @@ impl, GMs: 'static> } #[deprecated( - since = "0.4.3", - note = "Used for compatability with old Init API. Use `BeforeAfterInitAPI` together with `BeforeMount` and `AfterMount` instead." + since = "0.5.0", + note = "Used for compatibility with old Init API. Use `BeforeAfterInitAPI` together with `BeforeMount` and `AfterMount` instead." )] impl InitAPIData for MountPointInitInitAPI { type IntoBeforeMount = (); @@ -388,8 +388,8 @@ impl< > Builder { #[deprecated( - since = "0.4.3", - note = "Used for compatability with old Init API. Use `before_mount` and `after_mount` instead." + since = "0.5.0", + note = "Used for compatibility with old Init API. Use `before_mount` and `after_mount` instead." )] pub fn init>( self, @@ -425,8 +425,8 @@ impl< /// mount(seed::body().querySelector("section").unwrap().unwrap()) /// ``` #[deprecated( - since = "0.4.3", - note = "Used for compatability with old Init API. Use `before_mount` and `after_mount` instead." + since = "0.5.0", + note = "Used for compatibility with old Init API. Use `before_mount` and `after_mount` instead." )] pub fn mount( self, diff --git a/src/vdom/builder/init.rs b/src/vdom/builder/init.rs index 362c7c7ee..5197b5038 100644 --- a/src/vdom/builder/init.rs +++ b/src/vdom/builder/init.rs @@ -11,27 +11,27 @@ use crate::{ /// Used as a flexible wrapper for the init function. #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] #[deprecated( - since = "0.4.3", + since = "0.5.0", note = "Part of old Init API. Use a combination of `BeforeMount` and `AfterMount` instead." )] pub struct Init { /// Initial model to be used when the app begins. #[deprecated( - since = "0.4.3", + since = "0.5.0", note = "Part of old Init API. Use `AfterMount` instead." )] pub model: Mdl, /// How to handle initial url routing. Defaults to [`UrlHandling::PassToRoutes`] in the /// constructors. #[deprecated( - since = "0.4.3", + since = "0.5.0", note = "Part of old Init API. Use `AfterMount` instead." )] pub url_handling: UrlHandling, /// How to handle elements already present in the mount. Defaults to [`MountType::Append`] /// in the constructors. #[deprecated( - since = "0.4.3", + since = "0.5.0", note = "Part of old Init API. Use `BeforeMount` instead." )] pub mount_type: MountType, @@ -39,7 +39,7 @@ pub struct Init { impl Init { #[deprecated( - since = "0.4.3", + since = "0.5.0", note = "Part of old Init API. Use `AfterMount` instead." )] pub const fn new(model: Mdl) -> Self { @@ -51,7 +51,7 @@ impl Init { } #[deprecated( - since = "0.4.3", + since = "0.5.0", note = "Part of old Init API. Use `AfterMount` instead." )] pub const fn new_with_url_handling(model: Mdl, url_handling: UrlHandling) -> Self { @@ -64,14 +64,14 @@ impl Init { } #[deprecated( - since = "0.4.3", + since = "0.5.0", note = "Part of old Init API. Use `AfterMount` instead." )] pub type Fn = Box) -> Init>; #[deprecated( - since = "0.4.3", + since = "0.5.0", note = "Part of old Init API. Use `IntoAfterMount` and `IntoBeforeMount` instead." )] pub trait Into, GMs> { From e89cbbb04fc819ed62e48a43d8717e2efc29a81c Mon Sep 17 00:00:00 2001 From: AlterionX Date: Fri, 22 Nov 2019 02:12:52 -0600 Subject: [PATCH 5/7] Refactor for naming clarity and split off internals swapping for OrderContainer. --- examples/drop/src/lib.rs | 14 ++++---------- src/orders.rs | 5 +++++ src/vdom/builder.rs | 6 +++--- src/vdom/builder/after_mount.rs | 9 +++++---- src/vdom/builder/before_mount.rs | 9 +++++---- src/vdom/builder/init.rs | 13 +++++++------ 6 files changed, 29 insertions(+), 27 deletions(-) diff --git a/examples/drop/src/lib.rs b/examples/drop/src/lib.rs index 31d515d64..9d5bc4a63 100644 --- a/examples/drop/src/lib.rs +++ b/examples/drop/src/lib.rs @@ -11,15 +11,6 @@ struct Model { drop_zone_content: Vec>, } -// AfterMount - -fn after_mount(_: Url, _: &mut impl Orders) -> AfterMount { - AfterMount::new(Model { - drop_zone_active: false, - drop_zone_content: vec![div!["Drop files here"]], - }) -} - // Update #[derive(Clone, Debug)] @@ -118,6 +109,9 @@ fn view(model: &Model) -> impl View { #[wasm_bindgen(start)] pub fn start() { seed::App::builder(update, view) - .after_mount(after_mount) + .after_mount(AfterMount::new(Model { + drop_zone_active: false, + drop_zone_content: vec![div!["Drop files here"]], + })) .build_and_start(); } diff --git a/src/orders.rs b/src/orders.rs index a12994426..5cdda9afe 100644 --- a/src/orders.rs +++ b/src/orders.rs @@ -110,6 +110,11 @@ impl, GMs> OrdersContainer { app, } } + + pub(crate) fn append_from(&mut self, mut other: Self) { + self.should_render = other.should_render; + self.effects.append(&mut other.effects); + } } impl + 'static, GMs> Orders diff --git a/src/vdom/builder.rs b/src/vdom/builder.rs index 1fdd595f9..1074d991b 100644 --- a/src/vdom/builder.rs +++ b/src/vdom/builder.rs @@ -8,11 +8,11 @@ use crate::{ }; pub mod init; -pub use init::{Fn as InitFn, Init, Into as IntoInit}; +pub use init::{Init, InitFn, IntoInit}; pub mod before_mount; -pub use before_mount::{BeforeMount, Into as IntoBeforeMount, MountPoint, MountType}; +pub use before_mount::{BeforeMount, IntoBeforeMount, MountPoint, MountType}; pub mod after_mount; -pub use after_mount::{AfterMount, Into as IntoAfterMount, UrlHandling}; +pub use after_mount::{AfterMount, IntoAfterMount, UrlHandling}; #[deprecated( since = "0.5.0", diff --git a/src/vdom/builder/after_mount.rs b/src/vdom/builder/after_mount.rs index 7ab2507d8..4cd3b43ff 100644 --- a/src/vdom/builder/after_mount.rs +++ b/src/vdom/builder/after_mount.rs @@ -47,7 +47,8 @@ impl AfterMount { } } -pub trait Into, GMs> { +#[allow(clippy::module_name_repetitions)] +pub trait IntoAfterMount, GMs> { fn into_after_mount( self: Box, init_url: Url, @@ -55,7 +56,7 @@ pub trait Into, GMs> { ) -> AfterMount; } -impl, GMs> Into for AfterMount { +impl, GMs> IntoAfterMount for AfterMount { fn into_after_mount( self: Box, _: Url, @@ -65,7 +66,7 @@ impl, GMs> Into for } } -impl, GMs, F> Into for F +impl, GMs, F> IntoAfterMount for F where F: FnOnce(Url, &mut OrdersContainer) -> AfterMount, { @@ -78,7 +79,7 @@ where } } -impl, GMs> Into for () { +impl, GMs> IntoAfterMount for () { fn into_after_mount( self: Box, _: Url, diff --git a/src/vdom/builder/before_mount.rs b/src/vdom/builder/before_mount.rs index 262c58958..4f8a4b3bf 100644 --- a/src/vdom/builder/before_mount.rs +++ b/src/vdom/builder/before_mount.rs @@ -91,19 +91,20 @@ impl Default for BeforeMount<()> { } } -pub trait Into { +#[allow(clippy::module_name_repetitions)] +pub trait IntoBeforeMount { type MP: MountPoint; fn into_before_mount(self, init_url: Url) -> BeforeMount; } -impl Into for BeforeMount { +impl IntoBeforeMount for BeforeMount { type MP = MP; fn into_before_mount(self, _: Url) -> BeforeMount { self } } -impl Into for F +impl IntoBeforeMount for F where F: FnOnce(Url) -> BeforeMount, { @@ -113,7 +114,7 @@ where } } -impl Into for () { +impl IntoBeforeMount for () { type MP = (); fn into_before_mount(self, _: Url) -> BeforeMount { BeforeMount::default() diff --git a/src/vdom/builder/init.rs b/src/vdom/builder/init.rs index 5197b5038..cde718a44 100644 --- a/src/vdom/builder/init.rs +++ b/src/vdom/builder/init.rs @@ -3,7 +3,7 @@ use crate::{ orders::OrdersContainer, routing::Url, vdom::builder::{ - after_mount::{AfterMount, Into as IntoAfterMount, UrlHandling}, + after_mount::{AfterMount, IntoAfterMount, UrlHandling}, before_mount::MountType, }, }; @@ -63,22 +63,24 @@ impl Init { } } +#[allow(clippy::module_name_repetitions)] #[deprecated( since = "0.5.0", note = "Part of old Init API. Use `AfterMount` instead." )] -pub type Fn = +pub type InitFn = Box) -> Init>; +#[allow(clippy::module_name_repetitions)] #[deprecated( since = "0.5.0", note = "Part of old Init API. Use `IntoAfterMount` and `IntoBeforeMount` instead." )] -pub trait Into, GMs> { +pub trait IntoInit, GMs> { fn into_init(self, init_url: Url, ord: &mut OrdersContainer) -> Init; } -impl, GMs, F> Into for F +impl, GMs, F> IntoInit for F where F: FnOnce(Url, &mut OrdersContainer) -> Init, { @@ -96,8 +98,7 @@ impl, GMs> IntoAfterMount ord: &mut OrdersContainer, ) -> AfterMount { let (init, old_ord) = *self; - ord.effects = old_ord.effects; - ord.should_render = old_ord.should_render; + ord.append_from(old_ord); AfterMount { model: init.model, url_handling: init.url_handling, From 3563de300b58ace2b27e284f8b091869c0cc5fcf Mon Sep 17 00:00:00 2001 From: AlterionX Date: Mon, 25 Nov 2019 19:07:11 -0600 Subject: [PATCH 6/7] Make routing messages occur after messages from `InitAfterMount` --- CHANGELOG.md | 1 + src/vdom.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 803711bd4..4fa2a7961 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ better denote state before and after mounting the `App` occurs. - Added `IntoInit`, `IntoAfterMount`, and `IntoBeforeMount` traits. It is possible to use these in place of a closure or function to produce the corresponding `Init`, `AfterMount`, and `BeforeMount` structs. +- Messages sent from `IntoAfterMount` will now be run after the routing message. ## v0.4.2 - Added an `Init` struct, which can help with initial routing (Breaking) diff --git a/src/vdom.rs b/src/vdom.rs index a90534516..f2e858c6f 100644 --- a/src/vdom.rs +++ b/src/vdom.rs @@ -349,14 +349,14 @@ impl + 'static, GMs: 'static> App { match url_handling { UrlHandling::PassToRoutes => { let url = routing::current_url(); - if let Some(routes) = self.data.routes.borrow().as_ref() { - if let Some(routing_msg) = routes(url) { - (self.cfg.update)( - routing_msg, - &mut self.data.model.borrow_mut().as_mut().unwrap(), - &mut orders, - ); - } + let routing_msg = self + .data + .routes + .borrow() + .as_ref() + .and_then(|routes| routes(url)); + if let Some(routing_msg) = routing_msg { + orders.effects.push_back(routing_msg.into()); } } UrlHandling::None => (), From e590679841537690ba7905a022f12df3aeab1763 Mon Sep 17 00:00:00 2001 From: AlterionX Date: Wed, 27 Nov 2019 22:43:35 -0600 Subject: [PATCH 7/7] Rename `run_cfg` to `init_cfg` and relevant changes. Rename `OrdersContainer::append_fron` to `merge`. --- src/orders.rs | 2 +- src/vdom.rs | 20 ++++++++++---------- src/vdom/builder.rs | 6 +++--- src/vdom/builder/init.rs | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/orders.rs b/src/orders.rs index 5cdda9afe..98378373c 100644 --- a/src/orders.rs +++ b/src/orders.rs @@ -111,7 +111,7 @@ impl, GMs> OrdersContainer { } } - pub(crate) fn append_from(&mut self, mut other: Self) { + pub(crate) fn merge(&mut self, mut other: Self) { self.should_render = other.should_render; self.effects.append(&mut other.effects); } diff --git a/src/vdom.rs b/src/vdom.rs index f2e858c6f..aabf12e7a 100644 --- a/src/vdom.rs +++ b/src/vdom.rs @@ -125,10 +125,10 @@ pub struct AppData { pub render_timestamp: Cell>, } -type OptDynRunCfg = - Option>>; +type OptDynInitCfg = + Option>>; -pub struct AppRunCfg +pub struct AppInitCfg where Ms: 'static, Mdl: 'static, @@ -161,7 +161,7 @@ where ElC: View, { /// Temporary app configuration that is removed after app begins running. - pub run_cfg: OptDynRunCfg, + pub init_cfg: OptDynInitCfg, /// App configuration available for the entire application lifetime. pub cfg: Rc>, /// Mutable app state @@ -216,13 +216,13 @@ impl + 'static, GMs: 'static> App { mount_point: Element, routes: Option>, window_events: Option>, - run_cfg: OptDynRunCfg, + init_cfg: OptDynInitCfg, ) -> Self { let window = util::window(); let document = window.document().expect("Can't find the window's document"); Self { - run_cfg, + init_cfg, cfg: Rc::new(AppCfg { document, mount_point, @@ -325,12 +325,12 @@ impl + 'static, GMs: 'static> App { note = "Please use `AppBuilder.build_and_start` instead" )] pub fn run(mut self) -> Self { - let AppRunCfg { + let AppInitCfg { mount_type, into_after_mount, .. - } = self.run_cfg.take().expect( - "`run_cfg` should be set in `App::new` which is called from `AppBuilder::build_and_start`", + } = self.init_cfg.take().expect( + "`init_cfg` should be set in `App::new` which is called from `AppBuilder::build_and_start`", ); // Bootstrap the virtual DOM. @@ -622,7 +622,7 @@ impl + 'static, GMs: 'static> App { impl, GMs> Clone for App { fn clone(&self) -> Self { Self { - run_cfg: None, + init_cfg: None, cfg: Rc::clone(&self.cfg), data: Rc::clone(&self.data), } diff --git a/src/vdom/builder.rs b/src/vdom/builder.rs index 1074d991b..4fb31a1bc 100644 --- a/src/vdom/builder.rs +++ b/src/vdom/builder.rs @@ -4,7 +4,7 @@ use crate::{ dom_types::View, orders::OrdersContainer, routing, - vdom::{alias::*, App, AppRunCfg}, + vdom::{alias::*, App, AppInitCfg}, }; pub mod init; @@ -124,7 +124,7 @@ impl< let mut initial_orders = OrdersContainer::new(app.clone()); let init = into_init.into_init(routing::current_url(), &mut initial_orders); - app.run_cfg.replace(AppRunCfg { + app.init_cfg.replace(AppInitCfg { mount_type: init.mount_type, into_after_mount: Box::new((init, initial_orders)), phantom: PhantomData, @@ -162,7 +162,7 @@ impl< mount_point.element(), builder.routes, builder.window_events, - Some(AppRunCfg { + Some(AppInitCfg { mount_type, into_after_mount: Box::new(into_after_mount), phantom: PhantomData, diff --git a/src/vdom/builder/init.rs b/src/vdom/builder/init.rs index cde718a44..16ebe50dc 100644 --- a/src/vdom/builder/init.rs +++ b/src/vdom/builder/init.rs @@ -98,7 +98,7 @@ impl, GMs> IntoAfterMount ord: &mut OrdersContainer, ) -> AfterMount { let (init, old_ord) = *self; - ord.append_from(old_ord); + ord.merge(old_ord); AfterMount { model: init.model, url_handling: init.url_handling,