diff --git a/.idea/leptos-use.iml b/.idea/leptos-use.iml index df1b62f..bb3717d 100644 --- a/.idea/leptos-use.iml +++ b/.idea/leptos-use.iml @@ -57,6 +57,7 @@ + diff --git a/examples/use_service_worker/src/main.rs b/examples/use_service_worker/src/main.rs index 2df2dcd..93dc1fa 100644 --- a/examples/use_service_worker/src/main.rs +++ b/examples/use_service_worker/src/main.rs @@ -1,6 +1,6 @@ use leptos::*; -use leptos_use::docs::demo_or_body; -use leptos_use::{use_service_worker, use_window}; +use leptos_use::docs::{demo_or_body, BooleanDisplay}; +use leptos_use::{use_document, use_service_worker, UseServiceWorkerReturn}; use web_sys::HtmlMetaElement; #[component] @@ -9,21 +9,28 @@ fn Demo() -> impl IntoView { .map(|meta| meta.content()) .expect("'version' meta element"); - let sw = use_service_worker(); + let UseServiceWorkerReturn { + registration, + installing, + waiting, + active, + skip_waiting, + .. + } = use_service_worker(); view! { -

"Current build: "{build}

+

"Current build: " {build}


-

"registration: "{move || format!("{:#?}", sw.registration.get())}

-

"installing: "{move || sw.installing.get()}

-

"waiting: "{move || sw.waiting.get()}

-

"active: "{move || sw.active.get()}

+

"registration: " {move || format!("{:#?}", registration())}

+

"installing: "

+

"waiting: "

+

"active: "


