integrate liveview with the launch builder
This commit is contained in:
1 parent
50873bf9c7
commit
636aeb4b90
20 files changed
+443
-97
No files matched your search
Generated
+1
-1
@@ -2323,7 +2323,6 @@ dependencies = [
|
|||||||
"dioxus-liveview",
|
"dioxus-liveview",
|
||||||
"dioxus-mobile",
|
"dioxus-mobile",
|
||||||
"dioxus-router",
|
"dioxus-router",
|
||||||
"dioxus-rsx",
|
|
||||||
"dioxus-signals",
|
"dioxus-signals",
|
||||||
"dioxus-ssr",
|
"dioxus-ssr",
|
||||||
"dioxus-tui",
|
"dioxus-tui",
|
||||||
@@ -2693,6 +2692,7 @@ dependencies = [
|
|||||||
"async-trait",
|
"async-trait",
|
||||||
"axum 0.6.20",
|
"axum 0.6.20",
|
||||||
"dioxus",
|
"dioxus",
|
||||||
|
"dioxus-cli-config",
|
||||||
"dioxus-core",
|
"dioxus-core",
|
||||||
"dioxus-hot-reload",
|
"dioxus-hot-reload",
|
||||||
"dioxus-html",
|
"dioxus-html",
|
||||||
|
|||||||
@@ -145,6 +145,12 @@ tokio = { version = "1.16.1", features = ["full"] }
|
|||||||
# To make most examples faster to compile, we split out assets and http-related stuff
|
# To make most examples faster to compile, we split out assets and http-related stuff
|
||||||
# This trims off like 270 dependencies, leading to a significant speedup in compilation time
|
# This trims off like 270 dependencies, leading to a significant speedup in compilation time
|
||||||
[features]
|
[features]
|
||||||
|
liveview = ["dioxus/liveview"]
|
||||||
|
fullstack = ["dioxus/fullstack"]
|
||||||
|
axum = ["dioxus/axum"]
|
||||||
|
salvo = ["dioxus/salvo"]
|
||||||
|
rocket = ["dioxus/rocket"]
|
||||||
|
server = ["dioxus/server"]
|
||||||
default = ["dioxus/desktop"]
|
default = ["dioxus/desktop"]
|
||||||
web = ["dioxus/web"]
|
web = ["dioxus/web"]
|
||||||
collect-assets = ["manganis"]
|
collect-assets = ["manganis"]
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ pub struct ConfigOptsBuild {
|
|||||||
#[clap(long, default_value_t = { "web".to_string() })]
|
#[clap(long, default_value_t = { "web".to_string() })]
|
||||||
pub client_feature: String,
|
pub client_feature: String,
|
||||||
|
|
||||||
/// The feature to use for the server in a fullstack app [default: "ssr"]
|
/// The feature to use for the server in a fullstack app [default: "server"]
|
||||||
#[clap(long, default_value_t = { "ssr".to_string() })]
|
#[clap(long, default_value_t = { "server".to_string() })]
|
||||||
pub server_feature: String,
|
pub server_feature: String,
|
||||||
|
|
||||||
/// Rustc platform triple
|
/// Rustc platform triple
|
||||||
@@ -140,8 +140,8 @@ pub struct ConfigOptsServe {
|
|||||||
#[clap(long, default_value_t = { "web".to_string() })]
|
#[clap(long, default_value_t = { "web".to_string() })]
|
||||||
pub client_feature: String,
|
pub client_feature: String,
|
||||||
|
|
||||||
/// The feature to use for the server in a fullstack app [default: "ssr"]
|
/// The feature to use for the server in a fullstack app [default: "server"]
|
||||||
#[clap(long, default_value_t = { "ssr".to_string() })]
|
#[clap(long, default_value_t = { "server".to_string() })]
|
||||||
pub server_feature: String,
|
pub server_feature: String,
|
||||||
|
|
||||||
/// Rustc platform triple
|
/// Rustc platform triple
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ dioxus-html = { workspace = true, optional = true }
|
|||||||
dioxus-core-macro = { workspace = true, optional = true }
|
dioxus-core-macro = { workspace = true, optional = true }
|
||||||
dioxus-config-macro = { workspace = true, optional = true }
|
dioxus-config-macro = { workspace = true, optional = true }
|
||||||
dioxus-hooks = { workspace = true, optional = true }
|
dioxus-hooks = { workspace = true, optional = true }
|
||||||
dioxus-rsx = { workspace = true, optional = true }
|
|
||||||
dioxus-signals = { workspace = true, optional = true }
|
dioxus-signals = { workspace = true, optional = true }
|
||||||
dioxus-router = { workspace = true, optional = true }
|
dioxus-router = { workspace = true, optional = true }
|
||||||
dioxus-web = { workspace = true, optional = true }
|
dioxus-web = { workspace = true, optional = true }
|
||||||
@@ -35,7 +34,7 @@ dioxus-hot-reload = { workspace = true, optional = true }
|
|||||||
[features]
|
[features]
|
||||||
default = ["macro", "html", "hot-reload", "signals", "hooks", "launch"]
|
default = ["macro", "html", "hot-reload", "signals", "hooks", "launch"]
|
||||||
signals = ["dioxus-signals"]
|
signals = ["dioxus-signals"]
|
||||||
macro = ["dioxus-core-macro", "dioxus-rsx"]
|
macro = ["dioxus-core-macro"]
|
||||||
html = ["dioxus-html"]
|
html = ["dioxus-html"]
|
||||||
hooks = ["dioxus-hooks"]
|
hooks = ["dioxus-hooks"]
|
||||||
hot-reload = ["dioxus-hot-reload"]
|
hot-reload = ["dioxus-hot-reload"]
|
||||||
@@ -51,9 +50,10 @@ web = ["dioxus-web", "dioxus-fullstack?/web", "dioxus-config-macro/web", "dioxus
|
|||||||
server = ["dioxus-fullstack?/ssr", "dioxus-config-macro/ssr", "dioxus-router?/ssr"]
|
server = ["dioxus-fullstack?/ssr", "dioxus-config-macro/ssr", "dioxus-router?/ssr"]
|
||||||
ssr = ["dioxus-ssr"]
|
ssr = ["dioxus-ssr"]
|
||||||
liveview = ["dioxus-liveview", "dioxus-config-macro/liveview", "dioxus-router?/liveview"]
|
liveview = ["dioxus-liveview", "dioxus-config-macro/liveview", "dioxus-router?/liveview"]
|
||||||
axum = ["dioxus-fullstack?/axum"]
|
axum = ["dioxus-fullstack?/axum", "dioxus-liveview?/axum"]
|
||||||
salvo = ["dioxus-fullstack?/salvo"]
|
salvo = ["dioxus-fullstack?/salvo", "dioxus-liveview?/salvo"]
|
||||||
warp = ["dioxus-fullstack?/warp"]
|
warp = ["dioxus-fullstack?/warp", "dioxus-liveview?/warp"]
|
||||||
|
rocket = ["dioxus-liveview?/rocket"]
|
||||||
tui = ["dioxus-tui", "dioxus-config-macro/tui"]
|
tui = ["dioxus-tui", "dioxus-config-macro/tui"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ pub struct LaunchBuilder<Cfg: 'static = (), ContextFn: ?Sized = ValidContext> {
|
|||||||
|
|
||||||
pub type LaunchFn<Cfg, Context> = fn(fn() -> Element, Vec<Box<Context>>, Cfg);
|
pub type LaunchFn<Cfg, Context> = fn(fn() -> Element, Vec<Box<Context>>, Cfg);
|
||||||
|
|
||||||
#[cfg(feature = "fullstack")]
|
#[cfg(any(feature = "fullstack", feature = "liveview"))]
|
||||||
type ValidContext = SendContext;
|
type ValidContext = SendContext;
|
||||||
|
|
||||||
#[cfg(not(feature = "fullstack"))]
|
#[cfg(not(any(feature = "fullstack", feature = "liveview")))]
|
||||||
type ValidContext = UnsendContext;
|
type ValidContext = UnsendContext;
|
||||||
|
|
||||||
type SendContext = dyn Fn() -> Box<dyn Any> + Send + Sync + 'static;
|
type SendContext = dyn Fn() -> Box<dyn Any> + Send + Sync + 'static;
|
||||||
@@ -154,12 +154,24 @@ mod current_platform {
|
|||||||
pub use dioxus_web::launch::*;
|
pub use dioxus_web::launch::*;
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
feature = "tui",
|
feature = "liveview",
|
||||||
not(any(feature = "web", feature = "desktop", feature = "fullstack"))
|
not(any(feature = "web", feature = "desktop", feature = "fullstack"))
|
||||||
))]
|
))]
|
||||||
|
pub use dioxus_liveview::launch::*;
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "tui",
|
||||||
|
not(any(
|
||||||
|
feature = "liveview",
|
||||||
|
feature = "web",
|
||||||
|
feature = "desktop",
|
||||||
|
feature = "fullstack"
|
||||||
|
))
|
||||||
|
))]
|
||||||
pub use dioxus_tui::launch::*;
|
pub use dioxus_tui::launch::*;
|
||||||
|
|
||||||
#[cfg(not(any(
|
#[cfg(not(any(
|
||||||
|
feature = "liveview",
|
||||||
feature = "desktop",
|
feature = "desktop",
|
||||||
feature = "web",
|
feature = "web",
|
||||||
feature = "tui",
|
feature = "tui",
|
||||||
@@ -168,6 +180,7 @@ mod current_platform {
|
|||||||
pub type Config = ();
|
pub type Config = ();
|
||||||
|
|
||||||
#[cfg(not(any(
|
#[cfg(not(any(
|
||||||
|
feature = "liveview",
|
||||||
feature = "desktop",
|
feature = "desktop",
|
||||||
feature = "web",
|
feature = "web",
|
||||||
feature = "tui",
|
feature = "tui",
|
||||||
@@ -176,7 +189,7 @@ mod current_platform {
|
|||||||
pub fn launch(
|
pub fn launch(
|
||||||
root: fn() -> dioxus_core::Element,
|
root: fn() -> dioxus_core::Element,
|
||||||
contexts: Vec<Box<super::ValidContext>>,
|
contexts: Vec<Box<super::ValidContext>>,
|
||||||
platform_config: Config,
|
platform_config: (),
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ pub mod events {
|
|||||||
#[cfg(feature = "html")]
|
#[cfg(feature = "html")]
|
||||||
pub use dioxus_html as html;
|
pub use dioxus_html as html;
|
||||||
|
|
||||||
#[cfg(feature = "macro")]
|
|
||||||
pub use dioxus_rsx as rsx;
|
|
||||||
|
|
||||||
#[cfg(feature = "macro")]
|
#[cfg(feature = "macro")]
|
||||||
pub use dioxus_core_macro as core_macro;
|
pub use dioxus_core_macro as core_macro;
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ rustc-hash = { workspace = true }
|
|||||||
dioxus-core = { workspace = true, features = ["serialize"] }
|
dioxus-core = { workspace = true, features = ["serialize"] }
|
||||||
dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] }
|
dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] }
|
||||||
dioxus-hot-reload = { workspace = true, optional = true }
|
dioxus-hot-reload = { workspace = true, optional = true }
|
||||||
|
dioxus-cli-config = { workspace = true }
|
||||||
|
|
||||||
# warp
|
# warp
|
||||||
warp = { version = "0.3.3", optional = true }
|
warp = { version = "0.3.3", optional = true }
|
||||||
@@ -35,7 +36,7 @@ warp = { version = "0.3.3", optional = true }
|
|||||||
axum = { version = "0.6.1", optional = true, features = ["ws"] }
|
axum = { version = "0.6.1", optional = true, features = ["ws"] }
|
||||||
|
|
||||||
# salvo
|
# salvo
|
||||||
salvo = { version = "0.63.0", optional = true, features = ["websocket"] }
|
salvo = { version = "0.63.0", optional = true, features = ["websocket", "affix"] }
|
||||||
once_cell = "1.17.1"
|
once_cell = "1.17.1"
|
||||||
async-trait = "0.1.71"
|
async-trait = "0.1.71"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use axum::{extract::ws::WebSocketUpgrade, response::Html, routing::get, Router};
|
use axum::Router;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_liveview::LiveviewRouter;
|
||||||
|
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
let mut num = use_signal(|| 0);
|
let mut num = use_signal(|| 0);
|
||||||
@@ -18,44 +19,7 @@ async fn main() {
|
|||||||
|
|
||||||
let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into();
|
let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into();
|
||||||
|
|
||||||
let view = dioxus_liveview::LiveViewPool::new();
|
let app = Router::new().with_app("/", app);
|
||||||
let index_page_with_glue = |glue: &str| {
|
|
||||||
Html(format!(
|
|
||||||
r#"
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head> <title>Dioxus LiveView with axum</title> </head>
|
|
||||||
<body> <div id="main"></div> </body>
|
|
||||||
{glue}
|
|
||||||
</html>
|
|
||||||
"#,
|
|
||||||
))
|
|
||||||
};
|
|
||||||
|
|
||||||
let app =
|
|
||||||
Router::new()
|
|
||||||
.route(
|
|
||||||
"/",
|
|
||||||
get(move || async move {
|
|
||||||
index_page_with_glue(&dioxus_liveview::interpreter_glue(&format!(
|
|
||||||
"ws://{addr}/ws"
|
|
||||||
)))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/as-path",
|
|
||||||
get(move || async move {
|
|
||||||
index_page_with_glue(&dioxus_liveview::interpreter_glue("/ws"))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/ws",
|
|
||||||
get(move |ws: WebSocketUpgrade| async move {
|
|
||||||
ws.on_upgrade(move |socket| async move {
|
|
||||||
_ = view.launch(dioxus_liveview::axum_socket(socket), app).await;
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
println!("Listening on http://{addr}");
|
println!("Listening on http://{addr}");
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[handler]
|
#[handler]
|
||||||
fn index(_depot: &mut Depot, res: &mut Response) {
|
fn index(res: &mut Response) {
|
||||||
let addr: SocketAddr = ([127, 0, 0, 1], 3030).into();
|
let addr: SocketAddr = ([127, 0, 0, 1], 3030).into();
|
||||||
res.render(Text::Html(format!(
|
res.render(Text::Html(format!(
|
||||||
r#"
|
r#"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_liveview::adapters::warp_adapter::warp_socket;
|
use dioxus_liveview::warp_adapter::warp_socket;
|
||||||
use dioxus_liveview::LiveViewPool;
|
use dioxus_liveview::LiveViewPool;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use warp::ws::Ws;
|
use warp::ws::Ws;
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
use crate::{LiveViewError, LiveViewSocket};
|
use std::sync::Arc;
|
||||||
use axum::extract::ws::{Message, WebSocket};
|
|
||||||
|
use crate::{interpreter_glue, LiveViewError, LiveViewSocket, LiveviewRouter};
|
||||||
|
use axum::{
|
||||||
|
extract::{
|
||||||
|
ws::{Message, WebSocket},
|
||||||
|
WebSocketUpgrade,
|
||||||
|
},
|
||||||
|
response::Html,
|
||||||
|
routing::*,
|
||||||
|
Router,
|
||||||
|
};
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
|
||||||
/// Convert a warp websocket into a LiveViewSocket
|
/// Convert a warp websocket into a LiveViewSocket
|
||||||
@@ -22,3 +32,60 @@ fn transform_rx(message: Result<Message, axum::Error>) -> Result<Vec<u8>, LiveVi
|
|||||||
async fn transform_tx(message: Vec<u8>) -> Result<Message, axum::Error> {
|
async fn transform_tx(message: Vec<u8>) -> Result<Message, axum::Error> {
|
||||||
Ok(Message::Binary(message))
|
Ok(Message::Binary(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LiveviewRouter for Router {
|
||||||
|
fn create_default_liveview_router() -> Self {
|
||||||
|
Router::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_virtual_dom(
|
||||||
|
self,
|
||||||
|
route: &str,
|
||||||
|
app: impl Fn() -> dioxus_core::prelude::VirtualDom + Send + Sync + 'static,
|
||||||
|
) -> Self {
|
||||||
|
let view = crate::LiveViewPool::new();
|
||||||
|
|
||||||
|
let ws_path = format!("{}/ws", route);
|
||||||
|
let title = crate::app_title();
|
||||||
|
|
||||||
|
let index_page_with_glue = move |glue: &str| {
|
||||||
|
Html(format!(
|
||||||
|
r#"
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head> <title>{title}</title> </head>
|
||||||
|
<body> <div id="main"></div> </body>
|
||||||
|
{glue}
|
||||||
|
</html>
|
||||||
|
"#,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
let app = Arc::new(app);
|
||||||
|
|
||||||
|
self.route(
|
||||||
|
&ws_path,
|
||||||
|
get(move |ws: WebSocketUpgrade| async move {
|
||||||
|
let app = app.clone();
|
||||||
|
ws.on_upgrade(move |socket| async move {
|
||||||
|
_ = view
|
||||||
|
.launch_virtualdom(axum_socket(socket), move || app())
|
||||||
|
.await;
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
route,
|
||||||
|
get(move || async move { index_page_with_glue(&interpreter_glue(&ws_path)) }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn start(self, address: impl Into<std::net::SocketAddr>) {
|
||||||
|
if let Err(err) = axum::Server::bind(&address.into())
|
||||||
|
.serve(self.into_make_service())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
eprintln!("Failed to start axum server: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
use dioxus_core::{Element, VirtualDom};
|
||||||
|
|
||||||
|
#[cfg(feature = "warp")]
|
||||||
|
pub mod warp_adapter;
|
||||||
|
#[cfg(feature = "warp")]
|
||||||
|
pub use warp_adapter::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "axum")]
|
||||||
|
pub mod axum_adapter;
|
||||||
|
#[cfg(feature = "axum")]
|
||||||
|
pub use axum_adapter::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "salvo")]
|
||||||
|
pub mod salvo_adapter;
|
||||||
|
#[cfg(feature = "salvo")]
|
||||||
|
pub use salvo_adapter::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "rocket")]
|
||||||
|
pub mod rocket_adapter;
|
||||||
|
#[cfg(feature = "rocket")]
|
||||||
|
pub use rocket_adapter::*;
|
||||||
|
|
||||||
|
/// A trait for servers that can be used to host a LiveView app.
|
||||||
|
pub trait LiveviewRouter {
|
||||||
|
/// Create a new router.
|
||||||
|
fn create_default_liveview_router() -> Self;
|
||||||
|
|
||||||
|
/// Add a liveview route to the server from a component
|
||||||
|
fn with_app(self, route: &str, app: fn() -> Element) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.with_virtual_dom(route, move || VirtualDom::new(app))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a liveview route to the server from a virtual dom.
|
||||||
|
fn with_virtual_dom(
|
||||||
|
self,
|
||||||
|
route: &str,
|
||||||
|
app: impl Fn() -> VirtualDom + Send + Sync + 'static,
|
||||||
|
) -> Self;
|
||||||
|
|
||||||
|
/// Start the server on an address.
|
||||||
|
fn start(self, address: impl Into<std::net::SocketAddr>) -> impl Future<Output = ()>;
|
||||||
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
|
use crate::LiveViewPool;
|
||||||
|
use crate::LiveviewRouter;
|
||||||
use crate::{LiveViewError, LiveViewSocket};
|
use crate::{LiveViewError, LiveViewSocket};
|
||||||
use rocket::futures::{SinkExt, StreamExt};
|
use rocket::futures::{SinkExt, StreamExt};
|
||||||
|
use rocket::response::content::RawHtml;
|
||||||
|
use rocket::{get, routes, State};
|
||||||
|
use rocket_ws::Channel;
|
||||||
|
use rocket_ws::WebSocket;
|
||||||
use rocket_ws::{result::Error, stream::DuplexStream, Message};
|
use rocket_ws::{result::Error, stream::DuplexStream, Message};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Convert a rocket websocket into a LiveViewSocket
|
/// Convert a rocket websocket into a LiveViewSocket
|
||||||
///
|
///
|
||||||
@@ -21,5 +28,86 @@ fn transform_rx(message: Result<Message, Error>) -> Result<Vec<u8>, LiveViewErro
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn transform_tx(message: Vec<u8>) -> Result<Message, Error> {
|
async fn transform_tx(message: Vec<u8>) -> Result<Message, Error> {
|
||||||
Ok(Message::Text(String::from_utf8_lossy(&message).to_string()))
|
Ok(Message::Binary(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LiveviewRouter for rocket::Rocket<rocket::Build> {
|
||||||
|
fn create_default_liveview_router() -> Self {
|
||||||
|
Self::build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_virtual_dom(
|
||||||
|
self,
|
||||||
|
route: &str,
|
||||||
|
app: impl Fn() -> dioxus_core::prelude::VirtualDom + Send + Sync + 'static,
|
||||||
|
) -> Self {
|
||||||
|
#[get("/")]
|
||||||
|
async fn index(request: &rocket::route::Route) -> RawHtml<String> {
|
||||||
|
let route = request.uri.base();
|
||||||
|
|
||||||
|
let glue = crate::interpreter_glue(&format!("{route}/ws",));
|
||||||
|
|
||||||
|
let title = crate::app_title();
|
||||||
|
|
||||||
|
RawHtml(format!(
|
||||||
|
r#"
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head> <title>{title}</title> </head>
|
||||||
|
<body> <div id="main"></div> </body>
|
||||||
|
{glue}
|
||||||
|
</html>
|
||||||
|
"#
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/ws")]
|
||||||
|
fn ws(ws: WebSocket, app: &State<LiveviewApp>) -> Channel<'static> {
|
||||||
|
let app = app.inner();
|
||||||
|
let pool = app.pool.clone();
|
||||||
|
let app = app.app.clone();
|
||||||
|
|
||||||
|
ws.channel(move |stream| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let _ = pool
|
||||||
|
.launch_virtualdom(crate::rocket_socket(stream), move || app())
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LiveviewApp {
|
||||||
|
app: Arc<dyn Fn() -> dioxus_core::prelude::VirtualDom + Send + Sync + 'static>,
|
||||||
|
pool: LiveViewPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
let app = Arc::new(app);
|
||||||
|
|
||||||
|
let view = crate::LiveViewPool::new();
|
||||||
|
|
||||||
|
self.manage(LiveviewApp {
|
||||||
|
app: app,
|
||||||
|
pool: view,
|
||||||
|
})
|
||||||
|
.mount(route, routes![index, ws])
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn start(self, address: impl Into<std::net::SocketAddr>) {
|
||||||
|
let address = address.into();
|
||||||
|
|
||||||
|
let figment = self
|
||||||
|
.figment()
|
||||||
|
.clone()
|
||||||
|
.merge((rocket::Config::PORT, address.port()))
|
||||||
|
.merge((rocket::Config::ADDRESS, address.ip()));
|
||||||
|
|
||||||
|
self.configure(figment)
|
||||||
|
.ignite()
|
||||||
|
.await
|
||||||
|
.expect("Failed to ignite rocket")
|
||||||
|
.launch()
|
||||||
|
.await
|
||||||
|
.expect("Failed to launch rocket");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,16 @@
|
|||||||
use futures_util::{SinkExt, StreamExt};
|
use crate::LiveViewPool;
|
||||||
use salvo::ws::{Message, WebSocket};
|
use crate::LiveviewRouter;
|
||||||
|
|
||||||
use crate::{LiveViewError, LiveViewSocket};
|
use crate::{LiveViewError, LiveViewSocket};
|
||||||
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
use salvo::conn::TcpListener;
|
||||||
|
use salvo::http::StatusError;
|
||||||
|
use salvo::websocket::WebSocketUpgrade;
|
||||||
|
use salvo::websocket::{Message, WebSocket};
|
||||||
|
use salvo::writing::Text;
|
||||||
|
use salvo::Listener;
|
||||||
|
use salvo::Server;
|
||||||
|
use salvo::{handler, Depot, Request, Response, Router};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Convert a salvo websocket into a LiveViewSocket
|
/// Convert a salvo websocket into a LiveViewSocket
|
||||||
///
|
///
|
||||||
@@ -19,5 +28,81 @@ fn transform_rx(message: Result<Message, salvo::Error>) -> Result<Vec<u8>, LiveV
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn transform_tx(message: Vec<u8>) -> Result<Message, salvo::Error> {
|
async fn transform_tx(message: Vec<u8>) -> Result<Message, salvo::Error> {
|
||||||
Ok(Message::text(String::from_utf8_lossy(&message).to_string()))
|
Ok(Message::binary(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct LiveviewApp {
|
||||||
|
app: Arc<dyn Fn() -> dioxus_core::prelude::VirtualDom + Send + Sync + 'static>,
|
||||||
|
pool: Arc<LiveViewPool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LiveviewRouter for Router {
|
||||||
|
fn create_default_liveview_router() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_virtual_dom(
|
||||||
|
self,
|
||||||
|
route: &str,
|
||||||
|
app: impl Fn() -> dioxus_core::prelude::VirtualDom + Send + Sync + 'static,
|
||||||
|
) -> Self {
|
||||||
|
let app = Arc::new(app);
|
||||||
|
|
||||||
|
let view = crate::LiveViewPool::new();
|
||||||
|
|
||||||
|
self.push(
|
||||||
|
Router::with_path(route)
|
||||||
|
.hoop(salvo::affix::inject(LiveviewApp {
|
||||||
|
app: app,
|
||||||
|
pool: Arc::new(view),
|
||||||
|
}))
|
||||||
|
.get(index)
|
||||||
|
.push(Router::with_path("ws").get(connect)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn start(self, address: impl Into<std::net::SocketAddr>) {
|
||||||
|
let address = address.into();
|
||||||
|
|
||||||
|
let acceptor = TcpListener::new(address).bind().await;
|
||||||
|
Server::new(acceptor).serve(self).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[handler]
|
||||||
|
fn index(req: &mut Request, res: &mut Response) {
|
||||||
|
let base = req.uri().path();
|
||||||
|
let title = crate::app_title();
|
||||||
|
|
||||||
|
res.render(Text::Html(format!(
|
||||||
|
r#"
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head> <title>{title}</title> </head>
|
||||||
|
<body> <div id="main"></div> </body>
|
||||||
|
{glue}
|
||||||
|
</html>
|
||||||
|
"#,
|
||||||
|
glue = crate::interpreter_glue(&format!("{base}/ws"))
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[handler]
|
||||||
|
async fn connect(
|
||||||
|
req: &mut Request,
|
||||||
|
depot: &mut Depot,
|
||||||
|
res: &mut Response,
|
||||||
|
) -> Result<(), StatusError> {
|
||||||
|
let app = depot.obtain::<LiveviewApp>().unwrap().clone();
|
||||||
|
let view = app.pool.clone();
|
||||||
|
let app = app.app.clone();
|
||||||
|
|
||||||
|
WebSocketUpgrade::new()
|
||||||
|
.upgrade(req, res, |ws| async move {
|
||||||
|
_ = view
|
||||||
|
.launch_virtualdom(crate::salvo_socket(ws), move || app())
|
||||||
|
.await;
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
@@ -21,5 +21,5 @@ fn transform_rx(message: Result<Message, warp::Error>) -> Result<Vec<u8>, LiveVi
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn transform_tx(message: Vec<u8>) -> Result<Message, warp::Error> {
|
async fn transform_tx(message: Vec<u8>) -> Result<Message, warp::Error> {
|
||||||
Ok(Message::text(String::from_utf8_lossy(&message).to_string()))
|
Ok(Message::binary(message))
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
use dioxus_cli_config::CURRENT_CONFIG;
|
||||||
|
use dioxus_core::VirtualDom;
|
||||||
|
|
||||||
|
use crate::LiveviewRouter;
|
||||||
|
|
||||||
|
pub(crate) fn app_title() -> String {
|
||||||
|
CURRENT_CONFIG
|
||||||
|
.as_ref()
|
||||||
|
.map(|c| c.dioxus_config.web.app.title.clone())
|
||||||
|
.unwrap_or_else(|_| "Dioxus Liveview App".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A configuration for the LiveView server.
|
||||||
|
pub struct Config<R: LiveviewRouter> {
|
||||||
|
router: R,
|
||||||
|
address: std::net::SocketAddr,
|
||||||
|
route: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: LiveviewRouter> Default for Config<R> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
router: R::create_default_liveview_router(),
|
||||||
|
address: ([127, 0, 0, 1], 8080).into(),
|
||||||
|
route: "/".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: LiveviewRouter> Config<R> {
|
||||||
|
/// Set the route to use for the LiveView server.
|
||||||
|
pub fn route(mut self, route: impl Into<String>) -> Self {
|
||||||
|
self.route = route.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new configuration for the LiveView server.
|
||||||
|
pub fn with_app(mut self, app: fn() -> dioxus_core::prelude::Element) -> Self {
|
||||||
|
self.router = self.router.with_app(&self.route, app);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new configuration for the LiveView server.
|
||||||
|
pub fn with_virtual_dom(
|
||||||
|
mut self,
|
||||||
|
virtual_dom: impl Fn() -> VirtualDom + Send + Sync + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.router = self.router.with_virtual_dom(&self.route, virtual_dom);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the address to listen on.
|
||||||
|
pub fn address(mut self, address: impl Into<std::net::SocketAddr>) -> Self {
|
||||||
|
self.address = address.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Launch the LiveView server.
|
||||||
|
pub async fn launch(self) {
|
||||||
|
println!("{} started on http://{}", app_title(), self.address);
|
||||||
|
self.router.start(self.address).await
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use dioxus_core::*;
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
#[cfg(feature = "axum")]
|
||||||
|
pub type Config = crate::Config<axum::Router>;
|
||||||
|
#[cfg(feature = "salvo")]
|
||||||
|
pub type Config = crate::Config<salvo::Router>;
|
||||||
|
#[cfg(feature = "rocket")]
|
||||||
|
pub type Config = crate::Config<rocket::Rocket<rocket::Build>>;
|
||||||
|
|
||||||
|
/// Launches the WebView and runs the event loop, with configuration and root props.
|
||||||
|
pub fn launch(
|
||||||
|
root: fn() -> Element,
|
||||||
|
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
||||||
|
platform_config: Config,
|
||||||
|
) {
|
||||||
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.block_on(async move {
|
||||||
|
platform_config
|
||||||
|
.with_virtual_dom(move || {
|
||||||
|
let mut virtual_dom = VirtualDom::new(root);
|
||||||
|
|
||||||
|
for context in &contexts {
|
||||||
|
virtual_dom.insert_any_root_context(context());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual_dom
|
||||||
|
})
|
||||||
|
.launch()
|
||||||
|
.await;
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -2,29 +2,7 @@
|
|||||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
|
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
|
||||||
#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
|
#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
|
||||||
|
|
||||||
pub mod adapters {
|
mod adapters;
|
||||||
#[cfg(feature = "warp")]
|
|
||||||
pub mod warp_adapter;
|
|
||||||
#[cfg(feature = "warp")]
|
|
||||||
pub use warp_adapter::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "axum")]
|
|
||||||
pub mod axum_adapter;
|
|
||||||
#[cfg(feature = "axum")]
|
|
||||||
pub use axum_adapter::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "salvo")]
|
|
||||||
pub mod salvo_adapter;
|
|
||||||
|
|
||||||
#[cfg(feature = "salvo")]
|
|
||||||
pub use salvo_adapter::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "rocket")]
|
|
||||||
pub mod rocket_adapter;
|
|
||||||
#[cfg(feature = "rocket")]
|
|
||||||
pub use rocket_adapter::*;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use adapters::*;
|
pub use adapters::*;
|
||||||
|
|
||||||
@@ -33,8 +11,11 @@ pub mod pool;
|
|||||||
mod query;
|
mod query;
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
pub use pool::*;
|
pub use pool::*;
|
||||||
|
mod config;
|
||||||
mod eval;
|
mod eval;
|
||||||
mod events;
|
mod events;
|
||||||
|
pub use config::*;
|
||||||
|
pub mod launch;
|
||||||
|
|
||||||
pub trait WebsocketTx: SinkExt<String, Error = LiveViewError> {}
|
pub trait WebsocketTx: SinkExt<String, Error = LiveViewError> {}
|
||||||
impl<T> WebsocketTx for T where T: SinkExt<String, Error = LiveViewError> {}
|
impl<T> WebsocketTx for T where T: SinkExt<String, Error = LiveViewError> {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::HistoryProvider;
|
use super::HistoryProvider;
|
||||||
use crate::routable::Routable;
|
use crate::routable::Routable;
|
||||||
use dioxus::prelude::*;
|
use dioxus_lib::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::{Mutex, RwLock};
|
use std::sync::{Mutex, RwLock};
|
||||||
use std::{collections::BTreeMap, rc::Rc, str::FromStr, sync::Arc};
|
use std::{collections::BTreeMap, rc::Rc, str::FromStr, sync::Arc};
|
||||||
@@ -172,8 +172,7 @@ where
|
|||||||
let updater_callback: Arc<RwLock<Arc<dyn Fn() + Send + Sync>>> =
|
let updater_callback: Arc<RwLock<Arc<dyn Fn() + Send + Sync>>> =
|
||||||
Arc::new(RwLock::new(Arc::new(|| {})));
|
Arc::new(RwLock::new(Arc::new(|| {})));
|
||||||
|
|
||||||
let eval_provider =
|
let eval_provider = consume_context::<Rc<dyn EvalProvider>>();
|
||||||
consume_context::<Rc<dyn EvalProvider>>().expect("evaluator not provided");
|
|
||||||
|
|
||||||
let create_eval = Rc::new(move |script: &str| {
|
let create_eval = Rc::new(move |script: &str| {
|
||||||
eval_provider
|
eval_provider
|
||||||
@@ -182,7 +181,7 @@ where
|
|||||||
}) as Rc<dyn Fn(&str) -> Result<UseEval, EvalError>>;
|
}) as Rc<dyn Fn(&str) -> Result<UseEval, EvalError>>;
|
||||||
|
|
||||||
// Listen to server actions
|
// Listen to server actions
|
||||||
push_future({
|
spawn({
|
||||||
let timeline = timeline.clone();
|
let timeline = timeline.clone();
|
||||||
let action_rx = action_rx.clone();
|
let action_rx = action_rx.clone();
|
||||||
let create_eval = create_eval.clone();
|
let create_eval = create_eval.clone();
|
||||||
@@ -242,12 +241,12 @@ where
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Listen to browser actions
|
// Listen to browser actions
|
||||||
push_future({
|
spawn({
|
||||||
let updater = updater_callback.clone();
|
let updater = updater_callback.clone();
|
||||||
let timeline = timeline.clone();
|
let timeline = timeline.clone();
|
||||||
let create_eval = create_eval.clone();
|
let create_eval = create_eval.clone();
|
||||||
async move {
|
async move {
|
||||||
let popstate_eval = {
|
let mut popstate_eval = {
|
||||||
let init_eval = create_eval(
|
let init_eval = create_eval(
|
||||||
r#"
|
r#"
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ macro_rules! default_history {
|
|||||||
);
|
);
|
||||||
// If we are not on wasm32 and the liveview feature is enabled, use the liveview history.
|
// If we are not on wasm32 and the liveview feature is enabled, use the liveview history.
|
||||||
#[cfg(all(feature = "liveview"))]
|
#[cfg(all(feature = "liveview"))]
|
||||||
return Box::new(AnyHistoryProviderImplWrapper::new(LiveviewHistory::new($initial_route)));
|
return Box::new(AnyHistoryProviderImplWrapper::new(LiveviewHistory::new_with_initial_path($initial_route)));
|
||||||
// Otherwise use the memory history.
|
// Otherwise use the memory history.
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
not(all(target_arch = "wasm32", feature = "web")),
|
not(all(target_arch = "wasm32", feature = "web")),
|
||||||
|
|||||||
Reference in new issue
Block a user