From 5f7a6e2f3f75072e2dd76d5652597697e3a4593e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 31 Mar 2023 07:55:39 -0500 Subject: [PATCH 1/4] fix docs for the web cache setting --- packages/web/src/cfg.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/web/src/cfg.rs b/packages/web/src/cfg.rs index 7e47ed461..7483a7a8b 100644 --- a/packages/web/src/cfg.rs +++ b/packages/web/src/cfg.rs @@ -49,15 +49,16 @@ impl Config { /// Set the name of the element that Dioxus will use as the root. /// - /// This is akint to calling React.render() on the element with the specified name. + /// This is akin to calling React.render() on the element with the specified name. pub fn rootname(mut self, name: impl Into) -> Self { self.rootname = name.into(); self } - /// Set the name of the element that Dioxus will use as the root. + /// Sets a string cache for wasm bindgen to [intern](https://docs.rs/wasm-bindgen/0.2.84/wasm_bindgen/fn.intern.html). This can help reduce the time it takes for wasm bindgen to pass + /// strings from rust to javascript. This can significantly improve pefromance when passing strings to javascript, but can have a negative impact on startup time. /// - /// This is akint to calling React.render() on the element with the specified name. + /// > Currently this cache is only used when creating static elements and attributes. pub fn with_string_cache(mut self, cache: Vec) -> Self { self.cached_strings = cache; self From 073c4140d9547741f578ed999877ab25cc0c163a Mon Sep 17 00:00:00 2001 From: Andrew Collins <50530928+ProfXwing@users.noreply.github.com> Date: Wed, 5 Apr 2023 22:09:17 -0400 Subject: [PATCH 2/4] added web context back and updated wry to latest --- packages/desktop/Cargo.toml | 2 +- packages/desktop/src/cfg.rs | 5 +++-- packages/desktop/src/lib.rs | 6 ++++-- packages/desktop/src/protocol.rs | 13 ++++++++----- packages/desktop/src/webview.rs | 15 +++++++-------- 5 files changed, 23 insertions(+), 18 deletions(-) diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 7645e4de5..5722a42a2 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -21,7 +21,7 @@ serde = "1.0.136" serde_json = "1.0.79" thiserror = "1.0.30" log = "0.4.14" -wry = { version = "0.23.4" } +wry = { version = "0.27.2" } futures-channel = "0.3.21" tokio = { version = "1.16.1", features = [ "sync", diff --git a/packages/desktop/src/cfg.rs b/packages/desktop/src/cfg.rs index 7b37b46d4..d7f1b5dfa 100644 --- a/packages/desktop/src/cfg.rs +++ b/packages/desktop/src/cfg.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::path::PathBuf; use wry::application::window::Icon; @@ -28,7 +29,7 @@ type DropHandler = Box bool>; pub(crate) type WryProtocol = ( String, - Box>) -> WryResult>> + 'static>, + Box>) -> WryResult>> + 'static>, ); impl Config { @@ -98,7 +99,7 @@ impl Config { /// Set a custom protocol pub fn with_custom_protocol(mut self, name: String, handler: F) -> Self where - F: Fn(&HttpRequest>) -> WryResult>> + 'static, + F: Fn(&HttpRequest>) -> WryResult>> + 'static, { self.protocols.push((name, Box::new(handler))); self diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 75c67dd22..434cf3305 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -36,8 +36,8 @@ use tao::{ }; pub use wry; pub use wry::application as tao; -use wry::application::window::WindowId; use wry::webview::WebView; +use wry::{application::window::WindowId, webview::WebContext}; /// Launch the WebView and run the event loop. /// @@ -281,7 +281,7 @@ fn create_new_window( event_handlers: &WindowEventHandlers, shortcut_manager: ShortcutRegistry, ) -> WebviewHandler { - let webview = webview::build(&mut cfg, event_loop, proxy.clone()); + let (webview, web_context) = webview::build(&mut cfg, event_loop, proxy.clone()); dom.base_scope().provide_context(DesktopContext::new( webview.clone(), @@ -299,6 +299,7 @@ fn create_new_window( webview, dom, waker: waker::tao_waker(proxy, id), + web_context, } } @@ -306,6 +307,7 @@ struct WebviewHandler { dom: VirtualDom, webview: Rc, waker: Waker, + web_context: WebContext, } /// Poll the virtualdom until it's pending diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index f8c002a6a..efdc849a5 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -1,5 +1,8 @@ use dioxus_interpreter_js::INTERPRETER_JS; -use std::path::{Path, PathBuf}; +use std::{ + borrow::Cow, + path::{Path, PathBuf}, +}; use wry::{ http::{status::StatusCode, Request, Response}, Result, @@ -27,7 +30,7 @@ pub(super) fn desktop_handler( custom_head: Option, custom_index: Option, root_name: &str, -) -> Result>> { +) -> Result>> { // If the request is for the root, we'll serve the index.html file. if request.uri().path() == "/" { // If a custom index is provided, just defer to that, expecting the user to know what they're doing. @@ -53,7 +56,7 @@ pub(super) fn desktop_handler( return Response::builder() .header("Content-Type", "text/html") - .body(body) + .body(Cow::from(body)) .map_err(From::from); } @@ -72,13 +75,13 @@ pub(super) fn desktop_handler( if asset.exists() { return Response::builder() .header("Content-Type", get_mime_from_path(&asset)?) - .body(std::fs::read(asset)?) + .body(Cow::from(std::fs::read(asset)?)) .map_err(From::from); } Response::builder() .status(StatusCode::NOT_FOUND) - .body(String::from("Not Found").into_bytes()) + .body(Cow::from(String::from("Not Found").into_bytes())) .map_err(From::from) } diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 099a2bd94..c6909972f 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -7,13 +7,13 @@ use tao::event_loop::{EventLoopProxy, EventLoopWindowTarget}; pub use wry; pub use wry::application as tao; use wry::application::window::Window; -use wry::webview::{WebView, WebViewBuilder}; +use wry::webview::{WebContext, WebView, WebViewBuilder}; pub fn build( cfg: &mut Config, event_loop: &EventLoopWindowTarget, proxy: EventLoopProxy, -) -> Rc { +) -> (Rc, WebContext) { let builder = cfg.window.clone(); let window = builder.build(event_loop).unwrap(); let file_handler = cfg.file_drop_handler.take(); @@ -33,6 +33,8 @@ pub fn build( )); } + let mut web_context = WebContext::new(cfg.data_dir.clone()); + let mut webview = WebViewBuilder::new(window) .unwrap() .with_transparent(cfg.window.window.transparent) @@ -52,11 +54,8 @@ pub fn build( .as_ref() .map(|handler| handler(window, evet)) .unwrap_or_default() - }); - - // These are commented out because wry is currently broken in wry - // let mut web_context = WebContext::new(cfg.data_dir.clone()); - // .with_web_context(&mut web_context); + }) + .with_web_context(&mut web_context); for (name, handler) in cfg.protocols.drain(..) { webview = webview.with_custom_protocol(name, handler) @@ -82,5 +81,5 @@ pub fn build( webview = webview.with_devtools(true); } - Rc::new(webview.build().unwrap()) + (Rc::new(webview.build().unwrap()), web_context) } From fe75138e429fe3db055ca029ba9aad924eeba682 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 7 Apr 2023 09:35:32 -0500 Subject: [PATCH 3/4] fix dangerous_inner_html with SSR --- packages/ssr/src/cache.rs | 16 +++++++++++++++- packages/ssr/src/renderer.rs | 23 +++++++++++++++++++++-- packages/ssr/tests/inner_html.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 packages/ssr/tests/inner_html.rs diff --git a/packages/ssr/src/cache.rs b/packages/ssr/src/cache.rs index e261d3b65..a67fd5f22 100644 --- a/packages/ssr/src/cache.rs +++ b/packages/ssr/src/cache.rs @@ -23,6 +23,8 @@ pub enum Segment { // This will be true if there are static styles inside_style_tag: bool, }, + /// A marker for where to insert a dynamic inner html + InnerHtmlMarker, } impl std::fmt::Write for StringChain { @@ -69,6 +71,9 @@ impl StringCache { write!(chain, "<{tag}")?; // we need to collect the styles and write them at the end let mut styles = Vec::new(); + // we need to collect the inner html and write it at the end + let mut inner_html = None; + // we need to keep track of if we have dynamic attrs to know if we need to insert a style and inner_html marker let mut has_dynamic_attrs = false; for attr in *attrs { match attr { @@ -77,7 +82,9 @@ impl StringCache { value, namespace, } => { - if let Some("style") = namespace { + if *name == "dangerous_inner_html" { + inner_html = Some(value); + } else if let Some("style") = namespace { styles.push((name, value)); } else { write!(chain, " {name}=\"{value}\"")?; @@ -110,6 +117,13 @@ impl StringCache { write!(chain, "/>")?; } else { write!(chain, ">")?; + // Write the static inner html, or insert a marker if dynamic inner html is possible + if let Some(inner_html) = inner_html { + chain.write_str(inner_html)?; + } else if has_dynamic_attrs { + chain.segments.push(Segment::InnerHtmlMarker); + } + for child in *children { Self::recurse(child, cur_path, root_idx, chain)?; } diff --git a/packages/ssr/src/renderer.rs b/packages/ssr/src/renderer.rs index be4025dba..cfa08aee4 100644 --- a/packages/ssr/src/renderer.rs +++ b/packages/ssr/src/renderer.rs @@ -70,6 +70,8 @@ impl Renderer { .or_insert_with(|| Rc::new(StringCache::from_template(template).unwrap())) .clone(); + let mut inner_html = None; + // We need to keep track of the dynamic styles so we can insert them into the right place let mut accumulated_dynamic_styles = Vec::new(); @@ -77,7 +79,9 @@ impl Renderer { match segment { Segment::Attr(idx) => { let attr = &template.dynamic_attrs[*idx]; - if attr.namespace == Some("style") { + if attr.name == "dangerous_inner_html" { + inner_html = Some(attr); + } else if attr.namespace == Some("style") { accumulated_dynamic_styles.push(attr); } else { match attr.value { @@ -165,6 +169,19 @@ impl Renderer { accumulated_dynamic_styles.clear(); } } + + Segment::InnerHtmlMarker => { + if let Some(inner_html) = inner_html.take() { + let inner_html = &inner_html.value; + match inner_html { + AttributeValue::Text(value) => write!(buf, "{}", value)?, + AttributeValue::Bool(value) => write!(buf, "{}", value)?, + AttributeValue::Float(f) => write!(buf, "{}", f)?, + AttributeValue::Int(i) => write!(buf, "{}", i)?, + _ => {} + } + } + } } } @@ -208,7 +225,9 @@ fn to_string_works() { StyleMarker { inside_style_tag: false, }, - PreRendered(">Hello world 1 -->".into(),), + PreRendered(">".into()), + InnerHtmlMarker, + PreRendered("Hello world 1 -->".into(),), Node(0,), PreRendered( "<-- Hello world 2
nest 1
nest 2
".into(), diff --git a/packages/ssr/tests/inner_html.rs b/packages/ssr/tests/inner_html.rs new file mode 100644 index 000000000..11fb94746 --- /dev/null +++ b/packages/ssr/tests/inner_html.rs @@ -0,0 +1,26 @@ +use dioxus::prelude::*; + +#[test] +fn static_inner_html() { + fn app(cx: Scope) -> Element { + render! { div { dangerous_inner_html: "
1234
" } } + } + + let mut dom = VirtualDom::new(app); + _ = dom.rebuild(); + + assert_eq!(dioxus_ssr::render(&dom), r#"
1234
"#); +} + +#[test] +fn dynamic_inner_html() { + fn app(cx: Scope) -> Element { + let inner_html = "
1234
"; + render! { div { dangerous_inner_html: "{inner_html}" } } + } + + let mut dom = VirtualDom::new(app); + _ = dom.rebuild(); + + assert_eq!(dioxus_ssr::render(&dom), r#"
1234
"#); +} From 9571adea3068bee5f62e2229004cb873e8dcabaa Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 9 Apr 2023 13:42:40 -0700 Subject: [PATCH 4/4] add counter example --- examples/counter.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 examples/counter.rs diff --git a/examples/counter.rs b/examples/counter.rs new file mode 100644 index 000000000..f51083a95 --- /dev/null +++ b/examples/counter.rs @@ -0,0 +1,36 @@ +//! Comparison example with leptos' counter example +//! https://github.com/leptos-rs/leptos/blob/main/examples/counters/src/lib.rs + +use dioxus::prelude::*; + +fn main() { + dioxus_desktop::launch(app); +} + +fn app(cx: Scope) -> Element { + let counters = use_state(cx, || vec![0, 0, 0]); + let sum: usize = counters.iter().copied().sum(); + + render! { + div { + button { onclick: move |_| counters.make_mut().push(0), "Add counter" } + button { onclick: move |_| { counters.make_mut().pop(); }, "Remove counter" } + p { "Total: {sum}" } + for (i, counter) in counters.iter().enumerate() { + li { + button { onclick: move |_| counters.make_mut()[i] -= 1, "-1" } + input { + value: "{counter}", + oninput: move |e| { + if let Ok(value) = e.value.parse::() { + counters.make_mut()[i] = value; + } + } + } + button { onclick: move |_| counters.make_mut()[i] += 1, "+1" } + button { onclick: move |_| { counters.make_mut().remove(i); }, "x" } + } + } + } + } +}