diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dea579..1cc0814 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `use_prefers_reduced_motion` +### Breaking Change 🛠 + +- `use_websocket` now supports different types for sending and receiving messages + +### Change 🔥 + +- There is now a feature for almost every function to get better compile and rust-analyzer times. + ## [0.12.0] - 2024-08-14 > Make sure you also update `cargo-leptos` to the latest version if you use that. diff --git a/Cargo.toml b/Cargo.toml index 5f6006f..618bf4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ homepage = "https://leptos-use.rs" actix-web = { version = "4", optional = true, default-features = false } async-trait = { version = "0.1", optional = true } cfg-if = "1" -codee = { version = "0.1", optional = true } +codee = { version = "0.2", optional = true } cookie = { version = "0.18", features = ["percent-encode"], optional = true } default-struct-builder = "0.5" futures-util = { version = "0.3", optional = true } @@ -40,7 +40,7 @@ wasm-bindgen-futures = "0.4" web-sys = { version = "=0.3.70", optional = true } [dev-dependencies] -codee = { version = "0.1", features = [ +codee = { version = "0.2", features = [ "json_serde", "msgpack_serde", "base64", diff --git a/examples/Cargo.toml b/examples/Cargo.toml index b3893d5..b91f5e5 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -94,5 +94,8 @@ panic = "abort" [lib] +[workspace.dependencies] +codee = { version = "0.2" } + [package.metadata.leptos] lib-profile-release = "wasm-release" diff --git a/examples/ssr/Cargo.toml b/examples/ssr/Cargo.toml index 59a77b3..df36523 100644 --- a/examples/ssr/Cargo.toml +++ b/examples/ssr/Cargo.toml @@ -8,7 +8,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] axum = { version = "0.7", optional = true } -codee = "0.1" +codee.workspace = true console_error_panic_hook = "0.1" console_log = "1" cfg-if = "1" diff --git a/examples/use_broadcast_channel/Cargo.toml b/examples/use_broadcast_channel/Cargo.toml index 636ae9c..a8e262b 100644 --- a/examples/use_broadcast_channel/Cargo.toml +++ b/examples/use_broadcast_channel/Cargo.toml @@ -8,7 +8,7 @@ leptos = { git = "https://github.com/leptos-rs/leptos", features = [ "nightly", "csr", ] } -codee = "0.1" +codee.workspace = true console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_cookie/Cargo.toml b/examples/use_cookie/Cargo.toml index 5a5079d..903ebc6 100644 --- a/examples/use_cookie/Cargo.toml +++ b/examples/use_cookie/Cargo.toml @@ -8,7 +8,7 @@ leptos = { git = "https://github.com/leptos-rs/leptos", features = [ "nightly", "csr", ] } -codee = "0.1" +codee.workspace = true console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_storage/Cargo.toml b/examples/use_storage/Cargo.toml index 17a330d..4d42e2b 100644 --- a/examples/use_storage/Cargo.toml +++ b/examples/use_storage/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -codee = { version = "0.1", features = ["json_serde"] } +codee = { workspace = true, features = ["json_serde"] } console_error_panic_hook = "0.1" console_log = "1" leptos = { git = "https://github.com/leptos-rs/leptos", features = [ diff --git a/examples/use_websocket/Cargo.toml b/examples/use_websocket/Cargo.toml index 160a73e..01ff4a1 100644 --- a/examples/use_websocket/Cargo.toml +++ b/examples/use_websocket/Cargo.toml @@ -8,7 +8,7 @@ leptos = { git = "https://github.com/leptos-rs/leptos", features = [ "nightly", "csr", ] } -codee = { version = "0.1", features = ["msgpack_serde"] } +codee = { workspace = true, features = ["msgpack_serde"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_websocket/src/main.rs b/examples/use_websocket/src/main.rs index 5aba341..b338996 100644 --- a/examples/use_websocket/src/main.rs +++ b/examples/use_websocket/src/main.rs @@ -33,7 +33,7 @@ fn Demo() -> impl IntoView { open, close, .. - } = use_websocket::("wss://echo.websocket.events/"); + } = use_websocket::("wss://echo.websocket.events/"); let send_message = move |_| { let m = Apple { @@ -101,7 +101,7 @@ fn Demo() -> impl IntoView { close: close2, message: message2, .. - } = use_websocket_with_options::( + } = use_websocket_with_options::( "wss://echo.websocket.events/", UseWebSocketOptions::default() .immediate(false) diff --git a/src/use_web_notification.rs b/src/use_web_notification.rs index ce76881..8bc48d0 100644 --- a/src/use_web_notification.rs +++ b/src/use_web_notification.rs @@ -3,6 +3,7 @@ use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use leptos::prelude::{wrappers::read::Signal, *}; use std::rc::Rc; +use wasm_bindgen::JsValue; /// Reactive [Notification API](https://developer.mozilla.org/en-US/docs/Web/API/Notification). /// @@ -219,7 +220,6 @@ impl From for web_sys::NotificationDirection { /// See [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/API/notification) for more info. /// /// The following implementations are missing: -/// - `vibrate` #[derive(DefaultBuilder, Clone)] #[cfg_attr(feature = "ssr", allow(dead_code))] pub struct UseWebNotificationOptions { @@ -268,10 +268,15 @@ pub struct UseWebNotificationOptions { renotify: bool, /// A boolean value specifying whether the notification should be silent, regardless of the device settings. - /// The default is `false`, which means the notification is not silent. If `true`, then the notification will be silent. + /// The default is `null`, which means the notification is not silent. If `true`, then the notification will be silent. #[builder(into)] silent: Option, + /// A `Vec` specifying the vibration pattern in milliseconds for vibrating and not vibrating. + /// The last entry can be a vibration since it stops automatically after each period. + #[builder(into)] + vibrate: Option>, + /// Called when the user clicks on displayed `Notification`. on_click: Rc, @@ -299,6 +304,7 @@ impl Default for UseWebNotificationOptions { require_interaction: false, renotify: false, silent: None, + vibrate: None, on_click: Rc::new(|_| {}), on_close: Rc::new(|_| {}), on_error: Rc::new(|_| {}), @@ -336,6 +342,9 @@ impl From<&UseWebNotificationOptions> for web_sys::NotificationOptions { web_sys_options.set_tag(tag); } + if let Some(vibrate) = &options.vibrate { + web_sys_options.set_vibrate(&vibration_pattern_to_jsvalue(vibrate)); + } web_sys_options } } @@ -345,7 +354,6 @@ impl From<&UseWebNotificationOptions> for web_sys::NotificationOptions { /// See [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/API/notification) for more info. /// /// The following implementations are missing: -/// - `vibrate` #[derive(DefaultBuilder, Default)] #[cfg_attr(feature = "ssr", allow(dead_code))] pub struct ShowOptions { @@ -396,9 +404,14 @@ pub struct ShowOptions { renotify: Option, /// A boolean value specifying whether the notification should be silent, regardless of the device settings. - /// The default is `false`, which means the notification is not silent. If `true`, then the notification will be silent. + /// The default is `null`, which means the notification is not silent. If `true`, then the notification will be silent. #[builder(into)] silent: Option, + + /// A `Vec` specifying the vibration pattern in milliseconds for vibrating and not vibrating. + /// The last entry can be a vibration since it stops automatically after each period. + #[builder(into)] + vibrate: Option>, } #[cfg(not(feature = "ssr"))] @@ -439,6 +452,10 @@ impl ShowOptions { if let Some(silent) = self.silent { options.set_silent(Some(silent)); } + + if let Some(vibrate) = &self.vibrate { + options.set_vibrate(&vibration_pattern_to_jsvalue(vibrate)); + } } } @@ -453,6 +470,15 @@ fn browser_supports_notifications() -> bool { false } +/// Helper function to convert a slice of `u16` into a `JsValue` array that represents a vibration pattern +fn vibration_pattern_to_jsvalue(pattern: &[u16]) -> JsValue { + let array = js_sys::Array::new(); + for &value in pattern.iter() { + array.push(&JsValue::from(value)); + } + array.into() +} + #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] /// The permission to send notifications pub enum NotificationPermission { diff --git a/src/use_websocket.rs b/src/use_websocket.rs index 74f28fd..11b97de 100644 --- a/src/use_websocket.rs +++ b/src/use_websocket.rs @@ -1,17 +1,15 @@ #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))] +use crate::{core::ConnectionReadyState, ReconnectLimit}; use cfg_if::cfg_if; +use codee::{CodecError, Decoder, Encoder, HybridCoderError, HybridDecoder, HybridEncoder}; +use default_struct_builder::DefaultBuilder; +use js_sys::Array; use leptos::{leptos_dom::helpers::TimeoutHandle, prelude::*}; +use std::marker::PhantomData; use std::sync::{atomic::AtomicBool, Arc}; use std::time::Duration; use thiserror::Error; - -use crate::{core::ConnectionReadyState, ReconnectLimit}; -use codee::{ - CodecError, Decoder, Encoder, HybridCoderError, HybridDecoder, HybridEncoder, IsBinary, -}; -use default_struct_builder::DefaultBuilder; -use js_sys::Array; use wasm_bindgen::prelude::*; use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket}; @@ -44,7 +42,7 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket}; /// open, /// close, /// .. -/// } = use_websocket::("wss://echo.websocket.events/"); +/// } = use_websocket::("wss://echo.websocket.events/"); /// /// let send_message = move |_| { /// send(&"Hello, world!".to_string()); @@ -97,7 +95,7 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket}; /// message, /// send, /// .. -/// } = use_websocket::("wss://some.websocket.server/"); +/// } = use_websocket::("wss://some.websocket.server/"); /// /// let send_data = move || { /// send(&SomeData { @@ -188,7 +186,7 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket}; /// message, /// send, /// .. -/// } = use_websocket::("ws:://some.websocket.io"); +/// } = use_websocket::("ws:://some.websocket.io"); /// /// provide_context(WebsocketContext::new(message, Arc::new(send.clone()))); /// # @@ -228,45 +226,47 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket}; /// ## Server-Side Rendering /// /// On the server the returned functions amount to no-ops. -pub fn use_websocket( +pub fn use_websocket( url: &str, ) -> UseWebSocketReturn< - T, + Tx, + Rx, impl Fn() + Clone + 'static, impl Fn() + Clone + 'static, - impl Fn(&T) + Clone + 'static, + impl Fn(&Tx) + Clone + 'static, > where - T: Send + Sync + 'static, - C: Encoder + Decoder, - C: IsBinary>::Encoded>, - C: HybridDecoder>::Encoded, Error = >::Error>, - C: HybridEncoder>::Encoded, Error = >::Error>, + Tx: Send + Sync + 'static, + Rx: Send + Sync + 'static, + C: Encoder + Decoder, + C: HybridEncoder>::Encoded, Error = >::Error>, + C: HybridDecoder>::Encoded, Error = >::Error>, { - use_websocket_with_options::(url, UseWebSocketOptions::default()) + use_websocket_with_options::(url, UseWebSocketOptions::default()) } /// Version of [`use_websocket`] that takes `UseWebSocketOptions`. See [`use_websocket`] for how to use. #[allow(clippy::type_complexity)] -pub fn use_websocket_with_options( +pub fn use_websocket_with_options( url: &str, options: UseWebSocketOptions< - T, - HybridCoderError<>::Error>, - HybridCoderError<>::Error>, + Rx, + HybridCoderError<>::Error>, + HybridCoderError<>::Error>, >, ) -> UseWebSocketReturn< - T, + Tx, + Rx, impl Fn() + Clone + 'static, impl Fn() + Clone + 'static, - impl Fn(&T) + Clone + 'static, + impl Fn(&Tx) + Clone + 'static, > where - T: Send + Sync + 'static, - C: Encoder + Decoder, - C: IsBinary>::Encoded>, - C: HybridDecoder>::Encoded, Error = >::Error>, - C: HybridEncoder>::Encoded, Error = >::Error>, + Tx: Send + Sync + 'static, + Rx: Send + Sync + 'static, + C: Encoder + Decoder, + C: HybridEncoder>::Encoded, Error = >::Error>, + C: HybridDecoder>::Encoded, Error = >::Error>, { let url = normalize_url(url); @@ -301,7 +301,11 @@ where let reconnect_ref: StoredValue>> = StoredValue::new(None); reconnect_ref.set_value({ + let unmounted = Arc::clone(&unmounted); + Some(Arc::new(move || { + let unmounted = Arc::clone(&unmounted); + if !manually_closed_ref.get_value() && !reconnect_limit.is_exceeded_by(reconnect_times_ref.get_value()) && ws_ref @@ -311,6 +315,9 @@ where reconnect_timer_ref.set_value( set_timeout_with_handle( move || { + if unmounted.get() { + return; + } if let Some(connect) = connect_ref.get_value() { connect(); reconnect_times_ref.update_value(|current| *current += 1); @@ -552,8 +559,8 @@ where let send = { let on_error = Arc::clone(&on_error); - move |value: &T| { - if C::is_binary() { + move |value: &Tx| { + if C::is_binary_encoder() { match C::encode_bin(value) { Ok(val) => send_bytes(&val), Err(err) => on_error(CodecError::Encode(err).into()), @@ -607,6 +614,7 @@ where open, close, send, + _marker: PhantomData, } } @@ -614,15 +622,15 @@ type ArcFnBytes = Arc; /// Options for [`use_websocket_with_options`]. #[derive(DefaultBuilder)] -pub struct UseWebSocketOptions +pub struct UseWebSocketOptions where - T: ?Sized, + Rx: ?Sized, { /// `WebSocket` connect callback. on_open: Arc, /// `WebSocket` message callback for typed message decoded by codec. #[builder(skip)] - on_message: Arc, + on_message: Arc, /// `WebSocket` message callback for text. on_message_raw: Arc, /// `WebSocket` message callback for binary. @@ -645,7 +653,7 @@ where protocols: Option>, } -impl UseWebSocketOptions { +impl UseWebSocketOptions { /// `WebSocket` error callback. pub fn on_error(self, handler: F) -> Self where @@ -660,7 +668,7 @@ impl UseWebSocketOptions { /// `WebSocket` message callback for typed message decoded by codec. pub fn on_message(self, handler: F) -> Self where - F: Fn(&T) + Send + Sync + 'static, + F: Fn(&Rx) + Send + Sync + 'static, { Self { on_message: Arc::new(handler), @@ -669,7 +677,7 @@ impl UseWebSocketOptions { } } -impl Default for UseWebSocketOptions { +impl Default for UseWebSocketOptions { fn default() -> Self { Self { on_open: Arc::new(|_| {}), @@ -688,17 +696,18 @@ impl Default for UseWebSocketOptions { /// Return type of [`use_websocket`]. #[derive(Clone)] -pub struct UseWebSocketReturn +pub struct UseWebSocketReturn where - T: Send + Sync + 'static, + Tx: Send + Sync + 'static, + Rx: Send + Sync + 'static, OpenFn: Fn() + Clone + 'static, CloseFn: Fn() + Clone + 'static, - SendFn: Fn(&T) + Clone + 'static, + SendFn: Fn(&Tx) + Clone + 'static, { /// The current state of the `WebSocket` connection. pub ready_state: Signal, /// Latest message received from `WebSocket`. - pub message: Signal>, + pub message: Signal>, /// The `WebSocket` instance. pub ws: Option, /// Opens the `WebSocket` connection @@ -707,6 +716,8 @@ where pub close: CloseFn, /// Sends data through the socket pub send: SendFn, + + _marker: PhantomData, } #[derive(Error, Debug)] diff --git a/src/watch_debounced.rs b/src/watch_debounced.rs index 3a9b1e8..726a105 100644 --- a/src/watch_debounced.rs +++ b/src/watch_debounced.rs @@ -1,4 +1,4 @@ -use crate::{watch_with_options, utils::DebounceOptions, WatchOptions}; +use crate::{utils::DebounceOptions, watch_with_options, WatchOptions}; use default_struct_builder::DefaultBuilder; use leptos::prelude::*; diff --git a/src/watch_throttled.rs b/src/watch_throttled.rs index d4e8bfe..d7c44a2 100644 --- a/src/watch_throttled.rs +++ b/src/watch_throttled.rs @@ -1,4 +1,4 @@ -use crate::{watch_with_options, utils::ThrottleOptions, WatchOptions}; +use crate::{utils::ThrottleOptions, watch_with_options, WatchOptions}; use default_struct_builder::DefaultBuilder; /// A throttled version of `leptos::watch`.