Parcourir la source

chore: clean up web impl

Jonathan Kelley il y a 2 ans
Parent
commit
ba26b1001a
3 fichiers modifiés avec 60 ajouts et 68 suppressions
  1. 0 7
      packages/web/src/dom.rs
  2. 24 9
      packages/web/src/hot_reload.rs
  3. 36 52
      packages/web/src/lib.rs

+ 0 - 7
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<dyn Any> {

+ 24 - 9
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<String> {
+    let (tx, rx) = futures_channel::mpsc::unbounded();
+
+    std::mem::forget(tx);
+
+    rx
+}
+
+#[cfg(debug_assertions)]
+pub(crate) fn init() -> UnboundedReceiver<String> {
+    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::<js_sys::JsString>() {
-            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<dyn FnMut(MessageEvent)>);
 
     ws.set_onmessage(Some(cl.as_ref().unchecked_ref()));
     cl.forget();
+
+    rx
 }

+ 36 - 52
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<T: 'static>(root: fn(Scope<T>) -> 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<T: 'static>(root: fn(Scope<T>) -> 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<T: 'static>(root: fn(Scope<T>) -> 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<T: 'static>(root: fn(Scope<T>) -> 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::<Element>()
+        .dyn_into::<web_sys::Element>()
         .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();