diff --git a/Cargo.toml b/Cargo.toml index 08c2a24..179e113 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,14 +26,15 @@ http1 = { version = "1", optional = true, package = "http" } http0_2 = { version = "0.2", optional = true, package = "http" } js-sys = "0.3" lazy_static = "1" -leptos = { version = "0.7.0-preview2", path = "../leptos/leptos" } -leptos_axum = { version = "0.7.0-preview2", optional = true } +leptos = { version = "0.7.0-alpha" } +leptos_axum = { version = "0.7.0-alpha", optional = true } leptos_actix = { version = "0.6", optional = true } leptos-spin = { version = "0.2", optional = true } num = { version = "0.4", optional = true } paste = "1" prost = { version = "0.12", optional = true } rmp-serde = { version = "1.1", optional = true } +send_wrapper = "0.6.0" serde = { version = "1", optional = true } serde_json = { version = "1", optional = true } thiserror = "1" @@ -133,7 +134,7 @@ features = [ [dev-dependencies] getrandom = { version = "0.2", features = ["js"] } -leptos_meta = "0.7.0-preview2" +leptos_meta = "0.7.0-alpha" rand = "0.8" [features] diff --git a/src/core/element_maybe_signal.rs b/src/core/element_maybe_signal.rs index 904e9e3..6187fd3 100644 --- a/src/core/element_maybe_signal.rs +++ b/src/core/element_maybe_signal.rs @@ -44,30 +44,16 @@ where } } -impl SignalGet for ElementMaybeSignal +impl DefinedAt for ElementMaybeSignal where T: Into + Clone + 'static, { - type Value = Option; - - fn get(&self) -> Option { - match self { - Self::Static(t) => t.clone(), - Self::Dynamic(s) => s.get(), - _ => unreachable!(), - } - } - - fn try_get(&self) -> Option> { - match self { - Self::Static(t) => Some(t.clone()), - Self::Dynamic(s) => s.try_get(), - _ => unreachable!(), - } + fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> { + None } } -impl SignalWith for ElementMaybeSignal +impl With for ElementMaybeSignal where T: Into + Clone + 'static, { @@ -90,7 +76,7 @@ where } } -impl SignalWithUntracked for ElementMaybeSignal +impl WithUntracked for ElementMaybeSignal where T: Into + Clone + 'static, { @@ -113,29 +99,6 @@ where } } -impl SignalGetUntracked for ElementMaybeSignal -where - T: Into + Clone + 'static, -{ - type Value = Option; - - fn get_untracked(&self) -> Option { - match self { - Self::Static(t) => t.clone(), - Self::Dynamic(s) => s.get_untracked(), - _ => unreachable!(), - } - } - - fn try_get_untracked(&self) -> Option> { - match self { - Self::Static(t) => Some(t.clone()), - Self::Dynamic(s) => s.try_get_untracked(), - _ => unreachable!(), - } - } -} - // From static element ////////////////////////////////////////////////////////////// impl From for ElementMaybeSignal diff --git a/src/core/elements_maybe_signal.rs b/src/core/elements_maybe_signal.rs index 2f5479e..d316944 100644 --- a/src/core/elements_maybe_signal.rs +++ b/src/core/elements_maybe_signal.rs @@ -45,30 +45,16 @@ where } } -impl SignalGet for ElementsMaybeSignal +impl DefinedAt for ElementsMaybeSignal where T: Into + Clone + 'static, { - type Value = Vec>; - - fn get(&self) -> Vec> { - match self { - Self::Static(v) => v.clone(), - Self::Dynamic(s) => s.get(), - Self::_Phantom(_) => unreachable!(), - } - } - - fn try_get(&self) -> Option>> { - match self { - Self::Static(v) => Some(v.clone()), - Self::Dynamic(s) => s.try_get(), - Self::_Phantom(_) => unreachable!(), - } + fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> { + None } } -impl SignalWith for ElementsMaybeSignal +impl With for ElementsMaybeSignal where T: Into + Clone + 'static, { @@ -91,7 +77,7 @@ where } } -impl SignalWithUntracked for ElementsMaybeSignal +impl WithUntracked for ElementsMaybeSignal where T: Into + Clone + 'static, { @@ -114,29 +100,6 @@ where } } -impl SignalGetUntracked for ElementsMaybeSignal -where - T: Into + Clone + 'static, -{ - type Value = Vec>; - - fn get_untracked(&self) -> Vec> { - match self { - Self::Static(t) => t.clone(), - Self::Dynamic(s) => s.get_untracked(), - Self::_Phantom(_) => unreachable!(), - } - } - - fn try_get_untracked(&self) -> Option>> { - match self { - Self::Static(t) => Some(t.clone()), - Self::Dynamic(s) => s.try_get_untracked(), - Self::_Phantom(_) => unreachable!(), - } - } -} - // From single static element ////////////////////////////////////////////////////////////// impl From for ElementsMaybeSignal diff --git a/src/core/maybe_rw_signal.rs b/src/core/maybe_rw_signal.rs index 867b21e..cc7cbd0 100644 --- a/src/core/maybe_rw_signal.rs +++ b/src/core/maybe_rw_signal.rs @@ -93,7 +93,7 @@ impl MaybeRwSignal { Self::DynamicRead(s) => { let (r, w) = signal(s.get_untracked()); - create_effect(move |_| { + Effect::new(move |_| { w.update(move |w| { *w = s.get(); }); diff --git a/src/core/use_rw_signal.rs b/src/core/use_rw_signal.rs index eef2675..a57ec23 100644 --- a/src/core/use_rw_signal.rs +++ b/src/core/use_rw_signal.rs @@ -37,49 +37,17 @@ impl Clone for UseRwSignal { impl Copy for UseRwSignal {} -impl SignalGet for UseRwSignal -where - T: Clone, -{ - type Value = T; - - fn get(&self) -> T { +impl DefinedAt for UseRwSignal { + fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> { match self { - Self::Separate(s, _) => s.get(), - Self::Combined(s) => s.get(), - } - } - - fn try_get(&self) -> Option { - match self { - Self::Separate(s, _) => s.try_get(), - Self::Combined(s) => s.try_get(), + Self::Combined(s) => s.defined_at(), + // NOTE: is this sufficient communication? + Self::Separate(_, s) => s.defined_at(), } } } -impl SignalGetUntracked for UseRwSignal -where - T: Clone, -{ - type Value = T; - - fn get_untracked(&self) -> T { - match self { - Self::Separate(s, _) => s.get_untracked(), - Self::Combined(s) => s.get_untracked(), - } - } - - fn try_get_untracked(&self) -> Option { - match self { - Self::Separate(s, _) => s.try_get_untracked(), - Self::Combined(s) => s.try_get_untracked(), - } - } -} - -impl SignalWith for UseRwSignal { +impl With for UseRwSignal { type Value = T; fn with(&self, f: impl FnOnce(&T) -> R) -> R { @@ -97,7 +65,7 @@ impl SignalWith for UseRwSignal { } } -impl SignalWithUntracked for UseRwSignal { +impl WithUntracked for UseRwSignal { type Value = T; fn with_untracked(&self, f: impl FnOnce(&T) -> R) -> R { @@ -115,7 +83,7 @@ impl SignalWithUntracked for UseRwSignal { } } -impl SignalSet for UseRwSignal { +impl Set for UseRwSignal { type Value = T; fn set(&self, new_value: T) { @@ -133,23 +101,7 @@ impl SignalSet for UseRwSignal { } } -impl SignalSetUntracked for UseRwSignal { - fn set_untracked(&self, new_value: T) { - match self { - Self::Separate(_, s) => s.set_untracked(new_value), - Self::Combined(s) => s.set_untracked(new_value), - } - } - - fn try_set_untracked(&self, new_value: T) -> Option { - match self { - Self::Separate(_, s) => s.try_set_untracked(new_value), - Self::Combined(s) => s.try_set_untracked(new_value), - } - } -} - -impl SignalUpdate for UseRwSignal { +impl Update for UseRwSignal { type Value = T; fn update(&self, f: impl FnOnce(&mut T)) { @@ -165,4 +117,18 @@ impl SignalUpdate for UseRwSignal { Self::Combined(s) => s.try_update(f), } } + + fn maybe_update(&self, fun: impl FnOnce(&mut Self::Value) -> bool) { + match self { + Self::Separate(_, s) => s.maybe_update(fun), + Self::Combined(s) => s.maybe_update(fun), + } + } + + fn try_maybe_update(&self, fun: impl FnOnce(&mut Self::Value) -> (bool, U)) -> Option { + match self { + Self::Separate(_, s) => s.try_maybe_update(fun), + Self::Combined(s) => s.try_maybe_update(fun), + } + } } diff --git a/src/on_click_outside.rs b/src/on_click_outside.rs index 7ac68c0..d3868c9 100644 --- a/src/on_click_outside.rs +++ b/src/on_click_outside.rs @@ -1,7 +1,6 @@ use crate::core::{ElementMaybeSignal, ElementsMaybeSignal}; use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; -use leptos::prelude::diagnostics::SpecialNonReactiveZone; cfg_if! { if #[cfg(not(feature = "ssr"))] { use leptos::prelude::*; @@ -115,6 +114,7 @@ where #[cfg(not(feature = "ssr"))] { + use leptos::prelude::diagnostics::SpecialNonReactiveZone; let OnClickOutsideOptions { ignore, capture, diff --git a/src/storage/use_local_storage.rs b/src/storage/use_local_storage.rs index 290facb..fb5f319 100644 --- a/src/storage/use_local_storage.rs +++ b/src/storage/use_local_storage.rs @@ -15,7 +15,7 @@ pub fn use_local_storage( key: impl AsRef, ) -> (Signal, WriteSignal, impl Fn() + Clone) where - T: Clone + Default + PartialEq, + T: Clone + Default + PartialEq + Send + Sync, C: StringCodec + Default, { use_storage_with_options( @@ -31,7 +31,7 @@ pub fn use_local_storage_with_options( options: UseStorageOptions, ) -> (Signal, WriteSignal, impl Fn() + Clone) where - T: Clone + PartialEq, + T: Clone + PartialEq + Send + Sync, C: StringCodec + Default, { use_storage_with_options(StorageType::Local, key, options) diff --git a/src/storage/use_session_storage.rs b/src/storage/use_session_storage.rs index f030036..1f6c1ab 100644 --- a/src/storage/use_session_storage.rs +++ b/src/storage/use_session_storage.rs @@ -15,7 +15,7 @@ pub fn use_session_storage( key: impl AsRef, ) -> (Signal, WriteSignal, impl Fn() + Clone) where - T: Clone + Default + PartialEq, + T: Clone + Default + PartialEq + Send + Sync, C: StringCodec + Default, { use_storage_with_options( @@ -31,7 +31,7 @@ pub fn use_session_storage_with_options( options: UseStorageOptions, ) -> (Signal, WriteSignal, impl Fn() + Clone) where - T: Clone + PartialEq, + T: Clone + PartialEq + Send + Sync, C: StringCodec + Default, { use_storage_with_options(StorageType::Session, key, options) diff --git a/src/storage/use_storage.rs b/src/storage/use_storage.rs index 97df6a9..8e673d7 100644 --- a/src/storage/use_storage.rs +++ b/src/storage/use_storage.rs @@ -5,7 +5,7 @@ use crate::{ use cfg_if::cfg_if; use leptos::prelude::wrappers::read::Signal; use leptos::prelude::*; -use std::rc::Rc; +use std::sync::Arc; use thiserror::Error; use wasm_bindgen::JsValue; @@ -93,7 +93,7 @@ pub fn use_storage( key: impl AsRef, ) -> (Signal, WriteSignal, impl Fn() + Clone) where - T: Default + Clone + PartialEq, + T: Default + Clone + PartialEq + Send + Sync, C: StringCodec + Default, { use_storage_with_options::(storage_type, key, UseStorageOptions::default()) @@ -106,7 +106,7 @@ pub fn use_storage_with_options( options: UseStorageOptions, ) -> (Signal, WriteSignal, impl Fn() + Clone) where - T: Clone + PartialEq, + T: Clone + PartialEq + Send + Sync, C: StringCodec + Default, { let UseStorageOptions { @@ -216,10 +216,10 @@ where fetch_from_storage(); // Fires when storage needs to be fetched - let notify = create_trigger(); + let notify = Trigger::new(); // Refetch from storage. Keeps track of how many times we've been notified. Does not increment for calls to set_data - let notify_id = create_memo::(move |prev| { + let notify_id = Memo::::new(move |prev| { notify.track(); match prev { None => 1, // Avoid async fetch of initial value @@ -334,7 +334,7 @@ pub struct UseStorageOptions> { // Translates to and from UTF-16 strings codec: C, // Callback for when an error occurs - on_error: Rc)>, + on_error: Arc)>, // Whether to continuously listen to changes from browser storage listen_to_storage_changes: bool, // Initial value to use when the storage key is not set @@ -346,7 +346,7 @@ pub struct UseStorageOptions> { /// Calls the on_error callback with the given error. Removes the error from the Result to avoid double error handling. #[cfg(not(feature = "ssr"))] fn handle_error( - on_error: &Rc)>, + on_error: &Arc)>, result: Result>, ) -> Result { result.map_err(|err| (on_error)(err)) @@ -356,7 +356,7 @@ impl + Default> Default for UseStorageOptions Self { Self { codec: C::default(), - on_error: Rc::new(|_err| ()), + on_error: Arc::new(|_err| ()), listen_to_storage_changes: true, initial_value: MaybeRwSignal::default(), filter: FilterOptions::default(), @@ -376,7 +376,7 @@ impl> UseStorageOptions { /// Optional callback whenever an error occurs. pub fn on_error(self, on_error: impl Fn(UseStorageError) + 'static) -> Self { Self { - on_error: Rc::new(on_error), + on_error: Arc::new(on_error), ..self } } diff --git a/src/use_color_mode.rs b/src/use_color_mode.rs index 3fb737f..a4d47bd 100644 --- a/src/use_color_mode.rs +++ b/src/use_color_mode.rs @@ -283,7 +283,7 @@ where on_changed(mode, Rc::new(default_on_changed.clone())); }; - create_effect({ + Effect::new({ let on_changed = on_changed.clone(); move |_| { diff --git a/src/use_cookie.rs b/src/use_cookie.rs index 1c40d88..b08baf5 100644 --- a/src/use_cookie.rs +++ b/src/use_cookie.rs @@ -628,7 +628,7 @@ fn read_cookies_string( let _ = ssr_cookies_header_getter; - let js_value: wasm_bindgen::JsValue = leptos::document().into(); + let js_value: wasm_bindgen::JsValue = document().into(); let document: web_sys::HtmlDocument = js_value.unchecked_into(); cookies = Some(document.cookie().unwrap_or_default()); } diff --git a/src/use_cycle_list.rs b/src/use_cycle_list.rs index a62d735..b7999c8 100644 --- a/src/use_cycle_list.rs +++ b/src/use_cycle_list.rs @@ -35,13 +35,13 @@ pub fn use_cycle_list( list: L, ) -> UseCycleListReturn< T, - impl Fn(usize) -> T + Clone, + impl Fn(usize) -> T + Clone + Send + Sync, impl Fn() + Clone, impl Fn() + Clone, impl Fn(i64) -> T + Clone, > where - T: Clone + PartialEq + 'static, + T: Clone + PartialEq + Send + Sync + 'static, L: Into>>, { use_cycle_list_with_options(list, UseCycleListOptions::default()) @@ -58,7 +58,7 @@ pub fn use_cycle_list_with_options( impl Fn(i64) -> T + Clone, > where - T: Clone + PartialEq + 'static, + T: Clone + PartialEq + Send + Sync + 'static, L: Into>>, { let UseCycleListOptions { diff --git a/src/use_debounce_fn.rs b/src/use_debounce_fn.rs index ceabb4d..9440c3d 100644 --- a/src/use_debounce_fn.rs +++ b/src/use_debounce_fn.rs @@ -1,8 +1,7 @@ pub use crate::utils::DebounceOptions; use crate::utils::{create_filter_wrapper, create_filter_wrapper_with_arg, debounce_filter}; -use leptos::MaybeSignal; -use std::cell::RefCell; -use std::rc::Rc; +use leptos::prelude::MaybeSignal; +use std::sync::{Arc, Mutex}; /// Debounce execution of a function. /// @@ -77,7 +76,7 @@ use std::rc::Rc; pub fn use_debounce_fn( func: F, ms: impl Into> + 'static, -) -> impl Fn() -> Rc>> + Clone +) -> impl Fn() -> Arc>> + Clone where F: Fn() -> R + Clone + 'static, R: 'static, @@ -90,19 +89,19 @@ pub fn use_debounce_fn_with_options( func: F, ms: impl Into> + 'static, options: DebounceOptions, -) -> impl Fn() -> Rc>> + Clone +) -> impl Fn() -> Arc>> + Clone where F: Fn() -> R + Clone + 'static, R: 'static, { - create_filter_wrapper(Rc::new(debounce_filter(ms, options)), func) + create_filter_wrapper(Arc::new(debounce_filter(ms, options)), func) } /// Version of [`use_debounce_fn`] with an argument for the debounced function. See the docs for [`use_debounce_fn`] for how to use. pub fn use_debounce_fn_with_arg( func: F, ms: impl Into> + 'static, -) -> impl Fn(Arg) -> Rc>> + Clone +) -> impl Fn(Arg) -> Arc>> + Clone where F: Fn(Arg) -> R + Clone + 'static, Arg: Clone + 'static, @@ -116,11 +115,11 @@ pub fn use_debounce_fn_with_arg_and_options( func: F, ms: impl Into> + 'static, options: DebounceOptions, -) -> impl Fn(Arg) -> Rc>> + Clone +) -> impl Fn(Arg) -> Arc>> + Clone where F: Fn(Arg) -> R + Clone + 'static, Arg: Clone + 'static, R: 'static, { - create_filter_wrapper_with_arg(Rc::new(debounce_filter(ms, options)), func) + create_filter_wrapper_with_arg(Arc::new(debounce_filter(ms, options)), func) } diff --git a/src/use_device_orientation.rs b/src/use_device_orientation.rs index 343a3eb..d7b1265 100644 --- a/src/use_device_orientation.rs +++ b/src/use_device_orientation.rs @@ -1,6 +1,5 @@ use cfg_if::cfg_if; use leptos::prelude::wrappers::read::Signal; -use leptos::prelude::*; /// Reactive [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent). /// Provide web developers with information from the physical orientation of @@ -42,6 +41,7 @@ pub fn use_device_orientation() -> UseDeviceOrientationReturn { let beta = || None; let gamma = || None; } else { + use leptos::prelude::*; use crate::{use_event_listener_with_options, UseEventListenerOptions, use_supported, js}; use leptos::ev::deviceorientation; @@ -67,7 +67,7 @@ pub fn use_device_orientation() -> UseDeviceOrientationReturn { .once(false), ); - leptos::on_cleanup(cleanup); + on_cleanup(cleanup); } }} diff --git a/src/use_device_pixel_ratio.rs b/src/use_device_pixel_ratio.rs index 41bc448..4e28eef 100644 --- a/src/use_device_pixel_ratio.rs +++ b/src/use_device_pixel_ratio.rs @@ -1,6 +1,5 @@ use cfg_if::cfg_if; use leptos::prelude::wrappers::read::Signal; -use leptos::prelude::*; /// Reactive [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) /// @@ -31,15 +30,17 @@ use leptos::prelude::*; /// On the server this function returns a Signal that is always `1.0`. pub fn use_device_pixel_ratio() -> Signal { cfg_if! { if #[cfg(feature = "ssr")] { + use leptos::prelude::*; let pixel_ratio = Signal::derive(|| 1.0); } else { use crate::{use_event_listener_with_options, UseEventListenerOptions}; + use leptos::prelude::*; use leptos::ev::change; let initial_pixel_ratio = window().device_pixel_ratio(); let (pixel_ratio, set_pixel_ratio) = signal(initial_pixel_ratio); - create_effect(move |_| { + Effect::new(move |_| { let media = window().match_media( &format!("(resolution: {}dppx)", pixel_ratio.get()) ).unwrap(); diff --git a/src/use_display_media.rs b/src/use_display_media.rs index 05f3f7e..a732f86 100644 --- a/src/use_display_media.rs +++ b/src/use_display_media.rs @@ -3,6 +3,7 @@ use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use leptos::prelude::wrappers::read::Signal; use leptos::prelude::*; +use send_wrapper::SendWrapper; use wasm_bindgen::{JsCast, JsValue}; /// Reactive [`mediaDevices.getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) streaming. @@ -55,7 +56,8 @@ pub fn use_display_media_with_options( let (enabled, set_enabled) = enabled.into_signal(); - let (stream, set_stream) = signal(None::>); + let (stream, set_stream) = + signal(None::, SendWrapper>>); let _start = move || async move { cfg_if! { if #[cfg(not(feature = "ssr"))] { @@ -63,7 +65,10 @@ pub fn use_display_media_with_options( return; } - let stream = create_media(audio).await; + let stream = create_media(audio) + .await + .map(SendWrapper::new) + .map_err(SendWrapper::new); set_stream.update(|s| *s = Some(stream)); } else { @@ -83,7 +88,7 @@ pub fn use_display_media_with_options( let start = move || { cfg_if! { if #[cfg(not(feature = "ssr"))] { - spawn_local(async move { + leptos::spawn::spawn_local(async move { _start().await; stream.with_untracked(move |stream| { if let Some(Ok(_)) = stream { @@ -103,7 +108,7 @@ pub fn use_display_media_with_options( move || enabled.get(), move |enabled, _, _| { if *enabled { - spawn_local(async move { + leptos::spawn::spawn_local(async move { _start().await; }); } else { @@ -176,7 +181,7 @@ where /// Initially this is `None` until `start` resolved successfully. /// In case the stream couldn't be started, for example because the user didn't grant permission, /// this has the value `Some(Err(...))`. - pub stream: Signal>>, + pub stream: Signal, SendWrapper>>>, /// Starts the screen streaming. Triggers the ask for permission if not already granted. pub start: StartFn, diff --git a/src/use_drop_zone.rs b/src/use_drop_zone.rs index caaa35a..6756866 100644 --- a/src/use_drop_zone.rs +++ b/src/use_drop_zone.rs @@ -1,7 +1,6 @@ use crate::core::ElementMaybeSignal; use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; -use leptos::prelude::diagnostics::SpecialNonReactiveZone; use leptos::prelude::wrappers::read::Signal; use leptos::prelude::*; use std::fmt::{Debug, Formatter}; @@ -78,6 +77,7 @@ where #[cfg(not(feature = "ssr"))] { + use leptos::prelude::diagnostics::SpecialNonReactiveZone; let UseDropZoneOptions { on_drop, on_enter, diff --git a/src/use_event_listener.rs b/src/use_event_listener.rs index 5600ec5..7dbc150 100644 --- a/src/use_event_listener.rs +++ b/src/use_event_listener.rs @@ -2,7 +2,6 @@ use crate::core::ElementMaybeSignal; use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use leptos::ev::EventDescriptor; -use leptos::prelude::diagnostics::SpecialNonReactiveZone; cfg_if! { if #[cfg(not(feature = "ssr"))] { use crate::{watch_with_options, WatchOptions}; @@ -120,6 +119,8 @@ where #[cfg(not(feature = "ssr"))] { + use leptos::prelude::diagnostics::SpecialNonReactiveZone; + use send_wrapper::SendWrapper; let event_name = event.name(); let closure_js = Closure::wrap(Box::new(move |e| { #[cfg(debug_assertions)] @@ -187,7 +188,8 @@ where cleanup_prev_element(); }; - on_cleanup(stop.clone()); + let cleanup_stop = SendWrapper::new(stop.clone()); + on_cleanup(move || cleanup_stop()); stop } diff --git a/src/use_event_source.rs b/src/use_event_source.rs index d5fe230..1c16671 100644 --- a/src/use_event_source.rs +++ b/src/use_event_source.rs @@ -5,9 +5,10 @@ use default_struct_builder::DefaultBuilder; use leptos::prelude::diagnostics::SpecialNonReactiveZone; use leptos::prelude::wrappers::read::Signal; use leptos::prelude::*; +use send_wrapper::SendWrapper; use std::cell::Cell; use std::marker::PhantomData; -use std::rc::Rc; +use std::sync::Arc; use std::time::Duration; use thiserror::Error; @@ -114,8 +115,9 @@ pub fn use_event_source( url: &str, ) -> UseEventSourceReturn where - T: Clone + PartialEq + 'static, + T: Clone + PartialEq + Send + Sync + 'static, C: StringCodec + Default, + C::Error: Send + Sync, { use_event_source_with_options(url, UseEventSourceOptions::::default()) } @@ -126,8 +128,9 @@ pub fn use_event_source_with_options( options: UseEventSourceOptions, ) -> UseEventSourceReturn where - T: Clone + PartialEq + 'static, + T: Clone + PartialEq + Send + Sync + 'static, C: StringCodec + Default, + C::Error: Send + Sync, { let UseEventSourceOptions { codec, @@ -142,14 +145,14 @@ where let url = url.to_owned(); - let (event, set_event) = signal(None::); + let (event, set_event) = signal(None::>); let (data, set_data) = signal(None::); let (ready_state, set_ready_state) = signal(ConnectionReadyState::Closed); - let (event_source, set_event_source) = signal(None::); + let (event_source, set_event_source) = signal(None::>); let (error, set_error) = signal(None::>); - let explicitly_closed = Rc::new(Cell::new(false)); - let retried = Rc::new(Cell::new(0)); + let explicitly_closed = Arc::new(Cell::new(false)); + let retried = Arc::new(Cell::new(0)); let set_data_from_string = move |data_string: Option| { if let Some(data_string) = data_string { @@ -161,7 +164,7 @@ where }; let close = { - let explicitly_closed = Rc::clone(&explicitly_closed); + let explicitly_closed = Arc::clone(&explicitly_closed); move || { if let Some(event_source) = event_source.get_untracked() { @@ -173,11 +176,11 @@ where } }; - let init = StoredValue::new(None::>); + let init = StoredValue::new(None::>); - init.set_value(Some(Rc::new({ - let explicitly_closed = Rc::clone(&explicitly_closed); - let retried = Rc::clone(&retried); + init.set_value(Some(Arc::new({ + let explicitly_closed = Arc::clone(&explicitly_closed); + let retried = Arc::clone(&retried); move || { use wasm_bindgen::prelude::*; @@ -194,7 +197,7 @@ where set_ready_state.set(ConnectionReadyState::Connecting); - set_event_source.set(Some(es.clone())); + set_event_source.set(Some(SendWrapper::new(es.clone()))); let on_open = Closure::wrap(Box::new(move |_: web_sys::Event| { set_ready_state.set(ConnectionReadyState::Open); @@ -204,14 +207,14 @@ where on_open.forget(); let on_error = Closure::wrap(Box::new({ - let explicitly_closed = Rc::clone(&explicitly_closed); - let retried = Rc::clone(&retried); - let on_failed = Rc::clone(&on_failed); + let explicitly_closed = Arc::clone(&explicitly_closed); + let retried = Arc::clone(&retried); + let on_failed = Arc::clone(&on_failed); let es = es.clone(); move |e: web_sys::Event| { set_ready_state.set(ConnectionReadyState::Closed); - set_error.set(Some(UseEventSourceError::Event(e))); + set_error.set(Some(UseEventSourceError::Event(SendWrapper::new(e)))); // only reconnect if EventSource isn't reconnecting by itself // this is the case when the connection is closed (readyState is 2) @@ -273,8 +276,8 @@ where { open = { let close = close.clone(); - let explicitly_closed = Rc::clone(&explicitly_closed); - let retried = Rc::clone(&retried); + let explicitly_closed = Arc::clone(&explicitly_closed); + let retried = Arc::clone(&retried); move || { close(); @@ -326,7 +329,7 @@ where reconnect_interval: u64, /// On maximum retry times reached. - on_failed: Rc, + on_failed: Arc, /// If `true` the `EventSource` connection will immediately be opened when calling this function. /// If `false` you have to manually call the `open` function. @@ -349,7 +352,7 @@ impl + Default> Default for UseEventSourceOptions { codec: C::default(), reconnect_limit: 3, reconnect_interval: 3000, - on_failed: Rc::new(|| {}), + on_failed: Arc::new(|| {}), immediate: true, named_events: vec![], with_credentials: false, @@ -361,8 +364,8 @@ impl + Default> Default for UseEventSourceOptions { /// Return type of [`use_event_source`]. pub struct UseEventSourceReturn where - Err: 'static, - T: Clone + 'static, + Err: Send + Sync + 'static, + T: Clone + Send + Sync + 'static, OpenFn: Fn() + Clone + 'static, CloseFn: Fn() + Clone + 'static, { @@ -373,7 +376,7 @@ where pub ready_state: Signal, /// The latest named event - pub event: Signal>, + pub event: Signal>>, /// The current error pub error: Signal>>, @@ -386,13 +389,13 @@ where pub close: CloseFn, /// The `EventSource` instance - pub event_source: Signal>, + pub event_source: Signal>>, } #[derive(Error, Debug)] pub enum UseEventSourceError { #[error("Error event: {0:?}")] - Event(web_sys::Event), + Event(SendWrapper), #[error("Error decoding value")] Deserialize(Err), diff --git a/src/use_geolocation.rs b/src/use_geolocation.rs index 0e583c1..5fbe920 100644 --- a/src/use_geolocation.rs +++ b/src/use_geolocation.rs @@ -2,6 +2,7 @@ use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use leptos::prelude::wrappers::read::Signal; use leptos::prelude::*; +use send_wrapper::SendWrapper; /// Reactive [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API). /// It allows the user to provide their location to web applications if they so desire. For privacy reasons, @@ -43,8 +44,8 @@ pub fn use_geolocation_with_options( options: UseGeolocationOptions, ) -> UseGeolocationReturn { let (located_at, set_located_at) = signal(None::); - let (error, set_error) = signal(None::); - let (coords, set_coords) = signal(None::); + let (error, set_error) = signal(None::>); + let (coords, set_coords) = signal(None::>); cfg_if! { if #[cfg(feature = "ssr")] { let resume = || (); @@ -56,24 +57,23 @@ pub fn use_geolocation_with_options( let _ = set_coords; } else { use crate::use_window; - use std::cell::Cell; - use std::rc::Rc; use wasm_bindgen::prelude::*; + use std::sync::{Arc, Mutex}; let update_position = move |position: web_sys::Position| { set_located_at.set(Some(position.timestamp())); - set_coords.set(Some(position.coords())); + set_coords.set(Some(SendWrapper::new(position.coords()))); set_error.set(None); }; let on_error = move |err: web_sys::PositionError| { - set_error.set(Some(err)); + set_error.set(Some(SendWrapper::new(err))); }; - let watch_handle = Rc::new(Cell::new(None::)); + let watch_handle = Arc::new(Mutex::new(None::)); let resume = { - let watch_handle = Rc::clone(&watch_handle); + let watch_handle = Arc::clone(&watch_handle); let position_options = options.as_position_options(); move || { @@ -85,15 +85,14 @@ pub fn use_geolocation_with_options( let on_error = Closure::wrap(Box::new(on_error) as Box); - watch_handle.replace( + *watch_handle.lock().unwrap() = geolocation .watch_position_with_error_callback_and_options( update_position.as_ref().unchecked_ref(), Some(on_error.as_ref().unchecked_ref()), &position_options, ) - .ok(), - ); + .ok(); update_position.forget(); on_error.forget(); @@ -107,12 +106,12 @@ pub fn use_geolocation_with_options( } let pause = { - let watch_handle = Rc::clone(&watch_handle); + let watch_handle = Arc::clone(&watch_handle); move || { let navigator = use_window().navigator(); if let Some(navigator) = navigator { - if let Some(handle) = watch_handle.take() { + if let Some(handle) = *watch_handle.lock().unwrap() { if let Ok(geolocation) = navigator.geolocation() { geolocation.clear_watch(handle); } @@ -204,13 +203,13 @@ where { /// The coordinates of the current device like latitude and longitude. /// See [`GeolocationCoordinates`](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationCoordinates).. - pub coords: Signal>, + pub coords: Signal>>, /// The timestamp of the current coordinates. pub located_at: Signal>, /// The last error received from `navigator.geolocation`. - pub error: Signal>, + pub error: Signal>>, /// Resume the geolocation watch. pub resume: ResumeFn, diff --git a/src/use_infinite_scroll.rs b/src/use_infinite_scroll.rs index 5bc0c59..3239ac0 100644 --- a/src/use_infinite_scroll.rs +++ b/src/use_infinite_scroll.rs @@ -158,7 +158,7 @@ where set_loading.set(true); let measure = measure.clone(); - spawn_local(async move { + leptos::spawn::spawn_local(async move { #[cfg(debug_assertions)] let zone = SpecialNonReactiveZone::enter(); diff --git a/src/use_intersection_observer.rs b/src/use_intersection_observer.rs index 8764c3c..7e3bcea 100644 --- a/src/use_intersection_observer.rs +++ b/src/use_intersection_observer.rs @@ -102,7 +102,7 @@ where let closure_js = Closure::::new( move |entries: js_sys::Array, observer| { #[cfg(debug_assertions)] - let _z = SpecialNonReactiveZone::enter(); + let _z = leptos::prelude::diagnostics::SpecialNonReactiveZone::enter(); callback( entries diff --git a/src/use_media_query.rs b/src/use_media_query.rs index 7700248..f3ad681 100644 --- a/src/use_media_query.rs +++ b/src/use_media_query.rs @@ -91,7 +91,7 @@ pub fn use_media_query(query: impl Into>) -> Signal { listener.replace(Rc::new(move |_| update()) as Rc); } - create_effect(move |_| update()); + Effect::new(move |_| update()); on_cleanup(cleanup); }} diff --git a/src/use_permission.rs b/src/use_permission.rs index 31f5f58..a3f1e24 100644 --- a/src/use_permission.rs +++ b/src/use_permission.rs @@ -46,12 +46,12 @@ pub fn use_permission(permission_name: &str) -> Signal { } }; - spawn_local({ + leptos::spawn::spawn_local({ let permission_name = permission_name.to_owned(); async move { if let Ok(status) = query_permission(permission_name).await { - let _ = use_event_listener(status.clone(), ev::change, { + let _ = use_event_listener(status.clone(), leptos::ev::change, { let on_change = on_change.clone(); move |_| on_change() }); diff --git a/src/use_preferred_dark.rs b/src/use_preferred_dark.rs index d34f4cd..b6dd60a 100644 --- a/src/use_preferred_dark.rs +++ b/src/use_preferred_dark.rs @@ -1,5 +1,4 @@ use crate::use_media_query; -use leptos::prelude::wrappers::read::Signal; use leptos::prelude::*; /// Reactive [dark theme preference](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). diff --git a/src/use_raf_fn.rs b/src/use_raf_fn.rs index 0f3ea2e..45fce03 100644 --- a/src/use_raf_fn.rs +++ b/src/use_raf_fn.rs @@ -138,7 +138,8 @@ pub fn use_raf_fn_with_options( resume(); } - on_cleanup(pause.clone()); + let pause_cleanup = send_wrapper::SendWrapper::new(pause.clone()); + on_cleanup(move || pause_cleanup()); Pausable { resume, diff --git a/src/use_scroll.rs b/src/use_scroll.rs index 87b7d5e..d2ac791 100644 --- a/src/use_scroll.rs +++ b/src/use_scroll.rs @@ -11,6 +11,7 @@ use crate::use_event_listener::use_event_listener_with_options; use crate::{ use_debounce_fn_with_arg, use_throttle_fn_with_arg_and_options, ThrottleOptions, }; +use leptos::ev; use leptos::ev::scrollend; use wasm_bindgen::JsCast; @@ -202,13 +203,13 @@ where let (is_scrolling, set_is_scrolling) = signal(false); - let arrived_state = create_rw_signal(Directions { + let arrived_state = RwSignal::new(Directions { left: true, right: false, top: true, bottom: false, }); - let directions = create_rw_signal(Directions { + let directions = RwSignal::new(Directions { left: false, right: false, top: false, diff --git a/src/use_service_worker.rs b/src/use_service_worker.rs index 14682ec..508b3d6 100644 --- a/src/use_service_worker.rs +++ b/src/use_service_worker.rs @@ -3,7 +3,8 @@ use leptos::prelude::actions::Action; use leptos::prelude::diagnostics::SpecialNonReactiveZone; use leptos::prelude::wrappers::read::Signal; use leptos::prelude::*; -use std::rc::Rc; +use send_wrapper::SendWrapper; +use std::sync::Arc; use wasm_bindgen::{prelude::Closure, JsCast, JsValue}; use web_sys::ServiceWorkerRegistration; @@ -73,20 +74,21 @@ pub fn use_service_worker_with_options( create_or_update_registration.dispatch(ServiceWorkerScriptUrl(options.script_url.to_string())); // And parse the result into individual signals. - let registration: Signal> = - Signal::derive(move || { - let a = get_registration.value().get(); - let b = create_or_update_registration.value().get(); - // We only dispatch create_or_update_registration once. - // Whenever we manually re-fetched the registration, the result of that has precedence! - match a { - Some(res) => res.map_err(ServiceWorkerRegistrationError::Js), - None => match b { - Some(res) => res.map_err(ServiceWorkerRegistrationError::Js), - None => Err(ServiceWorkerRegistrationError::NeverQueried), - }, - } - }); + let registration: Signal< + Result, ServiceWorkerRegistrationError>, + > = Signal::derive(move || { + let a = get_registration.value().get(); + let b = create_or_update_registration.value().get(); + // We only dispatch create_or_update_registration once. + // Whenever we manually re-fetched the registration, the result of that has precedence! + match a { + Some(res) => res.map_err(ServiceWorkerRegistrationError::Js), + None => match b { + Some(res) => res.map_err(|e| ServiceWorkerRegistrationError::Js(e)), + None => Err(ServiceWorkerRegistrationError::NeverQueried), + }, + } + }); let fetch_registration = Closure::wrap(Box::new(move |_event: JsValue| { get_registration.dispatch(()); @@ -182,7 +184,7 @@ pub struct UseServiceWorkerOptions { /// What should happen when a new service worker was activated? /// The default implementation reloads the current page. - on_controller_change: Rc, + on_controller_change: Arc, } impl Default for UseServiceWorkerOptions { @@ -190,7 +192,7 @@ impl Default for UseServiceWorkerOptions { Self { script_url: "service-worker.js".into(), skip_waiting_message: "skipWaiting".into(), - on_controller_change: Rc::new(move || { + on_controller_change: Arc::new(move || { use std::ops::Deref; if let Some(window) = use_window().deref() { if let Err(err) = window.location().reload() { @@ -211,7 +213,8 @@ where SkipFn: Fn() + Clone, { /// The current registration state. - pub registration: Signal>, + pub registration: + Signal, ServiceWorkerRegistrationError>>, /// Whether a SW is currently installing. pub installing: Signal, @@ -234,29 +237,37 @@ struct ServiceWorkerScriptUrl(pub String); #[derive(Debug, Clone)] pub enum ServiceWorkerRegistrationError { - Js(JsValue), + Js(SendWrapper), NeverQueried, } /// A leptos action which asynchronously checks for ServiceWorker updates, given an existing ServiceWorkerRegistration. -fn create_action_update( -) -> Action> { - Action::new(move |registration: &ServiceWorkerRegistration| { - let registration = registration.clone(); - async move { - match registration.update() { - Ok(promise) => js_fut!(promise) - .await - .and_then(|ok| ok.dyn_into::()), - Err(err) => Err(err), +fn create_action_update() -> Action< + SendWrapper, + Result, SendWrapper>, +> { + Action::new( + move |registration: &SendWrapper| { + let registration = registration.clone(); + async move { + match registration.update() { + Ok(promise) => js_fut!(promise) + .await + .and_then(|ok| ok.dyn_into::()) + .map(SendWrapper::new) + .map_err(SendWrapper::new), + Err(err) => Err(err), + } } - } - }) + }, + ) } /// A leptos action which asynchronously creates or updates and than retrieves the ServiceWorkerRegistration. -fn create_action_create_or_update_registration( -) -> Action> { +fn create_action_create_or_update_registration() -> Action< + ServiceWorkerScriptUrl, + Result, SendWrapper>, +> { Action::new(move |script_url: &ServiceWorkerScriptUrl| { let script_url = script_url.0.to_owned(); async move { @@ -272,14 +283,17 @@ fn create_action_create_or_update_registration( } /// A leptos action which asynchronously fetches the current ServiceWorkerRegistration. -fn create_action_get_registration() -> Action<(), Result> { +fn create_action_get_registration( +) -> Action<(), Result, SendWrapper>> { Action::new(move |(): &()| async move { if let Some(navigator) = use_window().navigator() { js_fut!(navigator.service_worker().get_registration()) .await .and_then(|ok| ok.dyn_into::()) + .map(SendWrapper::new) + .map_err(SendWrapper::new) } else { - Err(JsValue::from_str("no navigator")) + Err(SendWrapper::new(JsValue::from_str("no navigator"))) } }) } diff --git a/src/use_supported.rs b/src/use_supported.rs index 1ffa7b8..b121e49 100644 --- a/src/use_supported.rs +++ b/src/use_supported.rs @@ -1,4 +1,3 @@ -use leptos::prelude::wrappers::read::Signal; use leptos::prelude::*; /// SSR compatibe `is_supported` diff --git a/src/use_throttle_fn.rs b/src/use_throttle_fn.rs index 8483c24..03edc8f 100644 --- a/src/use_throttle_fn.rs +++ b/src/use_throttle_fn.rs @@ -1,7 +1,6 @@ use crate::utils::{create_filter_wrapper, create_filter_wrapper_with_arg, throttle_filter}; -use leptos::MaybeSignal; -use std::cell::RefCell; -use std::rc::Rc; +use leptos::prelude::MaybeSignal; +use std::sync::{Arc, Mutex}; pub use crate::utils::ThrottleOptions; @@ -73,7 +72,7 @@ pub use crate::utils::ThrottleOptions; pub fn use_throttle_fn( func: F, ms: impl Into> + 'static, -) -> impl Fn() -> Rc>> + Clone +) -> impl Fn() -> Arc>> + Clone where F: Fn() -> R + Clone + 'static, R: 'static, @@ -86,19 +85,19 @@ pub fn use_throttle_fn_with_options( func: F, ms: impl Into> + 'static, options: ThrottleOptions, -) -> impl Fn() -> Rc>> + Clone +) -> impl Fn() -> Arc>> + Clone where F: Fn() -> R + Clone + 'static, R: 'static, { - create_filter_wrapper(Rc::new(throttle_filter(ms, options)), func) + create_filter_wrapper(Arc::new(throttle_filter(ms, options)), func) } /// Version of [`use_throttle_fn`] with an argument for the throttled function. See the docs for [`use_throttle_fn`] for how to use. pub fn use_throttle_fn_with_arg( func: F, ms: impl Into> + 'static, -) -> impl Fn(Arg) -> Rc>> + Clone +) -> impl Fn(Arg) -> Arc>> + Clone where F: Fn(Arg) -> R + Clone + 'static, Arg: Clone + 'static, @@ -112,11 +111,11 @@ pub fn use_throttle_fn_with_arg_and_options( func: F, ms: impl Into> + 'static, options: ThrottleOptions, -) -> impl Fn(Arg) -> Rc>> + Clone +) -> impl Fn(Arg) -> Arc>> + Clone where F: Fn(Arg) -> R + Clone + 'static, Arg: Clone + 'static, R: 'static, { - create_filter_wrapper_with_arg(Rc::new(throttle_filter(ms, options)), func) + create_filter_wrapper_with_arg(Arc::new(throttle_filter(ms, options)), func) } diff --git a/src/use_timeout_fn.rs b/src/use_timeout_fn.rs index 49e6180..558f93b 100644 --- a/src/use_timeout_fn.rs +++ b/src/use_timeout_fn.rs @@ -2,9 +2,8 @@ use leptos::leptos_dom::helpers::TimeoutHandle; use leptos::prelude::diagnostics::SpecialNonReactiveZone; use leptos::prelude::wrappers::read::Signal; use leptos::prelude::*; -use std::cell::Cell; use std::marker::PhantomData; -use std::rc::Rc; +use std::sync::{Arc, Mutex}; use std::time::Duration; /// Wrapper for `setTimeout` with controls. @@ -46,13 +45,14 @@ where let (is_pending, set_pending) = signal(false); - let timer = Rc::new(Cell::new(None::)); + let timer = Arc::new(Mutex::new(None::)); let clear = { - let timer = Rc::clone(&timer); + let timer = Arc::clone(&timer); move || { - if let Some(timer) = timer.take() { + let timer = timer.lock().unwrap(); + if let Some(timer) = *timer { timer.clear(); } } @@ -68,7 +68,7 @@ where }; let start = { - let timer = Rc::clone(&timer); + let timer = Arc::clone(&timer); let callback = callback.clone(); move |arg: Arg| { @@ -76,12 +76,12 @@ where let handle = set_timeout_with_handle( { - let timer = Rc::clone(&timer); + let timer = Arc::clone(&timer); let callback = callback.clone(); move || { set_pending.set(false); - timer.set(None); + *timer.lock().unwrap() = None; #[cfg(debug_assertions)] let _z = SpecialNonReactiveZone::enter(); @@ -93,7 +93,7 @@ where ) .ok(); - timer.set(handle); + *timer.lock().unwrap() = handle; } }; diff --git a/src/use_web_notification.rs b/src/use_web_notification.rs index 908b075..5d183c3 100644 --- a/src/use_web_notification.rs +++ b/src/use_web_notification.rs @@ -1,9 +1,8 @@ use crate::{use_supported, use_window}; use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; -use leptos::prelude::diagnostics::SpecialNonReactiveZone; -use leptos::prelude::wrappers::read::Signal; -use leptos::prelude::*; +use leptos::prelude::{wrappers::read::Signal, *}; +use send_wrapper::SendWrapper; use std::rc::Rc; /// Reactive [Notification API](https://developer.mozilla.org/en-US/docs/Web/API/Notification). @@ -53,7 +52,7 @@ pub fn use_web_notification_with_options( ) -> UseWebNotificationReturn { let is_supported = use_supported(browser_supports_notifications); - let (notification, set_notification) = signal(None::); + let (notification, set_notification) = signal(None::>); let (permission, set_permission) = signal(NotificationPermission::default()); @@ -65,6 +64,7 @@ pub fn use_web_notification_with_options( let show = move |_: ShowOptions| (); let close = move || (); } else { + use leptos::{spawn::spawn_local, prelude::diagnostics::SpecialNonReactiveZone}; use crate::use_event_listener; use leptos::ev::visibilitychange; use wasm_bindgen::closure::Closure; @@ -462,7 +462,7 @@ where CloseFn: Fn() + Clone, { pub is_supported: Signal, - pub notification: Signal>, + pub notification: Signal>>, pub show: ShowFn, pub close: CloseFn, pub permission: Signal, diff --git a/src/use_websocket.rs b/src/use_websocket.rs index f33e5a1..90f965b 100644 --- a/src/use_websocket.rs +++ b/src/use_websocket.rs @@ -2,8 +2,8 @@ use cfg_if::cfg_if; use leptos::{leptos_dom::helpers::TimeoutHandle, prelude::*}; -use std::cell::Cell; -use std::rc::Rc; +use send_wrapper::SendWrapper; +use std::sync::{atomic::AtomicBool, Arc}; use std::time::Duration; use crate::core::ConnectionReadyState; @@ -100,16 +100,16 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket}; /// /// ``` /// # use leptos::prelude::*; -/// use std::rc::Rc; +/// use std::sync::Arc; /// /// #[derive(Clone)] /// pub struct WebsocketContext { /// pub message: Signal>, -/// send: Rc, // use Rc to make it easily cloneable +/// send: Arc, // use Arc to make it easily cloneable /// } /// /// impl WebsocketContext { -/// pub fn new(message: Signal>, send: Rc) -> Self { +/// pub fn new(message: Signal>, send: Arc) -> Self { /// Self { /// message, /// send, @@ -129,15 +129,15 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket}; /// ``` /// # use leptos::prelude::*; /// # use leptos_use::{use_websocket, UseWebsocketReturn}; -/// # use std::rc::Rc; +/// # use std::sync::Arc; /// # #[derive(Clone)] /// # pub struct WebsocketContext { /// # pub message: Signal>, -/// # send: Rc, +/// # send: Arc, /// # } /// # /// # impl WebsocketContext { -/// # pub fn new(message: Signal>, send: Rc) -> Self { +/// # pub fn new(message: Signal>, send: Arc) -> Self { /// # Self { /// # message, /// # send, @@ -153,7 +153,7 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket}; /// .. /// } = use_websocket("ws:://some.websocket.io"); /// -/// provide_context(WebsocketContext::new(message, Rc::new(send.clone()))); +/// provide_context(WebsocketContext::new(message, Arc::new(send.clone()))); /// # /// # view! {} /// # } @@ -164,11 +164,11 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket}; /// ``` /// # use leptos::prelude::*; /// # use leptos_use::{use_websocket, UseWebsocketReturn}; -/// # use std::rc::Rc; +/// # use std::sync::Arc; /// # #[derive(Clone)] /// # pub struct WebsocketContext { /// # pub message: Signal>, -/// # send: Rc, +/// # send: Arc, /// # } /// # /// # impl WebsocketContext { @@ -228,26 +228,27 @@ pub fn use_websocket_with_options( let (ready_state, set_ready_state) = signal(ConnectionReadyState::Closed); let (message, set_message) = signal(None); let (message_bytes, set_message_bytes) = signal(None); - let ws_ref: StoredValue> = StoredValue::new(None); + let ws_ref: StoredValue>> = StoredValue::new(None); let reconnect_timer_ref: StoredValue> = StoredValue::new(None); let reconnect_times_ref: StoredValue = StoredValue::new(0); - let unmounted = Rc::new(Cell::new(false)); + let unmounted = Arc::new(AtomicBool::new(false)); - let connect_ref: StoredValue>> = StoredValue::new(None); + let connect_ref: StoredValue>> = StoredValue::new(None); #[cfg(not(feature = "ssr"))] { - let reconnect_ref: StoredValue>> = StoredValue::new(None); + let reconnect_ref: StoredValue>> = + StoredValue::new(None); reconnect_ref.set_value({ let ws = ws_ref.get_value(); - Some(Rc::new(move || { + Some(Arc::new(move || { if reconnect_times_ref.get_value() < reconnect_limit - && ws - .clone() - .map_or(false, |ws: WebSocket| ws.ready_state() != WebSocket::OPEN) + && ws.clone().map_or(false, |ws: SendWrapper| { + ws.ready_state() != WebSocket::OPEN + }) { reconnect_timer_ref.set_value( set_timeout_with_handle( @@ -267,9 +268,9 @@ pub fn use_websocket_with_options( connect_ref.set_value({ let ws = ws_ref.get_value(); - let unmounted = Rc::clone(&unmounted); + let unmounted = Arc::clone(&unmounted); - Some(Rc::new(move || { + Some(Arc::new(move || { reconnect_timer_ref.set_value(None); if let Some(web_socket) = &ws { @@ -294,8 +295,8 @@ pub fn use_websocket_with_options( // onopen handler { - let unmounted = Rc::clone(&unmounted); - let on_open = Rc::clone(&on_open); + let unmounted = Arc::clone(&unmounted); + let on_open = Arc::clone(&on_open); let onopen_closure = Closure::wrap(Box::new(move |e: Event| { if unmounted.get() { @@ -303,7 +304,7 @@ pub fn use_websocket_with_options( } #[cfg(debug_assertions)] - let zone = SpecialNonReactiveZone::enter(); + let zone = diagnostics::SpecialNonReactiveZone::enter(); on_open(e); @@ -320,9 +321,9 @@ pub fn use_websocket_with_options( // onmessage handler { - let unmounted = Rc::clone(&unmounted); - let on_message = Rc::clone(&on_message); - let on_message_bytes = Rc::clone(&on_message_bytes); + let unmounted = Arc::clone(&unmounted); + let on_message = Arc::clone(&on_message); + let on_message_bytes = Arc::clone(&on_message_bytes); let onmessage_closure = Closure::wrap(Box::new(move |e: MessageEvent| { if unmounted.get() { @@ -342,7 +343,7 @@ pub fn use_websocket_with_options( let txt = String::from(&txt); #[cfg(debug_assertions)] - let zone = SpecialNonReactiveZone::enter(); + let zone = diagnostics::SpecialNonReactiveZone::enter(); on_message(txt.clone()); @@ -358,7 +359,7 @@ pub fn use_websocket_with_options( let array = array.to_vec(); #[cfg(debug_assertions)] - let zone = SpecialNonReactiveZone::enter(); + let zone = diagnostics::SpecialNonReactiveZone::enter(); on_message_bytes(array.clone()); @@ -376,8 +377,8 @@ pub fn use_websocket_with_options( // onerror handler { - let unmounted = Rc::clone(&unmounted); - let on_error = Rc::clone(&on_error); + let unmounted = Arc::clone(&unmounted); + let on_error = Arc::clone(&on_error); let onerror_closure = Closure::wrap(Box::new(move |e: Event| { if unmounted.get() { @@ -389,7 +390,7 @@ pub fn use_websocket_with_options( } #[cfg(debug_assertions)] - let zone = SpecialNonReactiveZone::enter(); + let zone = diagnostics::SpecialNonReactiveZone::enter(); on_error(e); @@ -405,8 +406,8 @@ pub fn use_websocket_with_options( // onclose handler { - let unmounted = Rc::clone(&unmounted); - let on_close = Rc::clone(&on_close); + let unmounted = Arc::clone(&unmounted); + let on_close = Arc::clone(&on_close); let onclose_closure = Closure::wrap(Box::new(move |e: CloseEvent| { if unmounted.get() { @@ -418,7 +419,7 @@ pub fn use_websocket_with_options( } #[cfg(debug_assertions)] - let zone = SpecialNonReactiveZone::enter(); + let zone = diagnostics::SpecialNonReactiveZone::enter(); on_close(e); @@ -486,7 +487,7 @@ pub fn use_websocket_with_options( // clean up (unmount) on_cleanup(move || { - unmounted.set(true); + unmounted.store(true, std::sync::atomic::Ordering::Relaxed); close(); }); @@ -506,15 +507,15 @@ pub fn use_websocket_with_options( #[derive(DefaultBuilder)] pub struct UseWebSocketOptions { /// `WebSocket` connect callback. - on_open: Rc, + on_open: Arc, /// `WebSocket` message callback for text. - on_message: Rc, + on_message: Arc, /// `WebSocket` message callback for binary. - on_message_bytes: Rc)>, + on_message_bytes: Arc) + Send + Sync>, /// `WebSocket` error callback. - on_error: Rc, + on_error: Arc, /// `WebSocket` close callback. - on_close: Rc, + on_close: Arc, /// Retry times. Defaults to 3. reconnect_limit: u64, /// Retry interval in ms. Defaults to 3000. @@ -530,11 +531,11 @@ pub struct UseWebSocketOptions { impl Default for UseWebSocketOptions { fn default() -> Self { Self { - on_open: Rc::new(|_| {}), - on_message: Rc::new(|_| {}), - on_message_bytes: Rc::new(|_| {}), - on_error: Rc::new(|_| {}), - on_close: Rc::new(|_| {}), + on_open: Arc::new(|_| {}), + on_message: Arc::new(|_| {}), + on_message_bytes: Arc::new(|_| {}), + on_error: Arc::new(|_| {}), + on_close: Arc::new(|_| {}), reconnect_limit: 3, reconnect_interval: 3000, immediate: true, @@ -559,7 +560,7 @@ where /// Latest binary message received from `WebSocket`. pub message_bytes: Signal>>, /// The `WebSocket` instance. - pub ws: Option, + pub ws: Option>, /// Opens the `WebSocket` connection pub open: OpenFn, /// Closes the `WebSocket` connection diff --git a/src/utils/filters/debounce.rs b/src/utils/filters/debounce.rs index ecc7db4..dc835e4 100644 --- a/src/utils/filters/debounce.rs +++ b/src/utils/filters/debounce.rs @@ -5,8 +5,7 @@ use default_struct_builder::DefaultBuilder; use leptos::leptos_dom::helpers::TimeoutHandle; use leptos::prelude::diagnostics::SpecialNonReactiveZone; use leptos::prelude::*; -use std::cell::{Cell, RefCell}; -use std::rc::Rc; +use std::sync::{Arc, Mutex}; use std::time::Duration; #[derive(Copy, Clone, DefaultBuilder, Default)] @@ -20,23 +19,24 @@ pub struct DebounceOptions { pub fn debounce_filter( ms: impl Into>, options: DebounceOptions, -) -> impl Fn(Rc R>) -> Rc>> + Clone +) -> impl Fn(Arc R>) -> Arc>> + Clone where R: 'static, { - let timer = Rc::new(Cell::new(None::)); - let max_timer = Rc::new(Cell::new(None::)); - let last_return_value: Rc>> = Rc::new(RefCell::new(None)); + let timer = Arc::new(Mutex::new(None::)); + let max_timer = Arc::new(Mutex::new(None::)); + let last_return_value: Arc>> = Arc::new(Mutex::new(None)); - let clear_timeout = move |timer: &Rc>>| { - if let Some(handle) = timer.get() { + let clear_timeout = move |timer: &Arc>>| { + let mut timer = timer.lock().unwrap(); + if let Some(handle) = *timer { handle.clear(); - timer.set(None); + *timer = None; } }; on_cleanup({ - let timer = Rc::clone(&timer); + let timer = Arc::clone(&timer); move || { clear_timeout(&timer); @@ -46,11 +46,11 @@ where let ms = ms.into(); let max_wait_signal = options.max_wait; - move |_invoke: Rc R>| { + move |_invoke: Arc R>| { let duration = ms.get_untracked(); let max_duration = max_wait_signal.get_untracked(); - let last_return_val = Rc::clone(&last_return_value); + let last_return_val = Arc::clone(&last_return_value); let invoke = move || { #[cfg(debug_assertions)] let zone = SpecialNonReactiveZone::enter(); @@ -60,7 +60,7 @@ where #[cfg(debug_assertions)] drop(zone); - let mut val_mut = last_return_val.borrow_mut(); + let mut val_mut = last_return_val.lock().unwrap(); *val_mut = Some(return_value); }; @@ -70,43 +70,41 @@ where clear_timeout(&max_timer); invoke(); - return Rc::clone(&last_return_value); + return Arc::clone(&last_return_value); } cfg_if! { if #[cfg(not(feature = "ssr"))] { // Create the max_timer. Clears the regular timer on invoke if let Some(max_duration) = max_duration { - if max_timer.get().is_none() { - let timer = Rc::clone(&timer); + let mut max_timer = max_timer.lock().unwrap(); + + if max_timer.is_none() { + let timer = Arc::clone(&timer); let invok = invoke.clone(); - max_timer.set( - set_timeout_with_handle( - move || { - clear_timeout(&timer); - invok(); - }, - Duration::from_millis(max_duration as u64), - ) - .ok(), - ); + *max_timer = set_timeout_with_handle( + move || { + clear_timeout(&timer); + invok(); + }, + Duration::from_millis(max_duration as u64), + ) + .ok(); } } - let max_timer = Rc::clone(&max_timer); + let max_timer = Arc::clone(&max_timer); // Create the regular timer. Clears the max timer on invoke - timer.set( - set_timeout_with_handle( - move || { - clear_timeout(&max_timer); - invoke(); - }, - Duration::from_millis(duration as u64), - ) - .ok(), - ); + *timer.lock().unwrap() = set_timeout_with_handle( + move || { + clear_timeout(&max_timer); + invoke(); + }, + Duration::from_millis(duration as u64), + ) + .ok(); }} - Rc::clone(&last_return_value) + Arc::clone(&last_return_value) } } diff --git a/src/utils/filters/mod.rs b/src/utils/filters/mod.rs index 4a8e5d3..27e689a 100644 --- a/src/utils/filters/mod.rs +++ b/src/utils/filters/mod.rs @@ -4,31 +4,30 @@ mod throttle; pub use debounce::*; pub use throttle::*; -use leptos::MaybeSignal; -use std::cell::RefCell; -use std::rc::Rc; +use leptos::prelude::MaybeSignal; +use std::sync::{Arc, Mutex}; -macro_rules! RcFilterFn { +macro_rules! ArcFilterFn { ($R:ident) => { - Rc $R>) -> Rc>>> + Arc $R>) -> Arc>>> } } pub fn create_filter_wrapper( - filter: RcFilterFn!(R), + filter: ArcFilterFn!(R), func: F, -) -> impl Fn() -> Rc>> + Clone +) -> impl Fn() -> Arc>> + Clone where F: Fn() -> R + Clone + 'static, R: 'static, { - move || Rc::clone(&filter)(Rc::new(func.clone())) + move || Arc::clone(&filter)(Arc::new(func.clone())) } pub fn create_filter_wrapper_with_arg( - filter: RcFilterFn!(R), + filter: ArcFilterFn!(R), func: F, -) -> impl Fn(Arg) -> Rc>> + Clone +) -> impl Fn(Arg) -> Arc>> + Clone where F: Fn(Arg) -> R + Clone + 'static, R: 'static, @@ -36,7 +35,7 @@ where { move |arg: Arg| { let func = func.clone(); - Rc::clone(&filter)(Rc::new(move || func(arg.clone()))) + Arc::clone(&filter)(Arc::new(move || func(arg.clone()))) } } @@ -70,15 +69,15 @@ impl FilterOptions { } } - pub fn filter_fn(&self) -> RcFilterFn!(R) + pub fn filter_fn(&self) -> ArcFilterFn!(R) where R: 'static, { match self { - FilterOptions::Debounce { ms, options } => Rc::new(debounce_filter(*ms, *options)), - FilterOptions::Throttle { ms, options } => Rc::new(throttle_filter(*ms, *options)), + FilterOptions::Debounce { ms, options } => Arc::new(debounce_filter(*ms, *options)), + FilterOptions::Throttle { ms, options } => Arc::new(throttle_filter(*ms, *options)), FilterOptions::None => { - Rc::new(|invoke: Rc R>| Rc::new(RefCell::new(Some(invoke())))) + Arc::new(|invoke: Arc R>| Arc::new(Mutex::new(Some(invoke())))) } } } diff --git a/src/utils/filters/throttle.rs b/src/utils/filters/throttle.rs index 9010d13..0227fce 100644 --- a/src/utils/filters/throttle.rs +++ b/src/utils/filters/throttle.rs @@ -6,9 +6,8 @@ use default_struct_builder::DefaultBuilder; use leptos::leptos_dom::helpers::TimeoutHandle; use leptos::prelude::diagnostics::SpecialNonReactiveZone; use leptos::prelude::*; -use std::cell::{Cell, RefCell}; use std::cmp::max; -use std::rc::Rc; +use std::sync::{atomic::AtomicBool, Arc, Mutex}; use std::time::Duration; #[derive(Copy, Clone, DefaultBuilder)] @@ -31,20 +30,21 @@ impl Default for ThrottleOptions { pub fn throttle_filter( ms: impl Into>, options: ThrottleOptions, -) -> impl Fn(Rc R>) -> Rc>> + Clone +) -> impl Fn(Arc R>) -> Arc>> + Clone where R: 'static, { - let last_exec = Rc::new(Cell::new(0_f64)); - let timer = Rc::new(Cell::new(None::)); - let is_leading = Rc::new(Cell::new(true)); - let last_return_value: Rc>> = Rc::new(RefCell::new(None)); + let last_exec = Arc::new(Mutex::new(0_f64)); + let timer = Arc::new(Mutex::new(None::)); + let is_leading = Arc::new(AtomicBool::new(true)); + let last_return_value: Arc>> = Arc::new(Mutex::new(None)); - let t = Rc::clone(&timer); + let t = Arc::clone(&timer); let clear = move || { - if let Some(handle) = t.get() { + let mut t = t.lock().unwrap(); + if let Some(handle) = *t { handle.clear(); - t.set(None); + *t = None; } }; @@ -52,11 +52,11 @@ where let ms = ms.into(); - move |mut _invoke: Rc R>| { + move |mut _invoke: Arc R>| { let duration = ms.get_untracked(); - let elapsed = now() - last_exec.get(); + let elapsed = now() - *last_exec.lock().unwrap(); - let last_return_val = Rc::clone(&last_return_value); + let last_return_val = Arc::clone(&last_return_value); let invoke = move || { #[cfg(debug_assertions)] let zone = SpecialNonReactiveZone::enter(); @@ -66,7 +66,7 @@ where #[cfg(debug_assertions)] drop(zone); - let mut val_mut = last_return_val.borrow_mut(); + let mut val_mut = last_return_val.lock().unwrap(); *val_mut = Some(return_value); }; @@ -74,50 +74,51 @@ where clear(); if duration <= 0.0 { - last_exec.set(now()); + *last_exec.lock().unwrap() = now(); invoke(); - return Rc::clone(&last_return_value); + return Arc::clone(&last_return_value); } - if elapsed > duration && (options.leading || !is_leading.get()) { - last_exec.set(now()); + if elapsed > duration + && (options.leading || !is_leading.load(std::sync::atomic::Ordering::Relaxed)) + { + *last_exec.lock().unwrap() = now(); invoke(); } else if options.trailing { cfg_if! { if #[cfg(not(feature = "ssr"))] { - let last_exec = Rc::clone(&last_exec); - let is_leading = Rc::clone(&is_leading); - timer.set( + let last_exec = Arc::clone(&last_exec); + let is_leading = Arc::clone(&is_leading); + *timer.lock().unwrap() = set_timeout_with_handle( move || { - last_exec.set(now()); - is_leading.set(true); + *last_exec.lock().unwrap() = now(); + is_leading.store(true, std::sync::atomic::Ordering::Relaxed); invoke(); clear(); }, Duration::from_millis(max(0, (duration - elapsed) as u64)), ) - .ok(), - ); + .ok(); }} } cfg_if! { if #[cfg(not(feature = "ssr"))] { - if !options.leading && timer.get().is_none() { - let is_leading = Rc::clone(&is_leading); - timer.set( - set_timeout_with_handle( + let mut timer = timer.lock().unwrap(); + + if !options.leading && timer.is_none() { + let is_leading = Arc::clone(&is_leading); + *timer = set_timeout_with_handle( move || { - is_leading.set(true); + is_leading.store(true, std::sync::atomic::Ordering::Relaxed); }, Duration::from_millis(duration as u64), ) - .ok(), - ); + .ok(); } }} - is_leading.set(false); + is_leading.store(false, std::sync::atomic::Ordering::Relaxed); - Rc::clone(&last_return_value) + Arc::clone(&last_return_value) } } diff --git a/src/watch_with_options.rs b/src/watch_with_options.rs index 3d68f27..86e30ca 100644 --- a/src/watch_with_options.rs +++ b/src/watch_with_options.rs @@ -1,7 +1,7 @@ use crate::filter_builder_methods; use crate::utils::{create_filter_wrapper, DebounceOptions, FilterOptions, ThrottleOptions}; use default_struct_builder::DefaultBuilder; -use leptos::prelude::*; +use leptos::prelude::{diagnostics::SpecialNonReactiveZone, *}; use std::cell::RefCell; use std::rc::Rc;