From ba26b1001a4bbd414193c3ff1eab09f32e91ab0c Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 30 Nov 2022 17:44:00 -0500 Subject: [PATCH] chore: clean up web impl --- packages/web/src/dom.rs | 7 --- packages/web/src/hot_reload.rs | 33 +++++++++---- packages/web/src/lib.rs | 88 ++++++++++++++-------------------- 3 files changed, 60 insertions(+), 68 deletions(-) diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 20e50c0fd..e211188c9 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -118,13 +118,6 @@ impl WebsysDom { } } -pub struct DioxusWebsysEvent(web_sys::Event); - -// safety: currently the web is not multithreaded and our VirtualDom exists on the same thread -#[allow(clippy::non_send_fields_in_send_ty)] -unsafe impl Send for DioxusWebsysEvent {} -unsafe impl Sync for DioxusWebsysEvent {} - // todo: some of these events are being casted to the wrong event type. // We need tests that simulate clicks/etc and make sure every event type works. pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -> Rc { diff --git a/packages/web/src/hot_reload.rs b/packages/web/src/hot_reload.rs index 7305cb0cb..ac474370a 100644 --- a/packages/web/src/hot_reload.rs +++ b/packages/web/src/hot_reload.rs @@ -1,11 +1,24 @@ -use dioxus_core::SchedulerMsg; -use dioxus_core::SetTemplateMsg; -use dioxus_core::VirtualDom; +#![allow(dead_code)] + +use futures_channel::mpsc::UnboundedReceiver; + use wasm_bindgen::closure::Closure; use wasm_bindgen::JsCast; use web_sys::{MessageEvent, WebSocket}; -pub(crate) fn init(dom: &VirtualDom) { +#[cfg(not(debug_assertions))] +pub(crate) fn init() -> UnboundedReceiver { + let (tx, rx) = futures_channel::mpsc::unbounded(); + + std::mem::forget(tx); + + rx +} + +#[cfg(debug_assertions)] +pub(crate) fn init() -> UnboundedReceiver { + use std::convert::TryInto; + let window = web_sys::window().unwrap(); let protocol = if window.location().protocol().unwrap() == "https:" { @@ -20,18 +33,20 @@ pub(crate) fn init(dom: &VirtualDom) { ); let ws = WebSocket::new(&url).unwrap(); - let mut channel = dom.get_scheduler_channel(); + + let (tx, rx) = futures_channel::mpsc::unbounded(); // change the rsx when new data is received let cl = Closure::wrap(Box::new(move |e: MessageEvent| { if let Ok(text) = e.data().dyn_into::() { - let msg: SetTemplateMsg = serde_json::from_str(&format!("{text}")).unwrap(); - channel - .start_send(SchedulerMsg::SetTemplate(Box::new(msg))) - .unwrap(); + if let Ok(val) = text.try_into() { + _ = tx.unbounded_send(val); + } } }) as Box); ws.set_onmessage(Some(cl.as_ref().unchecked_ref())); cl.forget(); + + rx } diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 5b33bb9a4..68ccf3cf5 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -9,9 +9,8 @@ //! - idle work //! - animations //! - jank-free rendering -//! - noderefs //! - controlled components -//! - re-hydration +//! - hydration //! - and more. //! //! The actual implementation is farily thin, with the heavy lifting happening inside the Dioxus Core crate. @@ -54,24 +53,22 @@ // - Do the VDOM work during the idlecallback // - Do DOM work in the next requestAnimationFrame callback -use std::{rc::Rc, time::Duration}; +use std::time::Duration; pub use crate::cfg::Config; use crate::dom::virtual_event_from_websys_event; pub use crate::util::use_eval; use dioxus_core::{Element, ElementId, Scope, VirtualDom}; +use futures_channel::mpsc::unbounded; use futures_util::{pin_mut, FutureExt, StreamExt}; use gloo_timers::future::sleep; -use web_sys::Event; mod cache; mod cfg; mod dom; -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// mod hot_reload; -// #[cfg(feature = "hydrate")] +mod hot_reload; // mod rehydrate; -// mod ric_raf; +mod ric_raf; mod util; /// Launch the VirtualDOM given a root component and a configuration. @@ -175,8 +172,7 @@ pub async fn run_with_props(root: fn(Scope) -> Element, root_prop console_error_panic_hook::set_once(); } - // #[cfg(any(feature = "hot-reload", debug_assertions))] - // hot_reload::init(&dom); + let mut hotreload_rx = hot_reload::init(); for s in crate::cache::BUILTIN_INTERNED_STRINGS { wasm_bindgen::intern(s); @@ -185,7 +181,7 @@ pub async fn run_with_props(root: fn(Scope) -> Element, root_prop wasm_bindgen::intern(s); } - // a let should_hydrate = cfg.hydrate; + let should_hydrate = cfg.hydrate; let (tx, mut rx) = futures_channel::mpsc::unbounded(); @@ -193,30 +189,39 @@ pub async fn run_with_props(root: fn(Scope) -> Element, root_prop log::info!("rebuilding app"); - let edits = dom.rebuild(); - websys_dom.apply_edits(edits.template_mutations); - websys_dom.apply_edits(edits.edits); + if should_hydrate { + } else { + let edits = dom.rebuild(); + websys_dom.apply_edits(edits.template_mutations); + websys_dom.apply_edits(edits.edits); + } + + let mut work_loop = ric_raf::RafLoop::new(); loop { log::trace!("waiting for work"); + // if virtualdom has nothing, wait for it to have something before requesting idle time // if there is work then this future resolves immediately. - let mut res = { let work = dom.wait_for_work().fuse(); pin_mut!(work); - futures_util::select! { _ = work => None, + new_template = hotreload_rx.next() => { + todo!("Implement hot reload"); + None + } evt = rx.next() => evt } }; + // Dequeue all of the events from the channel in send order + // todo: we should re-order these if possible while let Some(evt) = res { let name = evt.type_(); let element = walk_event_for_id(&evt); let bubbles = dioxus_html::event_bubbles(name.as_str()); - if let Some((element, target)) = element { let data = virtual_event_from_websys_event(evt, target); dom.handle_event(name.as_str(), data, element, bubbles); @@ -224,57 +229,38 @@ pub async fn run_with_props(root: fn(Scope) -> Element, root_prop res = rx.try_next().transpose().unwrap().ok(); } - let deadline = sleep(Duration::from_millis(50)); - - let edits = dom.render_with_deadline(deadline).await; - - log::trace!("working.."); + // Jank free rendering + // + // 1. wait for the browser to give us "idle" time + // 2. During idle time, diff the dom + // 3. Stop diffing if the deadline is exceded + // 4. Wait for the animation frame to patch the dom // wait for the mainthread to schedule us in - // let mut deadline = work_loop.wait_for_idle_time().await; + let deadline = work_loop.wait_for_idle_time().await; // run the virtualdom work phase until the frame deadline is reached - // let mutations = dom.work_with_deadline(|| (&mut deadline).now_or_never().is_some()); + let edits = dom.render_with_deadline(deadline).await; // wait for the animation frame to fire so we can apply our changes - // work_loop.wait_for_raf().await; + work_loop.wait_for_raf().await; + + log::debug!("edits {:#?}", edits); - // for edit in mutations { - // // actually apply our changes during the animation frame websys_dom.apply_edits(edits.template_mutations); websys_dom.apply_edits(edits.edits); - // } } } -fn walk_event_for_id(event: &Event) -> Option<(ElementId, web_sys::Element)> { - use wasm_bindgen::{closure::Closure, JsCast, JsValue}; - use web_sys::{Document, Element, Event, HtmlElement}; +fn walk_event_for_id(event: &web_sys::Event) -> Option<(ElementId, web_sys::Element)> { + use wasm_bindgen::JsCast; let mut target = event .target() .expect("missing target") - .dyn_into::() + .dyn_into::() .expect("not a valid element"); - // break Ok(UserEvent { - // name: event_name_from_typ(&typ), - // data: virtual_event_from_websys_event(event.clone(), target.clone()), - // element: Some(ElementId(id)), - // scope_id: None, - // priority: dioxus_core::EventPriority::Medium, - // bubbles: event.bubbles(), - // }); - - // break Ok(UserEvent { - // name: event_name_from_typ(&typ), - // data: virtual_event_from_websys_event(event.clone(), target.clone()), - // element: None, - // scope_id: None, - // priority: dioxus_core::EventPriority::Low, - // bubbles: event.bubbles(), - // }); - loop { match target.get_attribute("data-dioxus-id").map(|f| f.parse()) { Some(Ok(id)) => return Some((ElementId(id), target)), @@ -315,5 +301,3 @@ fn walk_event_for_id(event: &Event) -> Option<(ElementId, web_sys::Element)> { // websys_dom.apply_edits(edits.template_mutations); // websys_dom.apply_edits(edits.edits); // } - -// let mut work_loop = ric_raf::RafLoop::new();