mirror of
https://github.com/adoyle0/leptos-use.git
synced 2025-02-02 10:54:15 -05:00
parent
a3dc18064f
commit
5f5779ed10
5 changed files with 119 additions and 96 deletions
|
@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- `use_scroll` now uses `try_get_untracked` in the debounced callback to avoid panics if the context has been destroyed
|
||||
while the callback was waiting to be called.
|
||||
- `use_idle` works properly now (no more idles too early).
|
||||
- `use_web_notification` doesn't panic on the server anymore.
|
||||
|
||||
## [0.8.2] - 2023-11-09
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use crate::core::MaybeRwSignal;
|
||||
use crate::use_window::use_window;
|
||||
use cfg_if::cfg_if;
|
||||
use default_struct_builder::DefaultBuilder;
|
||||
use leptos::*;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use web_sys::{DisplayMediaStreamConstraints, MediaStream};
|
||||
|
||||
/// Reactive [`mediaDevices.getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) streaming.
|
||||
///
|
||||
|
@ -56,7 +54,7 @@ pub fn use_display_media_with_options(
|
|||
|
||||
let (enabled, set_enabled) = enabled.into_signal();
|
||||
|
||||
let (stream, set_stream) = create_signal(None::<Result<MediaStream, JsValue>>);
|
||||
let (stream, set_stream) = create_signal(None::<Result<web_sys::MediaStream, JsValue>>);
|
||||
|
||||
let _start = move || async move {
|
||||
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||
|
@ -67,6 +65,8 @@ pub fn use_display_media_with_options(
|
|||
let stream = create_media(audio).await;
|
||||
|
||||
set_stream.update(|s| *s = Some(stream));
|
||||
} else {
|
||||
let _ = audio;
|
||||
}}
|
||||
};
|
||||
|
||||
|
@ -122,13 +122,15 @@ pub fn use_display_media_with_options(
|
|||
}
|
||||
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
async fn create_media(audio: bool) -> Result<MediaStream, JsValue> {
|
||||
async fn create_media(audio: bool) -> Result<web_sys::MediaStream, JsValue> {
|
||||
use crate::use_window::use_window;
|
||||
|
||||
let media = use_window()
|
||||
.navigator()
|
||||
.ok_or_else(|| JsValue::from_str("Failed to access window.navigator"))
|
||||
.and_then(|n| n.media_devices())?;
|
||||
|
||||
let mut constraints = DisplayMediaStreamConstraints::new();
|
||||
let mut constraints = web_sys::DisplayMediaStreamConstraints::new();
|
||||
if audio {
|
||||
constraints.audio(&JsValue::from(true));
|
||||
}
|
||||
|
@ -136,7 +138,7 @@ async fn create_media(audio: bool) -> Result<MediaStream, JsValue> {
|
|||
let promise = media.get_display_media_with_constraints(&constraints)?;
|
||||
let res = wasm_bindgen_futures::JsFuture::from(promise).await?;
|
||||
|
||||
Ok::<_, JsValue>(MediaStream::unchecked_from_js(res))
|
||||
Ok::<_, JsValue>(web_sys::MediaStream::unchecked_from_js(res))
|
||||
}
|
||||
|
||||
// NOTE: there's no video value because it has to be `true`. Otherwise the stream would always resolve to an Error.
|
||||
|
@ -172,7 +174,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<Option<Result<MediaStream, JsValue>>>,
|
||||
pub stream: Signal<Option<Result<web_sys::MediaStream, JsValue>>>,
|
||||
|
||||
/// Starts the screen streaming. Triggers the ask for permission if not already granted.
|
||||
pub start: StartFn,
|
||||
|
|
|
@ -33,6 +33,10 @@ use crate::use_window;
|
|||
/// # view! { }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ## Server-Side Rendering
|
||||
///
|
||||
/// This function does **not** support SSR. Call it inside a `create_effect`.
|
||||
pub fn use_service_worker() -> UseServiceWorkerReturn<impl Fn() + Clone, impl Fn() + Clone> {
|
||||
use_service_worker_with_options(UseServiceWorkerOptions::default())
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use crate::{use_event_listener, use_supported, use_window};
|
||||
use crate::{use_supported, use_window};
|
||||
use cfg_if::cfg_if;
|
||||
use default_struct_builder::DefaultBuilder;
|
||||
use leptos::ev::visibilitychange;
|
||||
use leptos::*;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::closure::Closure;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
/// Reactive [Notification API](https://developer.mozilla.org/en-US/docs/Web/API/Notification).
|
||||
///
|
||||
|
@ -53,108 +51,122 @@ pub fn use_web_notification_with_options(
|
|||
|
||||
let (permission, set_permission) = create_signal(NotificationPermission::default());
|
||||
|
||||
let on_click_closure = Closure::<dyn Fn(web_sys::Event)>::new({
|
||||
let on_click = Rc::clone(&options.on_click);
|
||||
move |e: web_sys::Event| {
|
||||
on_click(e);
|
||||
}
|
||||
})
|
||||
.into_js_value();
|
||||
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||
let _ = options;
|
||||
let _ = set_notification;
|
||||
let _ = set_permission;
|
||||
|
||||
let on_close_closure = Closure::<dyn Fn(web_sys::Event)>::new({
|
||||
let on_close = Rc::clone(&options.on_close);
|
||||
move |e: web_sys::Event| {
|
||||
on_close(e);
|
||||
}
|
||||
})
|
||||
.into_js_value();
|
||||
let show = move |_: ShowOptions| ();
|
||||
let close = move || ();
|
||||
} else {
|
||||
use crate::use_event_listener;
|
||||
use leptos::ev::visibilitychange;
|
||||
use wasm_bindgen::closure::Closure;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
let on_error_closure = Closure::<dyn Fn(web_sys::Event)>::new({
|
||||
let on_error = Rc::clone(&options.on_error);
|
||||
move |e: web_sys::Event| {
|
||||
on_error(e);
|
||||
}
|
||||
})
|
||||
.into_js_value();
|
||||
|
||||
let on_show_closure = Closure::<dyn Fn(web_sys::Event)>::new({
|
||||
let on_show = Rc::clone(&options.on_show);
|
||||
move |e: web_sys::Event| {
|
||||
on_show(e);
|
||||
}
|
||||
})
|
||||
.into_js_value();
|
||||
|
||||
let show = {
|
||||
let options = options.clone();
|
||||
let on_click_closure = on_click_closure.clone();
|
||||
let on_close_closure = on_close_closure.clone();
|
||||
let on_error_closure = on_error_closure.clone();
|
||||
let on_show_closure = on_show_closure.clone();
|
||||
|
||||
move |options_override: ShowOptions| {
|
||||
if !is_supported.get_untracked() {
|
||||
return;
|
||||
let on_click_closure = Closure::<dyn Fn(web_sys::Event)>::new({
|
||||
let on_click = Rc::clone(&options.on_click);
|
||||
move |e: web_sys::Event| {
|
||||
on_click(e);
|
||||
}
|
||||
})
|
||||
.into_js_value();
|
||||
|
||||
let on_close_closure = Closure::<dyn Fn(web_sys::Event)>::new({
|
||||
let on_close = Rc::clone(&options.on_close);
|
||||
move |e: web_sys::Event| {
|
||||
on_close(e);
|
||||
}
|
||||
})
|
||||
.into_js_value();
|
||||
|
||||
let on_error_closure = Closure::<dyn Fn(web_sys::Event)>::new({
|
||||
let on_error = Rc::clone(&options.on_error);
|
||||
move |e: web_sys::Event| {
|
||||
on_error(e);
|
||||
}
|
||||
})
|
||||
.into_js_value();
|
||||
|
||||
let on_show_closure = Closure::<dyn Fn(web_sys::Event)>::new({
|
||||
let on_show = Rc::clone(&options.on_show);
|
||||
move |e: web_sys::Event| {
|
||||
on_show(e);
|
||||
}
|
||||
})
|
||||
.into_js_value();
|
||||
|
||||
let show = {
|
||||
let options = options.clone();
|
||||
let on_click_closure = on_click_closure.clone();
|
||||
let on_close_closure = on_close_closure.clone();
|
||||
let on_error_closure = on_error_closure.clone();
|
||||
let on_show_closure = on_show_closure.clone();
|
||||
|
||||
spawn_local(async move {
|
||||
set_permission.set(request_web_notification_permission().await);
|
||||
move |options_override: ShowOptions| {
|
||||
if !is_supported.get_untracked() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut notification_options = web_sys::NotificationOptions::from(&options);
|
||||
options_override.override_notification_options(&mut notification_options);
|
||||
let options = options.clone();
|
||||
let on_click_closure = on_click_closure.clone();
|
||||
let on_close_closure = on_close_closure.clone();
|
||||
let on_error_closure = on_error_closure.clone();
|
||||
let on_show_closure = on_show_closure.clone();
|
||||
|
||||
let notification_value = web_sys::Notification::new_with_options(
|
||||
&options_override.title.unwrap_or(options.title),
|
||||
¬ification_options,
|
||||
)
|
||||
.expect("Notification should be created");
|
||||
spawn_local(async move {
|
||||
set_permission.set(request_web_notification_permission().await);
|
||||
|
||||
notification_value.set_onclick(Some(on_click_closure.unchecked_ref()));
|
||||
notification_value.set_onclose(Some(on_close_closure.unchecked_ref()));
|
||||
notification_value.set_onerror(Some(on_error_closure.unchecked_ref()));
|
||||
notification_value.set_onshow(Some(on_show_closure.unchecked_ref()));
|
||||
let mut notification_options = web_sys::NotificationOptions::from(&options);
|
||||
options_override.override_notification_options(&mut notification_options);
|
||||
|
||||
set_notification.set(Some(notification_value));
|
||||
});
|
||||
}
|
||||
};
|
||||
let notification_value = web_sys::Notification::new_with_options(
|
||||
&options_override.title.unwrap_or(options.title),
|
||||
¬ification_options,
|
||||
)
|
||||
.expect("Notification should be created");
|
||||
|
||||
let close = {
|
||||
move || {
|
||||
notification.with_untracked(|notification| {
|
||||
if let Some(notification) = notification {
|
||||
notification.close();
|
||||
notification_value.set_onclick(Some(on_click_closure.unchecked_ref()));
|
||||
notification_value.set_onclose(Some(on_close_closure.unchecked_ref()));
|
||||
notification_value.set_onerror(Some(on_error_closure.unchecked_ref()));
|
||||
notification_value.set_onshow(Some(on_show_closure.unchecked_ref()));
|
||||
|
||||
set_notification.set(Some(notification_value));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let close = {
|
||||
move || {
|
||||
notification.with_untracked(|notification| {
|
||||
if let Some(notification) = notification {
|
||||
notification.close();
|
||||
}
|
||||
});
|
||||
set_notification.set(None);
|
||||
}
|
||||
};
|
||||
|
||||
spawn_local(async move {
|
||||
set_permission.set(request_web_notification_permission().await);
|
||||
});
|
||||
|
||||
on_cleanup(close);
|
||||
|
||||
// Use close() to remove a notification that is no longer relevant to to
|
||||
// the user (e.g.the user already read the notification on the webpage).
|
||||
// Most modern browsers dismiss notifications automatically after a few
|
||||
// moments(around four seconds).
|
||||
if is_supported.get_untracked() {
|
||||
let _ = use_event_listener(document(), visibilitychange, move |e: web_sys::Event| {
|
||||
e.prevent_default();
|
||||
if document().visibility_state() == web_sys::VisibilityState::Visible {
|
||||
// The tab has become visible so clear the now-stale Notification:
|
||||
close()
|
||||
}
|
||||
});
|
||||
set_notification.set(None);
|
||||
}
|
||||
};
|
||||
|
||||
spawn_local(async move {
|
||||
set_permission.set(request_web_notification_permission().await);
|
||||
});
|
||||
|
||||
on_cleanup(close);
|
||||
|
||||
// Use close() to remove a notification that is no longer relevant to to
|
||||
// the user (e.g.the user already read the notification on the webpage).
|
||||
// Most modern browsers dismiss notifications automatically after a few
|
||||
// moments(around four seconds).
|
||||
if is_supported.get_untracked() {
|
||||
let _ = use_event_listener(document(), visibilitychange, move |e: web_sys::Event| {
|
||||
e.prevent_default();
|
||||
if document().visibility_state() == web_sys::VisibilityState::Visible {
|
||||
// The tab has become visible so clear the now-stale Notification:
|
||||
close()
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
||||
UseWebNotificationReturn {
|
||||
is_supported,
|
||||
|
@ -192,6 +204,7 @@ impl From<NotificationDirection> for web_sys::NotificationDirection {
|
|||
/// - `silent`
|
||||
/// - `image`
|
||||
#[derive(DefaultBuilder, Clone)]
|
||||
#[cfg_attr(feature = "ssr", allow(dead_code))]
|
||||
pub struct UseWebNotificationOptions {
|
||||
/// The title property of the Notification interface indicates
|
||||
/// the title of the notification
|
||||
|
@ -302,6 +315,7 @@ impl From<&UseWebNotificationOptions> for web_sys::NotificationOptions {
|
|||
/// - `silent`
|
||||
/// - `image`
|
||||
#[derive(DefaultBuilder, Default)]
|
||||
#[cfg_attr(feature = "ssr", allow(dead_code))]
|
||||
pub struct ShowOptions {
|
||||
/// The title property of the Notification interface indicates
|
||||
/// the title of the notification
|
||||
|
@ -344,6 +358,7 @@ pub struct ShowOptions {
|
|||
// renotify: Option<bool>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
impl ShowOptions {
|
||||
fn override_notification_options(&self, options: &mut web_sys::NotificationOptions) {
|
||||
if let Some(direction) = self.direction {
|
||||
|
@ -413,6 +428,7 @@ impl From<web_sys::NotificationPermission> for NotificationPermission {
|
|||
/// Use `window.Notification.requestPosition()`. Returns a future that should be awaited
|
||||
/// at least once before using [`use_web_notification`] to make sure
|
||||
/// you have the permission to send notifications.
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
async fn request_web_notification_permission() -> NotificationPermission {
|
||||
if let Ok(notification_permission) = web_sys::Notification::request_permission() {
|
||||
let _ = wasm_bindgen_futures::JsFuture::from(notification_permission).await;
|
||||
|
|
|
@ -485,7 +485,7 @@ pub struct UseWebSocketOptions {
|
|||
/// If `false` you have to manually call the `open` function.
|
||||
/// Defaults to `true`.
|
||||
immediate: bool,
|
||||
/// Sub protocols
|
||||
/// Sub protocols. See [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#protocols).
|
||||
protocols: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue