Merge branch 'master' into events-2
This commit is contained in:
commit
dccfba12d8
209 files changed
+6270
-2789
No files matched your search
+168
-19
@@ -5,9 +5,11 @@ use crate::{
|
||||
query::{QueryEngine, QueryResult},
|
||||
LiveViewError,
|
||||
};
|
||||
use dioxus_core::{prelude::*, Mutations};
|
||||
use dioxus_html::{EventData, HtmlEvent, MountedData, PlatformEventData};
|
||||
use dioxus_core::{prelude::*, BorrowedAttributeValue, Mutations};
|
||||
use dioxus_html::{event_bubbles,EventData, HtmlEvent, MountedData, PlatformEventData};
|
||||
use dioxus_interpreter_js::binary_protocol::Channel;
|
||||
use futures_util::{pin_mut, SinkExt, StreamExt};
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::Serialize;
|
||||
use std::{rc::Rc, time::Duration};
|
||||
use tokio_util::task::LocalPoolHandle;
|
||||
@@ -111,7 +113,7 @@ impl<S> LiveViewSocket for S where
|
||||
///
|
||||
/// This function makes it easy to integrate Dioxus LiveView with any socket-based framework.
|
||||
///
|
||||
/// As long as your framework can provide a Sink and Stream of Strings, you can use this function.
|
||||
/// As long as your framework can provide a Sink and Stream of Bytes, you can use this function.
|
||||
///
|
||||
/// You might need to transform the error types of the web backend into the LiveView error type.
|
||||
pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), LiveViewError> {
|
||||
@@ -124,20 +126,31 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li
|
||||
rx
|
||||
};
|
||||
|
||||
let mut templates: FxHashMap<String, u16> = Default::default();
|
||||
let mut max_template_count = 0;
|
||||
|
||||
// Create the a proxy for query engine
|
||||
let (query_tx, mut query_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let query_engine = QueryEngine::new(query_tx);
|
||||
vdom.base_scope().provide_context(query_engine.clone());
|
||||
init_eval(vdom.base_scope());
|
||||
|
||||
// todo: use an efficient binary packed format for this
|
||||
let edits = serde_json::to_string(&ClientUpdate::Edits(vdom.rebuild())).unwrap();
|
||||
|
||||
// pin the futures so we can use select!
|
||||
pin_mut!(ws);
|
||||
|
||||
// send the initial render to the client
|
||||
ws.send(edits.into_bytes()).await?;
|
||||
let mut edit_channel = Channel::default();
|
||||
if let Some(edits) = {
|
||||
let mutations = vdom.rebuild();
|
||||
apply_edits(
|
||||
mutations,
|
||||
&mut edit_channel,
|
||||
&mut templates,
|
||||
&mut max_template_count,
|
||||
)
|
||||
} {
|
||||
// send the initial render to the client
|
||||
ws.send(edits).await?;
|
||||
}
|
||||
|
||||
// desktop uses this wrapper struct thing around the actual event itself
|
||||
// this is sorta driven by tao/wry
|
||||
@@ -164,7 +177,7 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li
|
||||
match evt.as_ref().map(|o| o.as_deref()) {
|
||||
// respond with a pong every ping to keep the websocket alive
|
||||
Some(Ok(b"__ping__")) => {
|
||||
ws.send(b"__pong__".to_vec()).await?;
|
||||
ws.send(text_frame("__pong__")).await?;
|
||||
}
|
||||
Some(Ok(evt)) => {
|
||||
if let Ok(message) = serde_json::from_str::<IpcMessage>(&String::from_utf8_lossy(evt)) {
|
||||
@@ -202,7 +215,7 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li
|
||||
|
||||
// handle any new queries
|
||||
Some(query) = query_rx.recv() => {
|
||||
ws.send(serde_json::to_string(&ClientUpdate::Query(query)).unwrap().into_bytes()).await?;
|
||||
ws.send(text_frame(&serde_json::to_string(&ClientUpdate::Query(query)).unwrap())).await?;
|
||||
}
|
||||
|
||||
Some(msg) = hot_reload_wait => {
|
||||
@@ -224,20 +237,156 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li
|
||||
.render_with_deadline(tokio::time::sleep(Duration::from_millis(10)))
|
||||
.await;
|
||||
|
||||
ws.send(
|
||||
serde_json::to_string(&ClientUpdate::Edits(edits))
|
||||
.unwrap()
|
||||
.into_bytes(),
|
||||
)
|
||||
.await?;
|
||||
if let Some(edits) = {
|
||||
apply_edits(
|
||||
edits,
|
||||
&mut edit_channel,
|
||||
&mut templates,
|
||||
&mut max_template_count,
|
||||
)
|
||||
} {
|
||||
ws.send(edits).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn text_frame(text: &str) -> Vec<u8> {
|
||||
let mut bytes = vec![0];
|
||||
bytes.extend(text.as_bytes());
|
||||
bytes
|
||||
}
|
||||
|
||||
fn add_template(
|
||||
template: &Template<'static>,
|
||||
channel: &mut Channel,
|
||||
templates: &mut FxHashMap<String, u16>,
|
||||
max_template_count: &mut u16,
|
||||
) {
|
||||
for root in template.roots.iter() {
|
||||
create_template_node(channel, root);
|
||||
templates.insert(template.name.to_owned(), *max_template_count);
|
||||
}
|
||||
channel.add_templates(*max_template_count, template.roots.len() as u16);
|
||||
|
||||
*max_template_count += 1
|
||||
}
|
||||
|
||||
fn create_template_node(channel: &mut Channel, v: &'static TemplateNode<'static>) {
|
||||
use TemplateNode::*;
|
||||
match v {
|
||||
Element {
|
||||
tag,
|
||||
namespace,
|
||||
attrs,
|
||||
children,
|
||||
..
|
||||
} => {
|
||||
// Push the current node onto the stack
|
||||
match namespace {
|
||||
Some(ns) => channel.create_element_ns(tag, ns),
|
||||
None => channel.create_element(tag),
|
||||
}
|
||||
// Set attributes on the current node
|
||||
for attr in *attrs {
|
||||
if let TemplateAttribute::Static {
|
||||
name,
|
||||
value,
|
||||
namespace,
|
||||
} = attr
|
||||
{
|
||||
channel.set_top_attribute(name, value, namespace.unwrap_or_default())
|
||||
}
|
||||
}
|
||||
// Add each child to the stack
|
||||
for child in *children {
|
||||
create_template_node(channel, child);
|
||||
}
|
||||
// Add all children to the parent
|
||||
channel.append_children_to_top(children.len() as u16);
|
||||
}
|
||||
Text { text } => channel.create_raw_text(text),
|
||||
DynamicText { .. } => channel.create_raw_text("p"),
|
||||
Dynamic { .. } => channel.add_placeholder(),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_edits(
|
||||
mutations: Mutations,
|
||||
channel: &mut Channel,
|
||||
templates: &mut FxHashMap<String, u16>,
|
||||
max_template_count: &mut u16,
|
||||
) -> Option<Vec<u8>> {
|
||||
use dioxus_core::Mutation::*;
|
||||
if mutations.templates.is_empty() && mutations.edits.is_empty() {
|
||||
return None;
|
||||
}
|
||||
for template in mutations.templates {
|
||||
add_template(&template, channel, templates, max_template_count);
|
||||
}
|
||||
for edit in mutations.edits {
|
||||
match edit {
|
||||
AppendChildren { id, m } => channel.append_children(id.0 as u32, m as u16),
|
||||
AssignId { path, id } => channel.assign_id(path, id.0 as u32),
|
||||
CreatePlaceholder { id } => channel.create_placeholder(id.0 as u32),
|
||||
CreateTextNode { value, id } => channel.create_text_node(value, id.0 as u32),
|
||||
HydrateText { path, value, id } => channel.hydrate_text(path, value, id.0 as u32),
|
||||
LoadTemplate { name, index, id } => {
|
||||
if let Some(tmpl_id) = templates.get(name) {
|
||||
channel.load_template(*tmpl_id, index as u16, id.0 as u32)
|
||||
}
|
||||
}
|
||||
ReplaceWith { id, m } => channel.replace_with(id.0 as u32, m as u16),
|
||||
ReplacePlaceholder { path, m } => channel.replace_placeholder(path, m as u16),
|
||||
InsertAfter { id, m } => channel.insert_after(id.0 as u32, m as u16),
|
||||
InsertBefore { id, m } => channel.insert_before(id.0 as u32, m as u16),
|
||||
SetAttribute {
|
||||
name,
|
||||
value,
|
||||
id,
|
||||
ns,
|
||||
} => match value {
|
||||
BorrowedAttributeValue::Text(txt) => {
|
||||
channel.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
|
||||
}
|
||||
BorrowedAttributeValue::Float(f) => {
|
||||
channel.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default())
|
||||
}
|
||||
BorrowedAttributeValue::Int(n) => {
|
||||
channel.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default())
|
||||
}
|
||||
BorrowedAttributeValue::Bool(b) => channel.set_attribute(
|
||||
id.0 as u32,
|
||||
name,
|
||||
if b { "true" } else { "false" },
|
||||
ns.unwrap_or_default(),
|
||||
),
|
||||
BorrowedAttributeValue::None => {
|
||||
channel.remove_attribute(id.0 as u32, name, ns.unwrap_or_default())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
SetText { value, id } => channel.set_text(id.0 as u32, value),
|
||||
NewEventListener { name, id, .. } => {
|
||||
channel.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8)
|
||||
}
|
||||
RemoveEventListener { name, id } => {
|
||||
channel.remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8)
|
||||
}
|
||||
Remove { id } => channel.remove(id.0 as u32),
|
||||
PushRoot { id } => channel.push_root(id.0 as u32),
|
||||
}
|
||||
}
|
||||
|
||||
// Add an extra one at the beginning to tell the shim this is a binary frame
|
||||
let mut bytes = vec![1];
|
||||
bytes.extend(channel.export_memory());
|
||||
channel.reset();
|
||||
Some(bytes)
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "type", content = "data")]
|
||||
enum ClientUpdate<'a> {
|
||||
#[serde(rename = "edits")]
|
||||
Edits(Mutations<'a>),
|
||||
enum ClientUpdate {
|
||||
#[serde(rename = "query")]
|
||||
Query(String),
|
||||
}
|
||||
Reference in new issue
Block a user