restore liveview renderer

This commit is contained in:
Evan Almloff 2024-01-15 11:28:27 -06:00
parent 41d10075e3
commit 9a38331826
4 changed files with 32 additions and 158 deletions

View file

@ -27,6 +27,11 @@ impl MutationState {
bytes
}
pub fn write_memory_into(&mut self, buffer: &mut Vec<u8>) {
buffer.extend(self.channel.export_memory());
self.channel.reset();
}
pub fn channel(&mut self) -> &mut Channel {
&mut self.channel
}
@ -167,7 +172,7 @@ impl WriteMutations for MutationState {
self.channel
.remove_attribute(id.0 as u32, name, ns.unwrap_or_default())
}
_ => unreachable!(),
_ => unreachable!("Any attributes are not supported by the current renderer"),
}
}

View file

@ -1,7 +1,7 @@
#![allow(clippy::await_holding_refcell_ref)]
use async_trait::async_trait;
use dioxus_core::prelude::{consume_context, provide_context};
use dioxus_core::ScopeId;
use dioxus_html::prelude::{EvalError, EvalProvider, Evaluator};
use std::{cell::RefCell, rc::Rc};
@ -9,9 +9,9 @@ use crate::query::{Query, QueryEngine};
/// Provides the DesktopEvalProvider through [`cx.provide_context`].
pub fn init_eval() {
let query = consume_context::<QueryEngine>().unwrap();
let query = ScopeId::ROOT.consume_context::<QueryEngine>().unwrap();
let provider: Rc<dyn EvalProvider> = Rc::new(DesktopEvalProvider { query });
provide_context(provider);
ScopeId::ROOT.provide_context(provider);
}
/// Reprents the desktop-target's provider of evaluators.

View file

@ -25,6 +25,7 @@ pub mod adapters {
pub use rocket_adapter::*;
}
#[allow(unused_imports)]
pub use adapters::*;
mod element;

View file

@ -5,11 +5,10 @@ use crate::{
query::{QueryEngine, QueryResult},
LiveViewError,
};
use dioxus_core::{prelude::*, Attribute, AttributeValue, Mutations};
use dioxus_html::{event_bubbles, EventData, HtmlEvent, MountedData, PlatformEventData};
use dioxus_interpreter_js::binary_protocol::Channel;
use dioxus_core::prelude::*;
use dioxus_html::{EventData, HtmlEvent, MountedData, PlatformEventData};
use dioxus_interpreter_js::MutationState;
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;
@ -38,12 +37,12 @@ impl LiveViewPool {
pub async fn launch(
&self,
ws: impl LiveViewSocket,
app: fn(()) -> Element,
app: fn() -> Element,
) -> Result<(), LiveViewError> {
self.launch_with_props(ws, app, ()).await
self.launch_with_props(ws, |app| app(), app).await
}
pub async fn launch_with_props<T: Send + 'static>(
pub async fn launch_with_props<T: Clone + Send + 'static>(
&self,
ws: impl LiveViewSocket,
app: fn(T) -> Element,
@ -126,27 +125,22 @@ 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;
let mut mutations = MutationState::default();
// 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.in_runtime(|| {
ScopeId::ROOT.provide_context(query_engine.clone());
init_eval();
});
// pin the futures so we can use select!
pin_mut!(ws);
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,
)
vdom.rebuild(&mut mutations);
take_edits(&mut mutations)
} {
// send the initial render to the client
ws.send(edits).await?;
@ -233,18 +227,13 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li
}
}
let edits = vdom
.render_with_deadline(tokio::time::sleep(Duration::from_millis(10)))
.await;
vdom.render_with_deadline(
tokio::time::sleep(Duration::from_millis(10)),
&mut mutations,
)
.await;
if let Some(edits) = {
apply_edits(
edits,
&mut edit_channel,
&mut templates,
&mut max_template_count,
)
} {
if let Some(edits) = take_edits(&mut mutations) {
ws.send(edits).await?;
}
}
@ -256,132 +245,11 @@ fn text_frame(text: &str) -> Vec<u8> {
bytes
}
fn add_template(
template: &Template,
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) {
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),
}
}
fn take_edits(mutations: &mut MutationState) -> Option<Vec<u8>> {
// 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)
mutations.write_memory_into(&mut bytes);
(bytes.len() > 1).then_some(bytes)
}
#[derive(Serialize)]