Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Liveview Axum Integration + Example updates #366

Merged
merged 10 commits into from
Apr 24, 2022
5 changes: 5 additions & 0 deletions packages/html/src/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,11 @@ builder_constructors! {
/// element.
mark {};

/// Build a
/// [`<menu>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu)
/// element.
menu {};

/// Build a
/// [`<q>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q)
/// element.
Expand Down
13 changes: 12 additions & 1 deletion packages/liveview/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
38 changes: 38 additions & 0 deletions packages/liveview/examples/axum.rs
Original file line number Diff line number Diff line change
@@ -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("<title>Dioxus Liveview</title>");

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!"))))
}
38 changes: 19 additions & 19 deletions packages/liveview/examples/warp.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
#![cfg(feature = "warp")]

use dioxus_core::{Element, LazyNodes, Scope};
use dioxus_liveview as liveview;
use warp::ws::Ws;
use warp::Filter;

#[tokio::main]
async fn main() {
pretty_env_logger::init();

let addr = ([127, 0, 0, 1], 3030);
#[cfg(feature = "warp")]
{
pretty_env_logger::init();

// todo: compactify this routing under one liveview::app method
let view = liveview::new(addr);
let body = view.body("<title>Dioxus LiveView</title>");
let addr = ([127, 0, 0, 1], 3030);

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;
})
}));
// todo: compactify this routing under one liveview::app method
let view = liveview::new(addr);
let body = view.body("<title>Dioxus LiveView</title>");

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 {
Expand Down
76 changes: 76 additions & 0 deletions packages/liveview/src/adapters/axum_adapter.rs
Original file line number Diff line number Diff line change
@@ -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();
}
1 change: 1 addition & 0 deletions packages/liveview/src/adapters/warp_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down