merge upstream
This commit is contained in:
1 parent
f34053c18f
commit
0a7873fcd0
11 files changed
+166
-28
No files matched your search
@@ -161,7 +161,7 @@ So... Dioxus is great, but why won't it work for me?
|
|||||||
## Contributing
|
## Contributing
|
||||||
- Check out the website [section on contributing](https://dioxuslabs.com/learn/0.4/contributing).
|
- Check out the website [section on contributing](https://dioxuslabs.com/learn/0.4/contributing).
|
||||||
- Report issues on our [issue tracker](https://github.com/dioxuslabs/dioxus/issues).
|
- Report issues on our [issue tracker](https://github.com/dioxuslabs/dioxus/issues).
|
||||||
- Join the discord and ask questions!
|
- [Join](https://discord.gg/XgGxMSkvUM) the discord and ask questions!
|
||||||
|
|
||||||
|
|
||||||
<a href="https://github.com/dioxuslabs/dioxus/graphs/contributors">
|
<a href="https://github.com/dioxuslabs/dioxus/graphs/contributors">
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use crate::{innerlude::VNode, ScopeState};
|
|||||||
|
|
||||||
/// A concrete type provider for closures that build [`VNode`] structures.
|
/// A concrete type provider for closures that build [`VNode`] structures.
|
||||||
///
|
///
|
||||||
/// This struct wraps lazy structs that build [`VNode`] trees Normally, we cannot perform a blanket implementation over
|
/// This struct wraps lazy structs that build [`VNode`] trees. Normally, we cannot perform a blanket implementation over
|
||||||
/// closures, but if we wrap the closure in a concrete type, we can use it for different branches in matching.
|
/// closures, but if we wrap the closure in a concrete type, we can use it for different branches in matching.
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ salvo = { version = "0.44.1", optional = true, features = ["ws"] }
|
|||||||
once_cell = "1.17.1"
|
once_cell = "1.17.1"
|
||||||
async-trait = "0.1.71"
|
async-trait = "0.1.71"
|
||||||
|
|
||||||
|
# rocket
|
||||||
|
rocket = { version = "0.5.0", optional = true }
|
||||||
|
rocket_ws = { version = "0.1.0", optional = true }
|
||||||
|
|
||||||
# actix is ... complicated?
|
# actix is ... complicated?
|
||||||
# actix-files = { version = "0.6.2", optional = true }
|
# actix-files = { version = "0.6.2", optional = true }
|
||||||
# actix-web = { version = "4.2.1", optional = true }
|
# actix-web = { version = "4.2.1", optional = true }
|
||||||
@@ -49,13 +53,16 @@ tokio = { workspace = true, features = ["full"] }
|
|||||||
dioxus = { workspace = true }
|
dioxus = { workspace = true }
|
||||||
warp = "0.3.3"
|
warp = "0.3.3"
|
||||||
axum = { version = "0.6.1", features = ["ws"] }
|
axum = { version = "0.6.1", features = ["ws"] }
|
||||||
salvo = { version = "0.44.1", features = ["affix", "ws"] }
|
# salvo = { version = "0.44.1", features = ["affix", "ws"] }
|
||||||
|
rocket = "0.5.0"
|
||||||
|
rocket_ws = "0.1.0"
|
||||||
tower = "0.4.13"
|
tower = "0.4.13"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["hot-reload"]
|
default = ["hot-reload"]
|
||||||
# actix = ["actix-files", "actix-web", "actix-ws"]
|
# actix = ["actix-files", "actix-web", "actix-ws"]
|
||||||
hot-reload = ["dioxus-hot-reload"]
|
hot-reload = ["dioxus-hot-reload"]
|
||||||
|
rocket = ["dep:rocket", "dep:rocket_ws"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
@@ -68,3 +75,7 @@ required-features = ["salvo"]
|
|||||||
[[example]]
|
[[example]]
|
||||||
name = "warp"
|
name = "warp"
|
||||||
required-features = ["warp"]
|
required-features = ["warp"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "rocket"
|
||||||
|
required-features = ["rocket"]
|
||||||
@@ -28,6 +28,7 @@ The current backend frameworks supported include:
|
|||||||
- Axum
|
- Axum
|
||||||
- Warp
|
- Warp
|
||||||
- Salvo
|
- Salvo
|
||||||
|
- Rocket
|
||||||
|
|
||||||
Dioxus-LiveView exports some primitives to wire up an app into an existing backend framework.
|
Dioxus-LiveView exports some primitives to wire up an app into an existing backend framework.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate rocket;
|
||||||
|
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_liveview::LiveViewPool;
|
||||||
|
use rocket::response::content::RawHtml;
|
||||||
|
use rocket::{Config, Rocket, State};
|
||||||
|
use rocket_ws::{Channel, WebSocket};
|
||||||
|
|
||||||
|
fn app(cx: Scope) -> Element {
|
||||||
|
let mut num = use_state(cx, || 0);
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
"hello Rocket! {num}"
|
||||||
|
button { onclick: move |_| num += 1, "Increment" }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index_page_with_glue(glue: &str) -> RawHtml<String> {
|
||||||
|
RawHtml(format!(
|
||||||
|
r#"
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head> <title>Dioxus LiveView with Rocket</title> </head>
|
||||||
|
<body> <div id="main"></div> </body>
|
||||||
|
{glue}
|
||||||
|
</html>
|
||||||
|
"#,
|
||||||
|
glue = glue
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
async fn index(config: &Config) -> RawHtml<String> {
|
||||||
|
index_page_with_glue(&dioxus_liveview::interpreter_glue(&format!(
|
||||||
|
"ws://{addr}:{port}/ws",
|
||||||
|
addr = config.address,
|
||||||
|
port = config.port,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/as-path")]
|
||||||
|
async fn as_path() -> RawHtml<String> {
|
||||||
|
index_page_with_glue(&dioxus_liveview::interpreter_glue("/ws"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/ws")]
|
||||||
|
fn ws(ws: WebSocket, pool: &State<LiveViewPool>) -> Channel<'static> {
|
||||||
|
let pool = pool.inner().to_owned();
|
||||||
|
|
||||||
|
ws.channel(move |stream| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let _ = pool
|
||||||
|
.launch(dioxus_liveview::rocket_socket(stream), app)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let view = dioxus_liveview::LiveViewPool::new();
|
||||||
|
|
||||||
|
Rocket::build()
|
||||||
|
.manage(view)
|
||||||
|
.mount("/", routes![index, as_path, ws])
|
||||||
|
.ignite()
|
||||||
|
.await
|
||||||
|
.expect("Failed to ignite rocket")
|
||||||
|
.launch()
|
||||||
|
.await
|
||||||
|
.expect("Failed to launch rocket");
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
use crate::{LiveViewError, LiveViewSocket};
|
||||||
|
use rocket::futures::{SinkExt, StreamExt};
|
||||||
|
use rocket_ws::{result::Error, stream::DuplexStream, Message};
|
||||||
|
|
||||||
|
/// Convert a rocket websocket into a LiveViewSocket
|
||||||
|
///
|
||||||
|
/// This is required to launch a LiveView app using the rocket web framework
|
||||||
|
pub fn rocket_socket(stream: DuplexStream) -> impl LiveViewSocket {
|
||||||
|
stream
|
||||||
|
.map(transform_rx)
|
||||||
|
.with(transform_tx)
|
||||||
|
.sink_map_err(|_| LiveViewError::SendingFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transform_rx(message: Result<Message, Error>) -> Result<Vec<u8>, LiveViewError> {
|
||||||
|
message
|
||||||
|
.map_err(|_| LiveViewError::SendingFailed)?
|
||||||
|
.into_text()
|
||||||
|
.map(|s| s.into_bytes())
|
||||||
|
.map_err(|_| LiveViewError::SendingFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn transform_tx(message: Vec<u8>) -> Result<Message, Error> {
|
||||||
|
Ok(Message::Text(String::from_utf8_lossy(&message).to_string()))
|
||||||
|
}
|
||||||
@@ -18,6 +18,11 @@ pub mod adapters {
|
|||||||
|
|
||||||
#[cfg(feature = "salvo")]
|
#[cfg(feature = "salvo")]
|
||||||
pub use salvo_adapter::*;
|
pub use salvo_adapter::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "rocket")]
|
||||||
|
pub mod rocket_adapter;
|
||||||
|
#[cfg(feature = "rocket")]
|
||||||
|
pub use rocket_adapter::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use adapters::*;
|
pub use adapters::*;
|
||||||
|
|||||||
@@ -106,7 +106,10 @@ impl Renderer {
|
|||||||
|
|
||||||
if self.pre_render {
|
if self.pre_render {
|
||||||
if let AttributeValue::Listener(_) = &attr.value {
|
if let AttributeValue::Listener(_) = &attr.value {
|
||||||
accumulated_listeners.push(attr.name);
|
// The onmounted event doesn't need a DOM listener
|
||||||
|
if attr.name != "onmounted" {
|
||||||
|
accumulated_listeners.push(attr.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-11
@@ -255,17 +255,21 @@ impl WebsysDom {
|
|||||||
i.flush();
|
i.flush();
|
||||||
|
|
||||||
for id in to_mount {
|
for id in to_mount {
|
||||||
let node = get_node(id.0 as u32);
|
self.send_mount_event(id);
|
||||||
if let Some(element) = node.dyn_ref::<Element>() {
|
}
|
||||||
let data: MountedData = element.into();
|
}
|
||||||
let data = Rc::new(data);
|
|
||||||
let _ = self.event_channel.unbounded_send(UiEvent {
|
pub(crate) fn send_mount_event(&self, id: ElementId) {
|
||||||
name: "mounted".to_string(),
|
let node = get_node(id.0 as u32);
|
||||||
bubbles: false,
|
if let Some(element) = node.dyn_ref::<Element>() {
|
||||||
element: id,
|
let data: MountedData = element.into();
|
||||||
data,
|
let data = Rc::new(data);
|
||||||
});
|
let _ = self.event_channel.unbounded_send(UiEvent {
|
||||||
}
|
name: "mounted".to_string(),
|
||||||
|
bubbles: false,
|
||||||
|
element: id,
|
||||||
|
data,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,13 +253,6 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
|
|||||||
pin_mut!(work);
|
pin_mut!(work);
|
||||||
|
|
||||||
#[cfg(all(feature = "hot_reload", debug_assertions))]
|
#[cfg(all(feature = "hot_reload", debug_assertions))]
|
||||||
// futures_util::select! {
|
|
||||||
// _ = work => (None, None),
|
|
||||||
// new_template = hotreload_rx.next() => {
|
|
||||||
// (None, new_template)
|
|
||||||
// }
|
|
||||||
// evt = rx.next() =>
|
|
||||||
// }
|
|
||||||
match select(work, select(hotreload_rx.next(), rx.next())).await {
|
match select(work, select(hotreload_rx.next(), rx.next())).await {
|
||||||
Either::Left((_, _)) => (None, None),
|
Either::Left((_, _)) => (None, None),
|
||||||
Either::Right((Either::Left((new_template, _)), _)) => (None, new_template),
|
Either::Right((Either::Left((new_template, _)), _)) => (None, new_template),
|
||||||
|
|||||||
@@ -14,12 +14,17 @@ impl WebsysDom {
|
|||||||
pub fn rehydrate(&mut self, dom: &VirtualDom) -> Result<(), RehydrationError> {
|
pub fn rehydrate(&mut self, dom: &VirtualDom) -> Result<(), RehydrationError> {
|
||||||
let root_scope = dom.base_scope();
|
let root_scope = dom.base_scope();
|
||||||
let mut ids = Vec::new();
|
let mut ids = Vec::new();
|
||||||
|
let mut to_mount = Vec::new();
|
||||||
|
|
||||||
// Recursively rehydrate the dom from the VirtualDom
|
// Recursively rehydrate the dom from the VirtualDom
|
||||||
self.rehydrate_scope(root_scope, dom, &mut ids)?;
|
self.rehydrate_scope(root_scope, dom, &mut ids, &mut to_mount)?;
|
||||||
|
|
||||||
dioxus_interpreter_js::hydrate(ids);
|
dioxus_interpreter_js::hydrate(ids);
|
||||||
|
|
||||||
|
for id in to_mount {
|
||||||
|
self.send_mount_event(id);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,12 +33,13 @@ impl WebsysDom {
|
|||||||
scope: &ScopeState,
|
scope: &ScopeState,
|
||||||
dom: &VirtualDom,
|
dom: &VirtualDom,
|
||||||
ids: &mut Vec<u32>,
|
ids: &mut Vec<u32>,
|
||||||
|
to_mount: &mut Vec<ElementId>,
|
||||||
) -> Result<(), RehydrationError> {
|
) -> Result<(), RehydrationError> {
|
||||||
let vnode = match scope.root_node() {
|
let vnode = match scope.root_node() {
|
||||||
dioxus_core::RenderReturn::Ready(ready) => ready,
|
dioxus_core::RenderReturn::Ready(ready) => ready,
|
||||||
_ => return Err(VNodeNotInitialized),
|
_ => return Err(VNodeNotInitialized),
|
||||||
};
|
};
|
||||||
self.rehydrate_vnode(dom, vnode, ids)
|
self.rehydrate_vnode(dom, vnode, ids, to_mount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rehydrate_vnode(
|
fn rehydrate_vnode(
|
||||||
@@ -41,6 +47,7 @@ impl WebsysDom {
|
|||||||
dom: &VirtualDom,
|
dom: &VirtualDom,
|
||||||
vnode: &VNode,
|
vnode: &VNode,
|
||||||
ids: &mut Vec<u32>,
|
ids: &mut Vec<u32>,
|
||||||
|
to_mount: &mut Vec<ElementId>,
|
||||||
) -> Result<(), RehydrationError> {
|
) -> Result<(), RehydrationError> {
|
||||||
for (i, root) in vnode.template.get().roots.iter().enumerate() {
|
for (i, root) in vnode.template.get().roots.iter().enumerate() {
|
||||||
self.rehydrate_template_node(
|
self.rehydrate_template_node(
|
||||||
@@ -48,6 +55,7 @@ impl WebsysDom {
|
|||||||
vnode,
|
vnode,
|
||||||
root,
|
root,
|
||||||
ids,
|
ids,
|
||||||
|
to_mount,
|
||||||
Some(*vnode.root_ids.borrow().get(i).ok_or(VNodeNotInitialized)?),
|
Some(*vnode.root_ids.borrow().get(i).ok_or(VNodeNotInitialized)?),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -60,6 +68,7 @@ impl WebsysDom {
|
|||||||
vnode: &VNode,
|
vnode: &VNode,
|
||||||
node: &TemplateNode,
|
node: &TemplateNode,
|
||||||
ids: &mut Vec<u32>,
|
ids: &mut Vec<u32>,
|
||||||
|
to_mount: &mut Vec<ElementId>,
|
||||||
root_id: Option<ElementId>,
|
root_id: Option<ElementId>,
|
||||||
) -> Result<(), RehydrationError> {
|
) -> Result<(), RehydrationError> {
|
||||||
tracing::trace!("rehydrate template node: {:?}", node);
|
tracing::trace!("rehydrate template node: {:?}", node);
|
||||||
@@ -73,6 +82,11 @@ impl WebsysDom {
|
|||||||
let attribute = &vnode.dynamic_attrs[*id];
|
let attribute = &vnode.dynamic_attrs[*id];
|
||||||
let id = attribute.mounted_element();
|
let id = attribute.mounted_element();
|
||||||
mounted_id = Some(id);
|
mounted_id = Some(id);
|
||||||
|
if let dioxus_core::AttributeValue::Listener(_) = attribute.value {
|
||||||
|
if attribute.name == "onmounted" {
|
||||||
|
to_mount.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(id) = mounted_id {
|
if let Some(id) = mounted_id {
|
||||||
@@ -80,12 +94,12 @@ impl WebsysDom {
|
|||||||
}
|
}
|
||||||
if !children.is_empty() {
|
if !children.is_empty() {
|
||||||
for child in *children {
|
for child in *children {
|
||||||
self.rehydrate_template_node(dom, vnode, child, ids, None)?;
|
self.rehydrate_template_node(dom, vnode, child, ids, to_mount, None)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => {
|
TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => {
|
||||||
self.rehydrate_dynamic_node(dom, &vnode.dynamic_nodes[*id], ids)?;
|
self.rehydrate_dynamic_node(dom, &vnode.dynamic_nodes[*id], ids, to_mount)?;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -97,6 +111,7 @@ impl WebsysDom {
|
|||||||
dom: &VirtualDom,
|
dom: &VirtualDom,
|
||||||
dynamic: &DynamicNode,
|
dynamic: &DynamicNode,
|
||||||
ids: &mut Vec<u32>,
|
ids: &mut Vec<u32>,
|
||||||
|
to_mount: &mut Vec<ElementId>,
|
||||||
) -> Result<(), RehydrationError> {
|
) -> Result<(), RehydrationError> {
|
||||||
tracing::trace!("rehydrate dynamic node: {:?}", dynamic);
|
tracing::trace!("rehydrate dynamic node: {:?}", dynamic);
|
||||||
match dynamic {
|
match dynamic {
|
||||||
@@ -108,11 +123,16 @@ impl WebsysDom {
|
|||||||
}
|
}
|
||||||
dioxus_core::DynamicNode::Component(comp) => {
|
dioxus_core::DynamicNode::Component(comp) => {
|
||||||
let scope = comp.mounted_scope().ok_or(VNodeNotInitialized)?;
|
let scope = comp.mounted_scope().ok_or(VNodeNotInitialized)?;
|
||||||
self.rehydrate_scope(dom.get_scope(scope).ok_or(VNodeNotInitialized)?, dom, ids)?;
|
self.rehydrate_scope(
|
||||||
|
dom.get_scope(scope).ok_or(VNodeNotInitialized)?,
|
||||||
|
dom,
|
||||||
|
ids,
|
||||||
|
to_mount,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
dioxus_core::DynamicNode::Fragment(fragment) => {
|
dioxus_core::DynamicNode::Fragment(fragment) => {
|
||||||
for vnode in *fragment {
|
for vnode in *fragment {
|
||||||
self.rehydrate_vnode(dom, vnode, ids)?;
|
self.rehydrate_vnode(dom, vnode, ids, to_mount)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user