diff --git a/.github/workflows/cli_release.yml b/.github/workflows/cli_release.yml index c6c2b81e8..4cd33f47e 100644 --- a/.github/workflows/cli_release.yml +++ b/.github/workflows/cli_release.yml @@ -29,7 +29,7 @@ jobs: toolchain: "1.70.0", } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install stable uses: dtolnay/rust-toolchain@master with: diff --git a/.github/workflows/docs stable.yml b/.github/workflows/docs stable.yml index 069d4308a..642216f0f 100644 --- a/.github/workflows/docs stable.yml +++ b/.github/workflows/docs stable.yml @@ -23,7 +23,7 @@ jobs: - name: Setup mdBook run: | cargo install mdbook --git https://github.com/Demonthos/mdBook.git --branch master - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build run: cd docs && diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3a452e770..107ad9638 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -28,7 +28,7 @@ jobs: - name: Setup mdBook run: | cargo install mdbook --git https://github.com/Demonthos/mdBook.git --branch master - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build run: cd docs && diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 04f367b8e..037525557 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,7 +41,7 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: sudo apt-get update - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: cargo check --all --examples --tests test: @@ -56,7 +56,7 @@ jobs: - uses: davidB/rust-cargo-make@v1 - uses: browser-actions/setup-firefox@latest - uses: jetli/wasm-pack-action@v0.4.0 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: cargo make tests fmt: @@ -67,7 +67,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: rustup component add rustfmt - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: cargo fmt --all -- --check clippy: @@ -80,7 +80,7 @@ jobs: - run: sudo apt-get update - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev - run: rustup component add clippy - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: cargo clippy --workspace --examples --tests -- -D warnings matrix_test: @@ -124,7 +124,7 @@ jobs: } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: install stable uses: dtolnay/rust-toolchain@master @@ -154,7 +154,7 @@ jobs: # options: --security-opt seccomp=unconfined # steps: # - name: Checkout repository - # uses: actions/checkout@v3 + # uses: actions/checkout@v4 # - name: Generate code coverage # run: | # apt-get update &&\ diff --git a/.github/workflows/miri.yml b/.github/workflows/miri.yml index b1d772512..4a467b9b2 100644 --- a/.github/workflows/miri.yml +++ b/.github/workflows/miri.yml @@ -69,7 +69,7 @@ jobs: if: runner.os == 'Linux' run: echo "MIRIFLAGS=-Zmiri-tag-gc=1" >> $GITHUB_ENV - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust ${{ env.rust_nightly }} uses: dtolnay/rust-toolchain@master with: diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index dba32d257..500d41564 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: # Do our best to cache the toolchain and node install steps - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: node-version: 16 diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 2399c16b8..c83b1059a 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -691,7 +691,7 @@ impl<'a> IntoDynNode<'a> for &Element<'a> { impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> { fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> { - DynamicNode::Fragment(cx.bump().alloc([self.call(cx)])) + DynamicNode::Fragment(cx.bump().alloc([cx.render(self).unwrap()])) } } @@ -750,7 +750,7 @@ impl<'a> IntoTemplate<'a> for Element<'a> { } impl<'a, 'b> IntoTemplate<'a> for LazyNodes<'a, 'b> { fn into_template(self, cx: &'a ScopeState) -> VNode<'a> { - self.call(cx) + cx.render(self).unwrap() } } diff --git a/packages/html/src/elements.rs b/packages/html/src/elements.rs index 7e5bed124..7800369a9 100644 --- a/packages/html/src/elements.rs +++ b/packages/html/src/elements.rs @@ -1258,6 +1258,8 @@ builder_constructors! { spellcheck: BoolOrDefault DEFAULT, wrap: Wrap DEFAULT, value: String volatile, + + initial_value: String DEFAULT, }; diff --git a/packages/router-macro/src/lib.rs b/packages/router-macro/src/lib.rs index c949cf2f4..1078e5b9a 100644 --- a/packages/router-macro/src/lib.rs +++ b/packages/router-macro/src/lib.rs @@ -482,7 +482,8 @@ impl RouteEnum { let route = s; let (route, _hash) = route.split_once('#').unwrap_or((route, "")); let (route, query) = route.split_once('?').unwrap_or((route, "")); - let mut segments = route.split('/'); + let query = dioxus_router::exports::urlencoding::decode(query).unwrap_or(query.into()); + let mut segments = route.split('/').map(|s| dioxus_router::exports::urlencoding::decode(s).unwrap_or(s.into())); // skip the first empty segment if s.starts_with('/') { let _ = segments.next(); diff --git a/packages/router-macro/src/query.rs b/packages/router-macro/src/query.rs index 8b9db9e51..c97d3be29 100644 --- a/packages/router-macro/src/query.rs +++ b/packages/router-macro/src/query.rs @@ -14,7 +14,7 @@ impl QuerySegment { let ident = &self.ident; let ty = &self.ty; quote! { - let #ident = <#ty as dioxus_router::routable::FromQuery>::from_query(query); + let #ident = <#ty as dioxus_router::routable::FromQuery>::from_query(&*query); } } diff --git a/packages/router-macro/src/route_tree.rs b/packages/router-macro/src/route_tree.rs index 399d20e5e..6b89c80ab 100644 --- a/packages/router-macro/src/route_tree.rs +++ b/packages/router-macro/src/route_tree.rs @@ -312,7 +312,8 @@ impl<'a> RouteTreeSegmentData<'a> { quote! { { let mut segments = segments.clone(); - if let Some(segment) = segments.next() { + let segment = segments.next(); + if let Some(segment) = segment.as_deref() { if #segment == segment { #(#children)* } @@ -369,7 +370,7 @@ impl<'a> RouteTreeSegmentData<'a> { quote! { let mut trailing = String::from("/"); for seg in segments.clone() { - trailing += seg; + trailing += &*seg; trailing += "/"; } trailing.pop(); @@ -506,7 +507,9 @@ fn return_constructed( let remaining_segments = segments.clone(); let mut segments_clone = segments.clone(); let next_segment = segments_clone.next(); + let next_segment = next_segment.as_deref(); let segment_after_next = segments_clone.next(); + let segment_after_next = segment_after_next.as_deref(); match (next_segment, segment_after_next) { // This is the last segment, return the parsed route (None, _) | (Some(""), None) => { @@ -516,7 +519,7 @@ fn return_constructed( _ => { let mut trailing = String::new(); for seg in remaining_segments { - trailing += seg; + trailing += &*seg; trailing += "/"; } trailing.pop(); diff --git a/packages/router-macro/src/segment.rs b/packages/router-macro/src/segment.rs index 62dea59a7..e13141dc0 100644 --- a/packages/router-macro/src/segment.rs +++ b/packages/router-macro/src/segment.rs @@ -59,6 +59,7 @@ impl RouteSegment { { let mut segments = segments.clone(); let segment = segments.next(); + let segment = segment.as_deref(); let parsed = if let Some(#segment) = segment { Ok(()) } else { @@ -80,7 +81,8 @@ impl RouteSegment { quote! { { let mut segments = segments.clone(); - let parsed = if let Some(segment) = segments.next() { + let segment = segments.next(); + let parsed = if let Some(segment) = segment.as_deref() { <#ty as dioxus_router::routable::FromRouteSegment>::from_route_segment(segment).map_err(|err| #error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name(err))) } else { Err(#error_enum_name::#error_enum_varient(#inner_parse_enum::#missing_error_name)) @@ -100,9 +102,12 @@ impl RouteSegment { quote! { { let parsed = { - let mut segments = segments.clone(); - let segments: Vec<_> = segments.collect(); - <#ty as dioxus_router::routable::FromRouteSegments>::from_route_segments(&segments).map_err(|err| #error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name(err))) + let remaining_segments: Vec<_> = segments.collect(); + let mut new_segments: Vec<&str> = Vec::new(); + for segment in &remaining_segments { + new_segments.push(&*segment); + } + <#ty as dioxus_router::routable::FromRouteSegments>::from_route_segments(&new_segments).map_err(|err| #error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name(err))) }; match parsed { Ok(#name) => { diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index 46d7536b6..f8f967ef2 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -17,7 +17,7 @@ gloo = { version = "0.8.0", optional = true } log = { workspace = true } thiserror = { workspace = true } futures-util = { workspace = true } -serde_urlencoded = { version = "0.7.1", optional = true } +urlencoding = "2.1.3" serde = { version = "1", features = ["derive"], optional = true } url = "2.3.1" wasm-bindgen = { workspace = true, optional = true } diff --git a/packages/router/src/lib.rs b/packages/router/src/lib.rs index 49b7ffc42..a92394513 100644 --- a/packages/router/src/lib.rs +++ b/packages/router/src/lib.rs @@ -82,3 +82,8 @@ pub mod prelude { mod utils { pub(crate) mod use_router_internal; } + +#[doc(hidden)] +pub mod exports { + pub use urlencoding; +} diff --git a/packages/router/src/routable.rs b/packages/router/src/routable.rs index d9f5b2346..39a5245a9 100644 --- a/packages/router/src/routable.rs +++ b/packages/router/src/routable.rs @@ -34,7 +34,7 @@ pub trait FromQuery { impl From<&'a str>> FromQuery for T { fn from_query(query: &str) -> Self { - T::from(query) + T::from(&*urlencoding::decode(query).expect("Failed to decode url encoding")) } } @@ -54,10 +54,22 @@ where type Err = ::Err; fn from_route_segment(route: &str) -> Result { - T::from_str(route) + match urlencoding::decode(route) { + Ok(segment) => T::from_str(&segment), + Err(err) => { + log::error!("Failed to decode url encoding: {}", err); + T::from_str(route) + } + } } } +#[test] +fn full_circle() { + let route = "testing 1234 hello world"; + assert_eq!(String::from_route_segment(route).unwrap(), route); +} + /// Something that can be converted to route segments pub trait ToRouteSegments { /// Display the route segments @@ -71,12 +83,33 @@ where fn display_route_segements(self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for segment in self { write!(f, "/")?; - write!(f, "{}", segment)?; + let segment = segment.to_string(); + match urlencoding::decode(&segment) { + Ok(segment) => write!(f, "{}", segment)?, + Err(err) => { + log::error!("Failed to decode url encoding: {}", err); + write!(f, "{}", segment)? + } + } } Ok(()) } } +#[test] +fn to_route_segments() { + struct DisplaysRoute; + + impl std::fmt::Display for DisplaysRoute { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let segments = vec!["hello", "world"]; + segments.display_route_segements(f) + } + } + + assert_eq!(DisplaysRoute.to_string(), "/hello/world"); +} + /// Something that can be created from route segments pub trait FromRouteSegments: Sized { /// The error that can occur when parsing route segments diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 9cdabed3f..50d990cb2 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -252,7 +252,6 @@ impl WebsysDom { for id in to_mount { let node = dioxus_interpreter_js::get_node(id.0 as u32); if let Some(element) = node.dyn_ref::() { - log::info!("mounted event fired: {}", id.0); let data = PlatformEventData::new(Box::new(element.clone())); let _ = self.event_channel.unbounded_send(UiEvent { name: "mounted".to_string(),