From e256fe107978f2a2e96fcbe84bda928c00f87a47 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 14 Dec 2022 19:07:33 -0800 Subject: [PATCH] wip: add html event type --- packages/desktop/src/controller.rs | 17 +-- packages/desktop/src/events.rs | 60 +------- packages/desktop/src/lib.rs | 7 +- packages/html/Cargo.toml | 7 +- packages/html/src/events/drag.rs | 5 +- packages/html/src/events/form.rs | 5 +- packages/html/src/lib.rs | 6 + packages/html/src/transit.rs | 143 ++++++++++++++++++ packages/interpreter/src/interpreter.js | 6 +- packages/liveview/Cargo.toml | 5 +- packages/liveview/examples/salvo.rs | 2 +- packages/liveview/examples/warp.rs | 69 +++++---- .../liveview/src/adapters/warp_adapter.rs | 135 ++++++----------- packages/liveview/src/events.rs | 13 +- packages/liveview/src/interpreter.js | 3 +- packages/liveview/src/lib.rs | 56 +++---- packages/liveview/src/pool.rs | 46 ++++++ 17 files changed, 340 insertions(+), 245 deletions(-) create mode 100644 packages/html/src/transit.rs create mode 100644 packages/liveview/src/pool.rs diff --git a/packages/desktop/src/controller.rs b/packages/desktop/src/controller.rs index 9037189b1..c8015bc8a 100644 --- a/packages/desktop/src/controller.rs +++ b/packages/desktop/src/controller.rs @@ -1,6 +1,7 @@ use crate::desktop_context::{DesktopContext, UserWindowEvent}; -use crate::events::{decode_event, EventMessage}; +use crate::events::IpcMessage; use dioxus_core::*; +use dioxus_html::HtmlEvent; use futures_channel::mpsc::{unbounded, UnboundedSender}; use futures_util::StreamExt; #[cfg(target_os = "ios")] @@ -25,7 +26,7 @@ pub(super) struct DesktopController { pub(super) quit_app_on_close: bool, pub(super) is_ready: Arc, pub(super) proxy: EventLoopProxy, - pub(super) event_tx: UnboundedSender, + pub(super) event_tx: UnboundedSender, #[cfg(target_os = "ios")] pub(super) views: Vec<*mut Object>, @@ -40,7 +41,7 @@ impl DesktopController { proxy: EventLoopProxy, ) -> Self { let edit_queue = Arc::new(Mutex::new(Vec::new())); - let (event_tx, mut event_rx) = unbounded(); + let (event_tx, mut event_rx) = unbounded::(); let proxy2 = proxy.clone(); let pending_edits = edit_queue.clone(); @@ -68,14 +69,8 @@ impl DesktopController { loop { tokio::select! { _ = dom.wait_for_work() => {} - Some(json_value) = event_rx.next() => { - if let Ok(value) = serde_json::from_value::(json_value) { - let name = value.event.clone(); - let el_id = ElementId(value.mounted_dom_id); - if let Some(evt) = decode_event(value) { - dom.handle_event(&name, evt, el_id, dioxus_html::events::event_bubbles(&name)); - } - } + Some(value) = event_rx.next() => { + dom.handle_event(&value.name, value.data.into_any(), value.element, dioxus_html::events::event_bubbles(&value.name)); } } diff --git a/packages/desktop/src/events.rs b/packages/desktop/src/events.rs index b82d6f749..2496d6ddb 100644 --- a/packages/desktop/src/events.rs +++ b/packages/desktop/src/events.rs @@ -6,7 +6,7 @@ use serde_json::from_value; use std::any::Any; use std::rc::Rc; -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Debug)] pub(crate) struct IpcMessage { method: String, params: serde_json::Value, @@ -31,61 +31,3 @@ pub(crate) fn parse_ipc_message(payload: &str) -> Option { } } } - -macro_rules! match_data { - ( - $m:ident; - $name:ident; - $( - $tip:ty => $($mname:literal)|* ; - )* - ) => { - match $name { - $( $($mname)|* => { - let val: $tip = from_value::<$tip>($m).ok()?; - Rc::new(val) as Rc - })* - _ => return None, - } - }; -} - -#[derive(Deserialize)] -pub struct EventMessage { - pub contents: serde_json::Value, - pub event: String, - pub mounted_dom_id: usize, -} - -pub fn decode_event(value: EventMessage) -> Option> { - let val = value.contents; - let name = value.event.as_str(); - type DragData = MouseData; - - let evt = match_data! { val; name; - MouseData => "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup"; - ClipboardData => "copy" | "cut" | "paste"; - CompositionData => "compositionend" | "compositionstart" | "compositionupdate"; - KeyboardData => "keydown" | "keypress" | "keyup"; - FocusData => "blur" | "focus" | "focusin" | "focusout"; - FormData => "change" | "input" | "invalid" | "reset" | "submit"; - DragData => "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" | "drop"; - PointerData => "pointerlockchange" | "pointerlockerror" | "pointerdown" | "pointermove" | "pointerup" | "pointerover" | "pointerout" | "pointerenter" | "pointerleave" | "gotpointercapture" | "lostpointercapture"; - SelectionData => "selectstart" | "selectionchange" | "select"; - TouchData => "touchcancel" | "touchend" | "touchmove" | "touchstart"; - ScrollData => "scroll"; - WheelData => "wheel"; - MediaData => "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" - | "encrypted" | "ended" | "interruptbegin" | "interruptend" | "loadeddata" - | "loadedmetadata" | "loadstart" | "pause" | "play" | "playing" | "progress" - | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend" | "timeupdate" - | "volumechange" | "waiting" | "error" | "load" | "loadend" | "timeout"; - AnimationData => "animationstart" | "animationend" | "animationiteration"; - TransitionData => "transitionend"; - ToggleData => "toggle"; - // ImageData => "load" | "error"; - // OtherData => "abort" | "afterprint" | "beforeprint" | "beforeunload" | "hashchange" | "languagechange" | "message" | "offline" | "online" | "pagehide" | "pageshow" | "popstate" | "rejectionhandled" | "storage" | "unhandledrejection" | "unload" | "userproximity" | "vrdisplayactivate" | "vrdisplayblur" | "vrdisplayconnect" | "vrdisplaydeactivate" | "vrdisplaydisconnect" | "vrdisplayfocus" | "vrdisplaypointerrestricted" | "vrdisplaypointerunrestricted" | "vrdisplaypresentchange"; - }; - - Some(evt) -} diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index a635e3f4e..d8a0f8746 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -18,6 +18,7 @@ use std::sync::Arc; use desktop_context::UserWindowEvent; pub use desktop_context::{use_eval, use_window, DesktopContext, EvalResult}; +use dioxus_html::HtmlEvent; use futures_channel::mpsc::UnboundedSender; pub use wry; pub use wry::application as tao; @@ -156,7 +157,7 @@ fn build_webview( is_ready: Arc, proxy: tao::event_loop::EventLoopProxy, eval_sender: tokio::sync::mpsc::UnboundedSender, - event_tx: UnboundedSender, + event_tx: UnboundedSender, ) -> wry::webview::WebView { let builder = cfg.window.clone(); let window = builder.build(event_loop).unwrap(); @@ -190,7 +191,9 @@ fn build_webview( eval_sender.send(result).unwrap(); } "user_event" => { - _ = event_tx.unbounded_send(message.params()); + if let Ok(evt) = serde_json::from_value(message.params()) { + _ = event_tx.unbounded_send(evt); + } } "initialize" => { is_ready.store(true, std::sync::atomic::Ordering::Relaxed); diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 446da9303..434c0bf48 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -39,7 +39,10 @@ features = [ "ClipboardEvent", ] +[dev-dependencies] +serde_json = "*" + [features] -default = [] -serialize = ["serde", "serde_repr", "euclid/serde", "keyboard-types/serde"] +default = ["serialize"] +serialize = ["serde", "serde_repr", "euclid/serde", "keyboard-types/serde", "dioxus-core/serialize"] wasm-bind = ["web-sys", "wasm-bindgen"] diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index 5dd7f5c71..53c118a77 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -10,12 +10,11 @@ pub type DragEvent = Event; /// placing a pointer device (such as a mouse) on the touch surface and then dragging the pointer to a new location /// (such as another DOM element). Applications are free to interpret a drag and drop interaction in an /// application-specific way. +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone)] pub struct DragData { /// Inherit mouse data pub mouse: MouseData, - - /// And then add the rest of the drag data - pub data: Box, } impl_event! { diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index 5e0b6a17e..1d995fa09 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -11,9 +11,8 @@ pub struct FormData { pub value: String, pub values: HashMap, - - #[cfg_attr(feature = "serialize", serde(skip))] - pub files: Option>, + // #[cfg_attr(feature = "serialize", serde(skip))] + // pub files: Option>, } impl Debug for FormData { diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index ab2e49f1e..577899fe1 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -22,6 +22,12 @@ mod render_template; #[cfg(feature = "wasm-bind")] mod web_sys_bind; +#[cfg(feature = "serialize")] +mod transit; + +#[cfg(feature = "serialize")] +pub use transit::*; + pub use elements::*; pub use events::*; pub use global_attributes::*; diff --git a/packages/html/src/transit.rs b/packages/html/src/transit.rs new file mode 100644 index 000000000..803869f31 --- /dev/null +++ b/packages/html/src/transit.rs @@ -0,0 +1,143 @@ +use std::{any::Any, rc::Rc}; + +use crate::events::*; +use dioxus_core::ElementId; +use serde::{Deserialize, Serialize}; + +// macro_rules! match_data { +// ( +// $m:ident; +// $name:ident; +// $( +// $tip:ty => $($mname:literal)|* ; +// )* +// ) => { +// match $name { +// $( $($mname)|* => { +// let val: $tip = from_value::<$tip>($m).ok()?; +// Rc::new(val) as Rc +// })* +// _ => return None, +// } +// }; +// } + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct HtmlEvent { + pub element: ElementId, + pub name: String, + pub data: EventData, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(untagged)] +pub enum EventData { + Mouse(MouseData), + Clipboard(ClipboardData), + Composition(CompositionData), + Keyboard(KeyboardData), + Focus(FocusData), + Form(FormData), + Drag(DragData), + Pointer(PointerData), + Selection(SelectionData), + Touch(TouchData), + Scroll(ScrollData), + Wheel(WheelData), + Media(MediaData), + Animation(AnimationData), + Transition(TransitionData), + Toggle(ToggleData), +} + +impl EventData { + pub fn into_any(self) -> Rc { + match self { + EventData::Mouse(data) => Rc::new(data) as Rc, + EventData::Clipboard(data) => Rc::new(data) as Rc, + EventData::Composition(data) => Rc::new(data) as Rc, + EventData::Keyboard(data) => Rc::new(data) as Rc, + EventData::Focus(data) => Rc::new(data) as Rc, + EventData::Form(data) => Rc::new(data) as Rc, + EventData::Drag(data) => Rc::new(data) as Rc, + EventData::Pointer(data) => Rc::new(data) as Rc, + EventData::Selection(data) => Rc::new(data) as Rc, + EventData::Touch(data) => Rc::new(data) as Rc, + EventData::Scroll(data) => Rc::new(data) as Rc, + EventData::Wheel(data) => Rc::new(data) as Rc, + EventData::Media(data) => Rc::new(data) as Rc, + EventData::Animation(data) => Rc::new(data) as Rc, + EventData::Transition(data) => Rc::new(data) as Rc, + EventData::Toggle(data) => Rc::new(data) as Rc, + } + } +} + +#[test] +fn test_back_and_forth() { + let data = HtmlEvent { + element: ElementId(0), + data: EventData::Mouse(MouseData::default()), + name: "click".to_string(), + }; + + println!("{}", serde_json::to_string_pretty(&data).unwrap()); + + let o = r#" +{ + "element": 0, + "name": "click", + "data": { + "alt_key": false, + "button": 0, + "buttons": 0, + "client_x": 0, + "client_y": 0, + "ctrl_key": false, + "meta_key": false, + "offset_x": 0, + "offset_y": 0, + "page_x": 0, + "page_y": 0, + "screen_x": 0, + "screen_y": 0, + "shift_key": false + } +} + "#; + + let p: HtmlEvent = serde_json::from_str(o).unwrap(); +} + +// pub fn decode_event(value: ) -> Option> { +// let val = value.data; +// let name = value.event.as_str(); +// type DragData = MouseData; + +// let evt = match_data! { val; name; +// MouseData => "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup"; +// ClipboardData => "copy" | "cut" | "paste"; +// CompositionData => "compositionend" | "compositionstart" | "compositionupdate"; +// KeyboardData => "keydown" | "keypress" | "keyup"; +// FocusData => "blur" | "focus" | "focusin" | "focusout"; +// FormData => "change" | "input" | "invalid" | "reset" | "submit"; +// DragData => "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" | "drop"; +// PointerData => "pointerlockchange" | "pointerlockerror" | "pointerdown" | "pointermove" | "pointerup" | "pointerover" | "pointerout" | "pointerenter" | "pointerleave" | "gotpointercapture" | "lostpointercapture"; +// SelectionData => "selectstart" | "selectionchange" | "select"; +// TouchData => "touchcancel" | "touchend" | "touchmove" | "touchstart"; +// ScrollData => "scroll"; +// WheelData => "wheel"; +// MediaData => "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" +// | "encrypted" | "ended" | "interruptbegin" | "interruptend" | "loadeddata" +// | "loadedmetadata" | "loadstart" | "pause" | "play" | "playing" | "progress" +// | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend" | "timeupdate" +// | "volumechange" | "waiting" | "error" | "load" | "loadend" | "timeout"; +// AnimationData => "animationstart" | "animationend" | "animationiteration"; +// TransitionData => "transitionend"; +// ToggleData => "toggle"; +// // ImageData => "load" | "error"; +// // OtherData => "abort" | "afterprint" | "beforeprint" | "beforeunload" | "hashchange" | "languagechange" | "message" | "offline" | "online" | "pagehide" | "pageshow" | "popstate" | "rejectionhandled" | "storage" | "unhandledrejection" | "unload" | "userproximity" | "vrdisplayactivate" | "vrdisplayblur" | "vrdisplayconnect" | "vrdisplaydeactivate" | "vrdisplaydisconnect" | "vrdisplayfocus" | "vrdisplaypointerrestricted" | "vrdisplaypointerunrestricted" | "vrdisplaypresentchange"; +// }; + +// Some(evt) +// } diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index 3ac52a8f1..8e1496eb6 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -435,9 +435,9 @@ export class Interpreter { } window.ipc.postMessage( serializeIpcMessage("user_event", { - event: edit.name, - mounted_dom_id: parseInt(realId), - contents: contents, + name: edit.name, + element: parseInt(realId), + data: contents, }) ); } diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 90f956289..d01660373 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -39,6 +39,9 @@ tower = { version = "0.4.12", optional = true } # salvo salvo = { version = "0.32.0", optional = true, features = ["ws"] } +thiserror = "1.0.37" +uuid = { version = "1.2.2", features = ["v4"] } +anyhow = "1.0.66" [dev-dependencies] tokio = { version = "1", features = ["full"] } @@ -49,4 +52,4 @@ salvo = { version = "0.32.0", features = ["affix", "ws"] } tower = "0.4.12" [features] -default = [] \ No newline at end of file +default = ["warp"] diff --git a/packages/liveview/examples/salvo.rs b/packages/liveview/examples/salvo.rs index 132a62633..5b7e271c6 100644 --- a/packages/liveview/examples/salvo.rs +++ b/packages/liveview/examples/salvo.rs @@ -8,7 +8,7 @@ async fn main() { use dioxus_core::{Element, LazyNodes, Scope}; use dioxus_liveview as liveview; - use dioxus_liveview::Liveview; + use dioxus_liveview::LiveView; use salvo::extra::affix; use salvo::extra::ws::WsHandler; use salvo::prelude::*; diff --git a/packages/liveview/examples/warp.rs b/packages/liveview/examples/warp.rs index 7b2ddfef5..d19914446 100644 --- a/packages/liveview/examples/warp.rs +++ b/packages/liveview/examples/warp.rs @@ -1,35 +1,50 @@ -#[cfg(not(feature = "warp"))] -fn main() {} +use dioxus::prelude::*; +use dioxus_liveview::LiveView; +use std::net::SocketAddr; +use warp::ws::Ws; +use warp::Filter; + +fn app(cx: Scope) -> Element { + let mut num = use_state(cx, || 0); + + cx.render(rsx! { + div { + "hello world! {num}" + button { + onclick: move |_| num += 1, + "Increment" + } + } + }) +} -#[cfg(feature = "warp")] #[tokio::main] async fn main() { - use dioxus_core::{Element, LazyNodes, Scope}; - use dioxus_liveview as liveview; - use warp::ws::Ws; - use warp::Filter; - - fn app(cx: Scope) -> Element { - cx.render(LazyNodes::new(|f| f.text(format_args!("hello world!")))) - } - pretty_env_logger::init(); - let addr = ([127, 0, 0, 1], 3030); + let addr: SocketAddr = ([127, 0, 0, 1], 3030).into(); - // todo: compactify this routing under one liveview::app method - let view = liveview::new(addr); - let body = view.body("Dioxus LiveView"); + let index = warp::path::end().map(move || { + warp::reply::html(format!( + r#" + + + Dioxus LiveView with Warp +
{glue} + + "#, + glue = dioxus_liveview::interpreter_glue(&format!("ws://{addr}/ws/app")) + )) + }); - let routes = warp::path::end() - .map(move || warp::reply::html(body.clone())) - .or(warp::path("app") - .and(warp::ws()) - .and(warp::any().map(move || view.clone())) - .map(|ws: Ws, view: liveview::Liveview| { - ws.on_upgrade(|socket| async move { - view.upgrade_warp(socket, app).await; - }) - })); - warp::serve(routes).run(addr).await; + let view = LiveView::new(addr); + + let ws = warp::path("ws") + .and(warp::ws()) + .and(warp::any().map(move || view.clone())) + .map(move |ws: Ws, view: LiveView| ws.on_upgrade(|ws| view.upgrade_warp(ws, app))); + + println!("Listening on http://{}", addr); + + warp::serve(index.or(ws)).run(addr).await; } diff --git a/packages/liveview/src/adapters/warp_adapter.rs b/packages/liveview/src/adapters/warp_adapter.rs index 398c7cdd9..39a167359 100644 --- a/packages/liveview/src/adapters/warp_adapter.rs +++ b/packages/liveview/src/adapters/warp_adapter.rs @@ -1,110 +1,75 @@ -use crate::events; +use std::{convert::Infallible, time::Duration}; + +use crate::{ + events::{self, IpcMessage}, + LiveView, LiveViewError, +}; use dioxus_core::prelude::*; +use dioxus_html::a; use futures_util::{pin_mut, SinkExt, StreamExt}; use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; use tokio_util::task::LocalPoolHandle; use warp::ws::{Message, WebSocket}; -impl crate::Liveview { - pub async fn upgrade_warp(&self, ws: warp::ws::WebSocket, app: fn(Scope) -> Element) { - connect(ws, self.pool.clone(), app, ()).await; - } - pub async fn upgrade_warp_with_props( - &self, - ws: warp::ws::WebSocket, +impl LiveView { + pub async fn upgrade_warp(self, ws: WebSocket, app: fn(Scope<()>) -> Element) {} + + pub async fn upgrade_warp_with_props( + self, + ws: WebSocket, app: fn(Scope) -> Element, props: T, - ) where - T: Send + Sync + 'static, - { - connect(ws, self.pool.clone(), app, props).await; + ) { + self.pool + .spawn_pinned(move || liveview_eventloop(app, props, ws)) + .await; } } -pub async fn connect( - ws: WebSocket, - pool: LocalPoolHandle, - app: fn(Scope) -> Element, +async fn liveview_eventloop( + app: Component, props: T, -) where - T: Send + Sync + 'static, + mut ws: WebSocket, +) -> Result<(), LiveViewError> +where + T: Send + 'static, { - // Use a counter to assign a new unique ID for this user. - - // Split the socket into a sender and receive of messages. - let (mut user_ws_tx, mut user_ws_rx) = ws.split(); - - let (event_tx, event_rx) = mpsc::unbounded_channel(); - let (edits_tx, edits_rx) = mpsc::unbounded_channel(); - - let mut edits_rx = UnboundedReceiverStream::new(edits_rx); - let mut event_rx = UnboundedReceiverStream::new(event_rx); - - let vdom_fut = pool.spawn_pinned(move || async move { - let mut vdom = VirtualDom::new_with_props(app, props); - - let edits = vdom.rebuild(); - - let serialized = serde_json::to_string(&edits.edits).unwrap(); - edits_tx.send(serialized).unwrap(); - - loop { - use futures_util::future::{select, Either}; - - let new_event = { - let vdom_fut = vdom.wait_for_work(); - - pin_mut!(vdom_fut); - - match select(event_rx.next(), vdom_fut).await { - Either::Left((l, _)) => l, - Either::Right((_, _)) => None, - } - }; - - if let Some(new_event) = new_event { - vdom.handle_message(dioxus_core::SchedulerMsg::Event(new_event)); - } else { - let mutations = vdom.work_with_deadline(|| false); - for mutation in mutations { - let edits = serde_json::to_string(&mutation.edits).unwrap(); - edits_tx.send(edits).unwrap(); - } - } - } - }); + let mut vdom = VirtualDom::new_with_props(app, props); + let edits = serde_json::to_string(&vdom.rebuild()).unwrap(); + ws.send(Message::text(edits)).await.unwrap(); loop { - use futures_util::future::{select, Either}; + tokio::select! { + // poll any futures or suspense + _ = vdom.wait_for_work() => {} - match select(user_ws_rx.next(), edits_rx.next()).await { - Either::Left((l, _)) => { - if let Some(Ok(msg)) = l { - if let Ok(Some(msg)) = msg.to_str().map(events::parse_ipc_message) { - if msg.method == "user_event" { - let user_event = events::trigger_from_serialized(msg.params); - event_tx.send(user_event).unwrap(); + evt = ws.next() => { + match evt { + Some(Ok(evt)) => { + if let Ok(evt) = evt.to_str() { + let IpcMessage { name, element, bubbles, data } = serde_json::from_str(evt).unwrap(); + + vdom.handle_event(&name, data, element, bubbles); } - } else { - break; } - } else { - break; - } - } - Either::Right((edits, _)) => { - if let Some(edits) = edits { - // send the edits to the client - if user_ws_tx.send(Message::text(edits)).await.is_err() { - break; + Some(Err(e)) => { + // log this I guess? + // when would we get an error here? } - } else { - break; + None => break, } + } } - } - vdom_fut.abort(); + let edits = vdom + .render_with_deadline(tokio::time::sleep(Duration::from_millis(10))) + .await; + + ws.send(Message::text(serde_json::to_string(&edits).unwrap())) + .await + .unwrap(); + } + Ok(()) as Result<(), LiveViewError> } diff --git a/packages/liveview/src/events.rs b/packages/liveview/src/events.rs index 2c5f97cc7..a8bf4d6ca 100644 --- a/packages/liveview/src/events.rs +++ b/packages/liveview/src/events.rs @@ -11,15 +11,10 @@ use dioxus_html::events::*; #[derive(serde::Serialize, serde::Deserialize)] pub(crate) struct IpcMessage { - pub method: String, - pub params: serde_json::Value, -} - -pub(crate) fn parse_ipc_message(payload: &str) -> Option { - match serde_json::from_str(payload) { - Ok(message) => Some(message), - Err(_) => None, - } + pub name: String, + pub element: ElementId, + pub bubbles: bool, + pub data: serde_json::Value, } #[derive(serde::Serialize, serde::Deserialize)] diff --git a/packages/liveview/src/interpreter.js b/packages/liveview/src/interpreter.js index 7e99745f2..ee82a61e1 100644 --- a/packages/liveview/src/interpreter.js +++ b/packages/liveview/src/interpreter.js @@ -4,7 +4,6 @@ function main() { if (root != null) { // create a new ipc window.ipc = new IPC(root); - window.ipc.send(serializeIpcMessage("initialize")); } } @@ -970,4 +969,4 @@ function event_bubbles(event) { case "toggle": return true; } -} \ No newline at end of file +} diff --git a/packages/liveview/src/lib.rs b/packages/liveview/src/lib.rs index 540d29974..01a84c245 100644 --- a/packages/liveview/src/lib.rs +++ b/packages/liveview/src/lib.rs @@ -1,6 +1,21 @@ #![allow(dead_code)] +pub static INTERPRETER: &str = include_str!("interpreter.js"); + +pub fn interpreter_glue(url: &str) -> String { + format!( + r#" + + "# + ) +} + pub(crate) mod events; + pub mod adapters { #[cfg(feature = "warp")] pub mod warp_adapter; @@ -16,41 +31,8 @@ use std::net::SocketAddr; use tokio_util::task::LocalPoolHandle; -#[derive(Clone)] -pub struct Liveview { - pool: LocalPoolHandle, - addr: String, -} +pub mod pool; +pub use pool::*; -impl Liveview { - pub fn body(&self, header: &str) -> String { - format!( - r#" - - - - {header} - - -
- - -"#, - addr = self.addr, - interpreter = include_str!("../src/interpreter.js") - ) - } -} - -pub fn new(addr: impl Into) -> Liveview { - let addr: SocketAddr = addr.into(); - - Liveview { - pool: LocalPoolHandle::new(16), - addr: addr.to_string(), - } -} +#[derive(Debug, Clone, PartialEq, thiserror::Error)] +pub enum LiveViewError {} diff --git a/packages/liveview/src/pool.rs b/packages/liveview/src/pool.rs new file mode 100644 index 000000000..c743df05c --- /dev/null +++ b/packages/liveview/src/pool.rs @@ -0,0 +1,46 @@ +use std::net::SocketAddr; + +use tokio_util::task::LocalPoolHandle; + +#[derive(Clone)] +pub struct LiveView { + pub(crate) pool: LocalPoolHandle, + pub(crate) addr: String, +} + +impl LiveView { + pub fn body(&self, header: &str) -> String { + format!( + r#" + + + + {header} + + +
+ + +"#, + addr = self.addr, + interpreter = include_str!("../src/interpreter.js") + ) + } + + pub fn interpreter_code(&self) -> String { + include_str!("../src/interpreter.js").to_string() + } + + pub fn new(addr: impl Into) -> Self { + let addr: SocketAddr = addr.into(); + + LiveView { + pool: LocalPoolHandle::new(16), + addr: addr.to_string(), + } + } +}