From f3af2ad9ea3dc9d291c43fd13d3550bf4cfe2e52 Mon Sep 17 00:00:00 2001 From: Maccesch Date: Sat, 21 Oct 2023 15:21:11 -0500 Subject: [PATCH] fixed some SSR issues with not using use_window() in some functions --- CHANGELOG.md | 7 +++++ Cargo.toml | 2 +- examples/ssr/Cargo.toml | 2 +- examples/ssr/src/main.rs | 6 ++-- examples/use_draggable/src/main.rs | 7 +++-- examples/use_event_listener/src/main.rs | 4 +-- src/core/mod.rs | 2 ++ src/core/ssr_safe_method.rs | 19 ++++++++++++ src/use_active_element.rs | 6 ++-- src/use_breakpoints.rs | 4 +-- src/use_document.rs | 17 +++++++---- src/use_draggable.rs | 15 ++++------ src/use_mouse.rs | 40 ++++++++----------------- src/use_window.rs | 15 +++++++--- src/utils/is.rs | 6 ++-- 15 files changed, 88 insertions(+), 64 deletions(-) create mode 100644 src/core/ssr_safe_method.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 65d75ca6..c37f1ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.7.2] - 2023-10-21 + +### Fixes 🍕 + +- Some functions still used `window()` which could lead to panics in SSR. This is now fixed. + Specifically for `use_draggable`. + ## [0.7.1] - 2023-10-02 ### New Function 🚀 diff --git a/Cargo.toml b/Cargo.toml index 2747c15a..ee22cf86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "leptos-use" -version = "0.7.1" +version = "0.7.2" edition = "2021" authors = ["Marc-Stefan Cassola"] categories = ["gui", "web-programming"] diff --git a/examples/ssr/Cargo.toml b/examples/ssr/Cargo.toml index 7b30d871..5010f37a 100644 --- a/examples/ssr/Cargo.toml +++ b/examples/ssr/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "start-axum" +name = "leptos-use-ssr" version = "0.1.0" edition = "2021" diff --git a/examples/ssr/src/main.rs b/examples/ssr/src/main.rs index 6b924044..c08d00e6 100644 --- a/examples/ssr/src/main.rs +++ b/examples/ssr/src/main.rs @@ -5,8 +5,8 @@ async fn main() { use leptos::logging::log; use leptos::*; use leptos_axum::{generate_route_list, LeptosRoutes}; - use start_axum::app::*; - use start_axum::fileserv::file_and_error_handler; + use leptos_use_ssr::app::*; + use leptos_use_ssr::fileserv::file_and_error_handler; simple_logger::init_with_level(log::Level::Debug).expect("couldn't initialize logging"); @@ -18,7 +18,7 @@ async fn main() { let conf = get_configuration(None).await.unwrap(); let leptos_options = conf.leptos_options; let addr = leptos_options.site_addr; - let routes = generate_route_list(|| view! { }).await; + let routes = generate_route_list(|| view! { }); // build our application with a route let app = Router::new() diff --git a/examples/use_draggable/src/main.rs b/examples/use_draggable/src/main.rs index cf92cdb1..9ed1bfb6 100644 --- a/examples/use_draggable/src/main.rs +++ b/examples/use_draggable/src/main.rs @@ -2,13 +2,16 @@ use leptos::html::Div; use leptos::*; use leptos_use::core::Position; use leptos_use::docs::demo_or_body; -use leptos_use::{use_draggable_with_options, UseDraggableOptions, UseDraggableReturn}; +use leptos_use::{use_draggable_with_options, use_window, UseDraggableOptions, UseDraggableReturn}; #[component] fn Demo() -> impl IntoView { let el = create_node_ref::
(); - let inner_width = window().inner_width().unwrap().as_f64().unwrap(); + let inner_width = use_window() + .as_ref() + .map(|w| w.inner_width().unwrap().as_f64().unwrap()) + .unwrap_or(0.0); let UseDraggableReturn { x, y, style, .. } = use_draggable_with_options( el, diff --git a/examples/use_event_listener/src/main.rs b/examples/use_event_listener/src/main.rs index eb2b22dc..76c2f62a 100644 --- a/examples/use_event_listener/src/main.rs +++ b/examples/use_event_listener/src/main.rs @@ -2,11 +2,11 @@ use leptos::ev::{click, keydown}; use leptos::html::A; use leptos::logging::log; use leptos::*; -use leptos_use::use_event_listener; +use leptos_use::{use_event_listener, use_window}; #[component] fn Demo() -> impl IntoView { - let _ = use_event_listener(window(), keydown, |evt| { + let _ = use_event_listener(use_window(), keydown, |evt| { log!("window keydown: '{}'", evt.key()); }); diff --git a/src/core/mod.rs b/src/core/mod.rs index 2a8c2e0b..ea1f1b74 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -5,6 +5,7 @@ mod maybe_rw_signal; mod pointer_type; mod position; mod size; +mod ssr_safe_method; mod storage; pub use connection_ready_state::*; @@ -14,4 +15,5 @@ pub use maybe_rw_signal::*; pub use pointer_type::*; pub use position::*; pub use size::*; +pub(crate) use ssr_safe_method::*; pub use storage::*; diff --git a/src/core/ssr_safe_method.rs b/src/core/ssr_safe_method.rs new file mode 100644 index 00000000..9ffa513f --- /dev/null +++ b/src/core/ssr_safe_method.rs @@ -0,0 +1,19 @@ +macro_rules! impl_ssr_safe_method { + ( + $(#[$attr:meta])* + $method:ident(&self$(, $p_name:ident: $p_ty:ty)*) -> $return_ty:ty + $(; $($post_fix:tt)+)? + ) => { + $(#[$attr])* + #[inline(always)] + pub fn $method(&self, $($p_name: $p_ty),*) -> $return_ty { + self.0.as_ref() + .map( + |w| w.$method($($p_name),*) + ) + $($($post_fix)+)? + } + }; +} + +pub(crate) use impl_ssr_safe_method; diff --git a/src/use_active_element.rs b/src/use_active_element.rs index 4a671b09..76da95ad 100644 --- a/src/use_active_element.rs +++ b/src/use_active_element.rs @@ -1,6 +1,6 @@ #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))] -use crate::{use_document, use_event_listener_with_options, UseEventListenerOptions}; +use crate::{use_document, use_event_listener_with_options, use_window, UseEventListenerOptions}; use leptos::ev::{blur, focus}; use leptos::html::{AnyElement, ToHtmlElement}; use leptos::*; @@ -45,7 +45,7 @@ pub fn use_active_element() -> Signal>> { let listener_options = UseEventListenerOptions::default().capture(true); let _ = use_event_listener_with_options( - window(), + use_window(), blur, move |event| { if event.related_target().is_some() { @@ -58,7 +58,7 @@ pub fn use_active_element() -> Signal>> { ); let _ = use_event_listener_with_options( - window(), + use_window(), focus, move |_| { set_active_element.update(|el| *el = get_active_element()); diff --git a/src/use_breakpoints.rs b/src/use_breakpoints.rs index 578c15fb..d05840f3 100644 --- a/src/use_breakpoints.rs +++ b/src/use_breakpoints.rs @@ -1,4 +1,4 @@ -use crate::use_media_query; +use crate::{use_media_query, use_window}; use leptos::logging::error; use leptos::*; use paste::paste; @@ -185,7 +185,7 @@ macro_rules! impl_cmp_reactively { impl UseBreakpointsReturn { fn match_(query: &str) -> bool { - if let Ok(Some(query_list)) = window().match_media(query) { + if let Ok(Some(query_list)) = use_window().match_media(query) { return query_list.matches(); } diff --git a/src/use_document.rs b/src/use_document.rs index 2a7a1bb2..d9afaac8 100644 --- a/src/use_document.rs +++ b/src/use_document.rs @@ -1,6 +1,7 @@ use cfg_if::cfg_if; use std::ops::Deref; +use crate::core::impl_ssr_safe_method; #[cfg(not(feature = "ssr"))] use leptos::*; @@ -46,11 +47,15 @@ impl Deref for UseDocument { } impl UseDocument { - pub fn body(&self) -> Option { - self.0.as_ref().and_then(|d| d.body()) - } + impl_ssr_safe_method!( + /// Returns `Some(Document)` in the Browser. `None` otherwise. + body(&self) -> Option; + .unwrap_or_default() + ); - pub fn active_element(&self) -> Option { - self.0.as_ref().and_then(|d| d.active_element()) - } + impl_ssr_safe_method!( + /// Returns the active (focused) `Some(web_sys::Element)` in the Browser. `None` otherwise. + active_element(&self) -> Option; + .unwrap_or_default() + ); } diff --git a/src/use_draggable.rs b/src/use_draggable.rs index b293626a..5954243a 100644 --- a/src/use_draggable.rs +++ b/src/use_draggable.rs @@ -1,5 +1,5 @@ use crate::core::{ElementMaybeSignal, MaybeRwSignal, PointerType, Position}; -use crate::{use_event_listener_with_options, UseEventListenerOptions}; +use crate::{use_event_listener_with_options, use_window, UseEventListenerOptions, UseWindow}; use default_struct_builder::DefaultBuilder; use leptos::ev::{pointerdown, pointermove, pointerup}; use leptos::*; @@ -52,8 +52,8 @@ where use_draggable_with_options::< El, T, - web_sys::EventTarget, - web_sys::EventTarget, + UseWindow, + web_sys::Window, web_sys::EventTarget, web_sys::EventTarget, >(target, UseDraggableOptions::default()) @@ -267,19 +267,14 @@ where } impl Default - for UseDraggableOptions< - web_sys::EventTarget, - web_sys::EventTarget, - web_sys::EventTarget, - web_sys::EventTarget, - > + for UseDraggableOptions { fn default() -> Self { Self { exact: MaybeSignal::default(), prevent_default: MaybeSignal::default(), stop_propagation: MaybeSignal::default(), - dragging_element: window().into(), + dragging_element: use_window(), handle: None, pointer_types: vec![PointerType::Mouse, PointerType::Touch, PointerType::Pen], initial_value: MaybeRwSignal::default(), diff --git a/src/use_mouse.rs b/src/use_mouse.rs index 5e4e17b2..f81741a8 100644 --- a/src/use_mouse.rs +++ b/src/use_mouse.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))] use crate::core::{ElementMaybeSignal, Position}; -use crate::{use_event_listener_with_options, UseEventListenerOptions}; +use crate::{use_event_listener_with_options, use_window, UseEventListenerOptions, UseWindow}; use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use leptos::ev::{dragover, mousemove, touchend, touchmove, touchstart}; @@ -208,8 +208,7 @@ where /// Options for [`use_mouse_with_options`]. pub struct UseMouseOptions where - El: Clone, - El: Into>, + El: Clone + Into>, T: Into + Clone + 'static, Ex: UseMouseEventExtractor + Clone, { @@ -232,33 +231,18 @@ where _marker: PhantomData, } -cfg_if! { if #[cfg(feature = "ssr")] { - impl Default for UseMouseOptions, web_sys::Window, UseMouseEventExtractorDefault> { - fn default() -> Self { - Self { - coord_type: UseMouseCoordType::::default(), - target: None, - touch: true, - reset_on_touch_ends: false, - initial_value: Position { x: 0.0, y: 0.0 }, - _marker: Default::default(), - } - } - } -} else { - impl Default for UseMouseOptions { - fn default() -> Self { - Self { - coord_type: UseMouseCoordType::::default(), - target: window(), - touch: true, - reset_on_touch_ends: false, - initial_value: Position { x: 0.0, y: 0.0 }, - _marker: Default::default(), - } +impl Default for UseMouseOptions { + fn default() -> Self { + Self { + coord_type: UseMouseCoordType::::default(), + target: use_window(), + touch: true, + reset_on_touch_ends: false, + initial_value: Position { x: 0.0, y: 0.0 }, + _marker: PhantomData, } } -}} +} /// Defines how to get the coordinates from the event. #[derive(Clone)] diff --git a/src/use_window.rs b/src/use_window.rs index b9dae5a9..b0185f85 100644 --- a/src/use_window.rs +++ b/src/use_window.rs @@ -1,3 +1,4 @@ +use crate::core::impl_ssr_safe_method; use crate::{use_document, UseDocument}; use cfg_if::cfg_if; use std::ops::Deref; @@ -48,14 +49,20 @@ impl Deref for UseWindow { } impl UseWindow { - /// Returns the `Some(Navigator)` in the Browser. `None` otherwise. - pub fn navigator(&self) -> Option { - self.0.as_ref().map(|w| w.navigator()) - } + impl_ssr_safe_method!( + /// Returns `Some(Navigator)` in the Browser. `None` otherwise. + navigator(&self) -> Option + ); /// Returns the same as [`use_document`]. #[inline(always)] pub fn document(&self) -> UseDocument { use_document() } + + impl_ssr_safe_method!( + /// Returns the same as `window().match_media()` in the Browser. `Ok(None)` otherwise. + match_media(&self, query: &str) -> Result, wasm_bindgen::JsValue>; + .unwrap_or(Ok(None)) + ); } diff --git a/src/utils/is.rs b/src/utils/is.rs index 2fb045cd..36558837 100644 --- a/src/utils/is.rs +++ b/src/utils/is.rs @@ -1,8 +1,10 @@ +use crate::use_window; use lazy_static::lazy_static; -use leptos::*; lazy_static! { - pub static ref IS_IOS: bool = if let Ok(user_agent) = window().navigator().user_agent() { + pub static ref IS_IOS: bool = if let Some(Ok(user_agent)) = + use_window().navigator().map(|n| n.user_agent()) + { user_agent.contains("iPhone") || user_agent.contains("iPad") || user_agent.contains("iPod") } else { false