- + } } @@ -36,26 +43,17 @@ fn main() { }) } -fn load_meta_element>(name: S) -> Result { +fn load_meta_element(name: &str) -> Result { use wasm_bindgen::JsCast; - use_window() - .as_ref() - .ok_or_else(|| "No window instance!".to_owned()) - .and_then(|window| { - window - .document() - .ok_or_else(|| "No document instance!".to_owned()) - }) - .and_then(|document| { - document - .query_selector(format!("meta[name=\"{}\"]", name.as_ref()).as_str()) - .ok() - .flatten() - .ok_or_else(|| format!("Unable to find meta element with name 'version'.")) - }) - .and_then(|element| { - element.dyn_into::().map_err(|err| { - format!("Unable to cast element to HtmlMetaElement. Err: '{err:?}'.") - }) - }) + if let Some(document) = &*use_document() { + document + .query_selector(format!("meta[name=\"{name}\"]").as_str()) + .ok() + .flatten() + .ok_or_else(|| format!("Unable to find meta element with name '{name}'."))? + .dyn_into::() + .map_err(|err| format!("Unable to cast element to HtmlMetaElement. Err: '{err:?}'.")) + } else { + Err("Unable to find document.".into()) + } } diff --git a/src/use_service_worker.rs b/src/use_service_worker.rs index 52beb12..ec2d262 100644 --- a/src/use_service_worker.rs +++ b/src/use_service_worker.rs @@ -1,46 +1,52 @@ use default_struct_builder::DefaultBuilder; use leptos::*; -use std::borrow::Cow; +use std::rc::Rc; use wasm_bindgen::{prelude::Closure, JsCast, JsValue}; use web_sys::ServiceWorkerRegistration; use crate::use_window; +/// Reactive [ServiceWorker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API). /// -/// -/// ## Demo -/// -/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_service_worker) +/// Please check the [working example](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_service_worker). /// /// ## Usage /// /// ``` /// # use leptos::*; -/// # use leptos_use::use_service_worker; +/// # use leptos_use::{use_service_worker_with_options, UseServiceWorkerOptions, UseServiceWorkerReturn}; /// # /// # #[component] /// # fn Demo() -> impl IntoView { -/// # let sw = use_service_worker_with_options(UseServiceWorkerOptions { -/// # script_url: "service-worker.js".into(), -/// # skip_waiting_message: "skipWaiting".into(), -/// # ..UseServiceWorkerOptions::default() -/// # }); -/// # -/// # view! { } +/// let UseServiceWorkerReturn { +/// registration, +/// installing, +/// waiting, +/// active, +/// skip_waiting, +/// check_for_update, +/// } = use_service_worker_with_options(UseServiceWorkerOptions::default() +/// .script_url("service-worker.js") +/// .skip_waiting_message("skipWaiting"), +/// ); +/// +/// # view! { } /// # } /// ``` -pub fn use_service_worker() -> UseServiceWorkerReturn { +pub fn use_service_worker() -> UseServiceWorkerReturn { use_service_worker_with_options(UseServiceWorkerOptions::default()) } /// Version of [`use_service_worker`] that takes a `UseServiceWorkerOptions`. See [`use_service_worker`] for how to use. -pub fn use_service_worker_with_options(options: UseServiceWorkerOptions) -> UseServiceWorkerReturn { +pub fn use_service_worker_with_options( + options: UseServiceWorkerOptions, +) -> UseServiceWorkerReturn { // Trigger the user-defined action (page-reload by default) // whenever a new ServiceWorker is installed. if let Some(navigator) = use_window().navigator() { let on_controller_change = options.on_controller_change.clone(); let js_closure = Closure::wrap(Box::new(move |_event: JsValue| { - on_controller_change.call(()); + on_controller_change(); }) as Box) .into_js_value(); navigator @@ -95,7 +101,7 @@ pub fn use_service_worker_with_options(options: UseServiceWorkerOptions) -> UseS } Err(err) => match err { ServiceWorkerRegistrationError::Js(err) => { - tracing::warn!("ServiceWorker registration failed: {err:?}") + logging::warn!("ServiceWorker registration failed: {err:?}") } ServiceWorkerRegistrationError::NeverQueried => {} }, @@ -125,28 +131,28 @@ pub fn use_service_worker_with_options(options: UseServiceWorkerOptions) -> UseS .unwrap_or_default() }) }), - check_for_update: Callback::new(move |()| { + check_for_update: move || { registration.with(|reg| { if let Ok(reg) = reg { update_sw.dispatch(reg.clone()) } }) - }), - skip_waiting: Callback::new(move |()| { + }, + skip_waiting: move || { registration.with_untracked(|reg| if let Ok(reg) = reg { match reg.waiting() { Some(sw) => { - tracing::info!("Updating to newly installed SW..."); + logging::debug_warn!("Updating to newly installed SW..."); if let Err(err) = sw.post_message(&JsValue::from_str(&options.skip_waiting_message)) { - tracing::warn!("Could not send message to active SW: Error: {err:?}"); + logging::warn!("Could not send message to active SW: Error: {err:?}"); } }, None => { - tracing::warn!("You tried to update the SW while no new SW was waiting. This is probably a bug."); + logging::warn!("You tried to update the SW while no new SW was waiting. This is probably a bug."); }, } }); - }), + }, } } @@ -155,16 +161,18 @@ pub fn use_service_worker_with_options(options: UseServiceWorkerOptions) -> UseS pub struct UseServiceWorkerOptions { /// The name of your service-worker file. Must be deployed alongside your app. /// The default name is 'service-worker.js'. - pub script_url: Cow<'static, str>, + #[builder(into)] + script_url: String, /// The message sent to a waiting ServiceWorker when you call the `skip_waiting` callback. /// The callback is part of the return type of [`use_service_worker`]! /// The default message is 'skipWaiting'. - pub skip_waiting_message: Cow<'static, str>, + #[builder(into)] + skip_waiting_message: String, /// What should happen when a new service worker was activated? /// The default implementation reloads the current page. - pub on_controller_change: Callback<()>, + on_controller_change: Rc, } impl Default for UseServiceWorkerOptions { @@ -172,11 +180,11 @@ impl Default for UseServiceWorkerOptions { Self { script_url: "service-worker.js".into(), skip_waiting_message: "skipWaiting".into(), - on_controller_change: Callback::new(move |()| { + on_controller_change: Rc::new(move || { use std::ops::Deref; if let Some(window) = use_window().deref() { if let Err(err) = window.location().reload() { - tracing::warn!( + logging::warn!( "Detected a ServiceWorkerController change but the page reload failed! Error: {err:?}" ); } @@ -187,7 +195,11 @@ impl Default for UseServiceWorkerOptions { } /// Return type of [`use_service_worker`]. -pub struct UseServiceWorkerReturn { +pub struct UseServiceWorkerReturn +where + CheckFn: Fn() + Clone, + SkipFn: Fn() + Clone, +{ /// The current registration state. pub registration: Signal>, @@ -201,11 +213,11 @@ pub struct UseServiceWorkerReturn { pub active: Signal, /// Check for a ServiceWorker update. - pub check_for_update: Callback<()>, + pub check_for_update: CheckFn, /// Call this to activate a new ("waiting") SW if one is available. /// Calling this while the [`UseServiceWorkerReturn::waiting`] signal resolves to false has no effect. - pub skip_waiting: Callback<()>, + pub skip_waiting: SkipFn, } struct ServiceWorkerScriptUrl(pub String);