refactor: modernize browser notifications with leptos-use and refactor utils/notification.rs
All checks were successful
Build MIPS Binary / build (push) Successful in 4m31s
All checks were successful
Build MIPS Binary / build (push) Successful in 4m31s
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
use leptos::*;
|
||||
use leptos_use::on_click_outside;
|
||||
use leptos_use::storage::use_local_storage;
|
||||
use codee::string::FromToStringCodec;
|
||||
use shared::GlobalLimitRequest;
|
||||
|
||||
@@ -131,6 +131,9 @@ pub fn provide_torrent_store() {
|
||||
let notifications = create_rw_signal(Vec::<NotificationItem>::new());
|
||||
let user = create_rw_signal(Option::<String>::None);
|
||||
|
||||
// Browser notification hook
|
||||
let show_browser_notification = crate::utils::notification::use_app_notification();
|
||||
|
||||
let store = TorrentStore {
|
||||
torrents,
|
||||
filter,
|
||||
@@ -149,6 +152,7 @@ pub fn provide_torrent_store() {
|
||||
return;
|
||||
}
|
||||
|
||||
let show_browser_notification = show_browser_notification.clone();
|
||||
spawn_local(async move {
|
||||
let mut backoff_ms: u32 = 1000; // Start with 1 second
|
||||
let max_backoff_ms: u32 = 30000; // Max 30 seconds
|
||||
@@ -247,7 +251,7 @@ pub fn provide_torrent_store() {
|
||||
shared::NotificationLevel::Info => "ℹ️ VibeTorrent",
|
||||
};
|
||||
|
||||
crate::utils::notification::show_notification_if_enabled(
|
||||
show_browser_notification(
|
||||
title,
|
||||
&n.message
|
||||
);
|
||||
@@ -329,49 +333,21 @@ pub async fn subscribe_to_push_notifications() {
|
||||
// First, request notification permission if not already granted
|
||||
let window = web_sys::window().expect("window should exist");
|
||||
|
||||
// Notification.permission is a static property, but web_sys exposes it via the Notification class instance or we check it manually.
|
||||
// Actually, Notification::permission() is a static method in web_sys.
|
||||
match web_sys::Notification::permission() {
|
||||
web_sys::NotificationPermission::Granted => {
|
||||
log::info!("Notification permission already granted");
|
||||
}
|
||||
web_sys::NotificationPermission::Denied => {
|
||||
log::warn!("Notification permission was denied");
|
||||
let permission = crate::utils::notification::get_notification_permission();
|
||||
if permission == "default" {
|
||||
log::info!("Requesting notification permission...");
|
||||
if !crate::utils::notification::request_notification_permission().await {
|
||||
log::warn!("Notification permission denied by user");
|
||||
return;
|
||||
}
|
||||
web_sys::NotificationPermission::Default => {
|
||||
log::info!("Requesting notification permission...");
|
||||
let permission_promise = match web_sys::Notification::request_permission() {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
log::error!("Failed to request notification permission: {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match wasm_bindgen_futures::JsFuture::from(permission_promise).await {
|
||||
Ok(val) => {
|
||||
let permission = val.as_string().unwrap_or_default();
|
||||
if permission != "granted" {
|
||||
log::warn!("Notification permission denied by user");
|
||||
return;
|
||||
}
|
||||
log::info!("Notification permission granted by user");
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to await notification permission: {:?}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
log::warn!("Unknown notification permission status");
|
||||
return;
|
||||
}
|
||||
} else if permission == "denied" {
|
||||
log::warn!("Notification permission was denied");
|
||||
return;
|
||||
}
|
||||
|
||||
log::info!("Notification permission granted! Proceeding with push subscription...");
|
||||
|
||||
|
||||
// Get VAPID public key from backend
|
||||
let public_key_response = match Request::get("/api/push/public-key").send().await {
|
||||
Ok(resp) => resp,
|
||||
|
||||
@@ -1,37 +1,19 @@
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::{Notification, NotificationOptions};
|
||||
use leptos::*;
|
||||
use leptos_use::{use_web_notification, UseWebNotificationReturn, NotificationPermission};
|
||||
|
||||
/// Request browser notification permission from user
|
||||
pub async fn request_notification_permission() -> bool {
|
||||
let window = web_sys::window().expect("no global window");
|
||||
|
||||
// Check if Notification API is available
|
||||
if js_sys::Reflect::has(&window, &JsValue::from_str("Notification")).unwrap_or(false) {
|
||||
let notification = js_sys::Reflect::get(&window, &JsValue::from_str("Notification"))
|
||||
.expect("Notification should exist");
|
||||
|
||||
// Request permission
|
||||
let promise = js_sys::Reflect::get(¬ification, &JsValue::from_str("requestPermission"))
|
||||
.expect("requestPermission should exist");
|
||||
|
||||
if let Ok(function) = promise.dyn_into::<js_sys::Function>() {
|
||||
if let Ok(promise) = function.call0(¬ification) {
|
||||
if let Ok(promise) = promise.dyn_into::<js_sys::Promise>() {
|
||||
let result = wasm_bindgen_futures::JsFuture::from(promise).await;
|
||||
|
||||
if let Ok(permission) = result {
|
||||
let permission_str = permission.as_string().unwrap_or_default();
|
||||
return permission_str == "granted";
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(promise) = Notification::request_permission() {
|
||||
if let Ok(result) = wasm_bindgen_futures::JsFuture::from(promise).await {
|
||||
return result.as_string().unwrap_or_default() == "granted";
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if browser notifications are supported and permitted
|
||||
/// Check if browser notifications are supported
|
||||
pub fn is_notification_supported() -> bool {
|
||||
let window = web_sys::window().expect("no global window");
|
||||
js_sys::Reflect::has(&window, &JsValue::from_str("Notification")).unwrap_or(false)
|
||||
@@ -39,68 +21,62 @@ pub fn is_notification_supported() -> bool {
|
||||
|
||||
/// Get current notification permission status
|
||||
pub fn get_notification_permission() -> String {
|
||||
if !is_notification_supported() {
|
||||
return "unsupported".to_string();
|
||||
match Notification::permission() {
|
||||
web_sys::NotificationPermission::Granted => "granted".to_string(),
|
||||
web_sys::NotificationPermission::Denied => "denied".to_string(),
|
||||
web_sys::NotificationPermission::Default => "default".to_string(),
|
||||
_ => "default".to_string(),
|
||||
}
|
||||
|
||||
let window = web_sys::window().expect("no global window");
|
||||
let notification = js_sys::Reflect::get(&window, &JsValue::from_str("Notification"))
|
||||
.expect("Notification should exist");
|
||||
|
||||
let permission = js_sys::Reflect::get(¬ification, &JsValue::from_str("permission"))
|
||||
.unwrap_or(JsValue::from_str("default"));
|
||||
|
||||
permission.as_string().unwrap_or("default".to_string())
|
||||
}
|
||||
|
||||
/// Show a browser notification
|
||||
/// Returns true if notification was shown successfully
|
||||
pub fn show_browser_notification(title: &str, body: &str, icon: Option<&str>) -> bool {
|
||||
// Check permission first
|
||||
let permission = get_notification_permission();
|
||||
if permission != "granted" {
|
||||
log::warn!("Notification permission not granted: {}", permission);
|
||||
/// Hook for using browser notifications within Leptos components or effects.
|
||||
/// This uses leptos-use for reactive permission tracking.
|
||||
pub fn use_app_notification() -> impl Fn(&str, &str) + Clone {
|
||||
let UseWebNotificationReturn { permission, .. } = use_web_notification();
|
||||
|
||||
move |title: &str, body: &str| {
|
||||
// Check user preference from localStorage
|
||||
let window = web_sys::window().expect("no global window");
|
||||
let storage = window.local_storage().ok().flatten();
|
||||
let enabled = storage
|
||||
.and_then(|s| s.get_item("vibetorrent_browser_notifications").ok().flatten())
|
||||
.unwrap_or_else(|| "true".to_string());
|
||||
|
||||
// Use the reactive permission signal from leptos-use
|
||||
if enabled == "true" && permission.get() == NotificationPermission::Granted {
|
||||
show_browser_notification(title, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Show a browser notification (non-reactive version)
|
||||
pub fn show_browser_notification(title: &str, body: &str) -> bool {
|
||||
if get_notification_permission() != "granted" {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create notification options
|
||||
let opts = NotificationOptions::new();
|
||||
opts.set_body(body);
|
||||
opts.set_icon(icon.unwrap_or("/icon-192.png"));
|
||||
opts.set_badge("/icon-192.png");
|
||||
opts.set_icon("/icon-192.png");
|
||||
opts.set_tag("vibetorrent");
|
||||
opts.set_require_interaction(false);
|
||||
opts.set_silent(Some(false));
|
||||
|
||||
// Create and show notification
|
||||
match Notification::new_with_options(title, &opts) {
|
||||
Ok(_notification) => {
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to create notification: {:?}", e);
|
||||
false
|
||||
}
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Show notification only if enabled in settings and permission granted
|
||||
/// Legacy helper for showing notification if enabled in settings
|
||||
pub fn show_notification_if_enabled(title: &str, body: &str) -> bool {
|
||||
// Check localStorage for user preference
|
||||
let window = web_sys::window().expect("no global window");
|
||||
let storage = window.local_storage().ok().flatten();
|
||||
|
||||
if let Some(storage) = storage {
|
||||
let enabled = storage
|
||||
.get_item("vibetorrent_browser_notifications")
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or("true".to_string());
|
||||
let enabled = storage
|
||||
.and_then(|s| s.get_item("vibetorrent_browser_notifications").ok().flatten())
|
||||
.unwrap_or_else(|| "true".to_string());
|
||||
|
||||
if enabled == "true" {
|
||||
return show_browser_notification(title, body, None);
|
||||
}
|
||||
if enabled == "true" {
|
||||
return show_browser_notification(title, body);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user