diff --git a/packages/fermi/src/hooks/state.rs b/packages/fermi/src/hooks/state.rs index ab035f152..9db9a9fdf 100644 --- a/packages/fermi/src/hooks/state.rs +++ b/packages/fermi/src/hooks/state.rs @@ -1,12 +1,10 @@ -use crate::{use_atom_root, AtomId, AtomRoot, Readable, Writable}; +use crate::{AtomId, AtomRoot, Writable}; use dioxus_core::{ScopeId, ScopeState}; use std::{ - cell::{RefCell, RefMut}, + cell::RefMut, fmt::{Debug, Display}, - marker::PhantomData, ops::{Add, Div, Mul, Not, Sub}, rc::Rc, - sync::Arc, }; /// Store state between component renders. diff --git a/packages/html/src/elements.rs b/packages/html/src/elements.rs index c6b1cb7e0..ad4f66b7b 100644 --- a/packages/html/src/elements.rs +++ b/packages/html/src/elements.rs @@ -428,6 +428,11 @@ builder_constructors! { /// element. mark {}; + /// Build a + /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu) + /// element. + menu {}; + /// Build a /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q) /// element. diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 176e78d51..313892f45 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -30,5 +30,16 @@ dioxus-core = { path = "../core", features = ["serialize"] } # warp warp = { version = "0.3", optional = true } +# axum +axum = { version = "0.5.1", optional = true, features = ["ws"] } +tower = { version = "0.4.12", optional = true } + +[dev-dependencies] +tokio = { version = "1", features = ["full"] } +dioxus = { path = "../../" } +warp = "0.3" +axum = { version = "0.5.1", features = ["ws"] } +tower = "0.4.12" + [features] -default = [] +default = [] \ No newline at end of file diff --git a/packages/liveview/examples/axum.rs b/packages/liveview/examples/axum.rs new file mode 100644 index 000000000..3bcdee8c2 --- /dev/null +++ b/packages/liveview/examples/axum.rs @@ -0,0 +1,38 @@ +use axum::{ + extract::ws::WebSocketUpgrade, response::Html, response::IntoResponse, routing::get, Extension, + Router, +}; +use dioxus_core::{Element, LazyNodes, Scope}; +use dioxus_liveview::Liveview; + +#[tokio::main] +async fn main() { + #[cfg(feature = "axum")] + { + pretty_env_logger::init(); + + let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into(); + + let view = dioxus_liveview::new(addr); + let body = view.body("Dioxus Liveview"); + + let app = Router::new() + .route("/", get(move || async { Html(body) })) + .route( + "/app", + get(move |ws: WebSocketUpgrade| async move { + ws.on_upgrade(move |socket| async move { + view.upgrade(socket, app).await; + }) + }), + ); + axum::Server::bind(&addr.to_string().parse().unwrap()) + .serve(app.into_make_service()) + .await + .unwrap(); + } +} + +fn app(cx: Scope) -> Element { + cx.render(LazyNodes::new(|f| f.text(format_args!("hello world!")))) +} diff --git a/packages/liveview/examples/warp.rs b/packages/liveview/examples/warp.rs index e20f07964..2c14d318b 100644 --- a/packages/liveview/examples/warp.rs +++ b/packages/liveview/examples/warp.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "warp")] - use dioxus_core::{Element, LazyNodes, Scope}; use dioxus_liveview as liveview; use warp::ws::Ws; @@ -7,26 +5,28 @@ use warp::Filter; #[tokio::main] async fn main() { - pretty_env_logger::init(); + #[cfg(feature = "warp")] + { + pretty_env_logger::init(); - let addr = ([127, 0, 0, 1], 3030); + let addr = ([127, 0, 0, 1], 3030); - // todo: compactify this routing under one liveview::app method - let view = liveview::new(addr); - let body = view.body("Dioxus LiveView"); + // todo: compactify this routing under one liveview::app method + let view = liveview::new(addr); + let body = view.body("Dioxus LiveView"); - 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(socket, app).await; - }) - })); - - warp::serve(routes).run(addr).await; + 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(socket, app).await; + }) + })); + warp::serve(routes).run(addr).await; + } } fn app(cx: Scope) -> Element { diff --git a/packages/liveview/src/adapters/axum_adapter.rs b/packages/liveview/src/adapters/axum_adapter.rs index 8b1378917..d85b6da68 100644 --- a/packages/liveview/src/adapters/axum_adapter.rs +++ b/packages/liveview/src/adapters/axum_adapter.rs @@ -1 +1,77 @@ +use crate::{events, Liveview}; +use axum::extract::ws::{Message, WebSocket}; +use dioxus_core::prelude::*; +use futures_util::{ + future::{select, Either}, + pin_mut, SinkExt, StreamExt, +}; +use tokio::sync::mpsc; +use tokio_stream::wrappers::UnboundedReceiverStream; +use tokio_util::task::LocalPoolHandle; +#[cfg(feature = "axum")] +impl crate::Liveview { + pub async fn upgrade(&self, ws: WebSocket, app: fn(Scope) -> Element) { + connect(ws, self.pool.clone(), app).await; + } +} + +pub async fn connect(socket: WebSocket, pool: LocalPoolHandle, app: fn(Scope) -> Element) { + let (mut user_ws_tx, mut user_ws_rx) = socket.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.clone().spawn_pinned(move || async move { + let mut vdom = VirtualDom::new(app); + let edits = vdom.rebuild(); + let serialized = serde_json::to_string(&edits.edits).unwrap(); + edits_tx.send(serialized).unwrap(); + loop { + 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(); + } + } + } + }); + loop { + 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_text().map(events::parse_ipc_message) { + let user_event = events::trigger_from_serialized(msg.params); + event_tx.send(user_event).unwrap(); + } 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; + } + } else { + break; + } + } + } + } + vdom_fut.abort(); +} diff --git a/packages/liveview/src/adapters/warp_adapter.rs b/packages/liveview/src/adapters/warp_adapter.rs index 0c6f3e892..5bdcf671f 100644 --- a/packages/liveview/src/adapters/warp_adapter.rs +++ b/packages/liveview/src/adapters/warp_adapter.rs @@ -6,6 +6,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream; use tokio_util::task::LocalPoolHandle; use warp::ws::{Message, WebSocket}; +#[cfg(feature = "warp")] impl crate::Liveview { pub async fn upgrade(&self, ws: warp::ws::WebSocket, app: fn(Scope) -> Element) { connect(ws, self.pool.clone(), app).await;