//! HUD bell for desktop notifications about the other players' moves. use std::cell::Cell; use std::rc::Rc; use sycamore::prelude::*; use wasm_bindgen_futures::spawn_local; use crate::notify::{self, Permission}; /// Notification state shared by the game page and its table views: the /// opt-in flag and the browser permission, both page-scoped so a /// permission prompt that resolves after a table re-render still lands /// somewhere safe. `alive` mirrors the game page's unmount flag. #[derive(Clone)] pub struct NotifyCtx { pub enabled: Signal, pub permission: Signal, pub alive: Rc>, } /// The caption for the current (enabled, permission) state, and whether /// the button is a dead end (the user must change browser settings). fn face(enabled: bool, permission: Permission) -> (&'static str, bool) { match (enabled, permission) { (true, _) => ("🔔 Notifications on", false), (false, Permission::Granted | Permission::Default) => ("🔔 Notify me", false), (false, Permission::Denied | Permission::Unsupported) => { ("🔔 Notifications blocked", true) } } } /// The move-notification bell, rendered in the HUD of seated players. /// /// The preference is global (all games) and persisted per device. The /// permission prompt fires on the first click: browsers require a user /// gesture, and asking on page load would be ignored. pub fn notify_toggle(ctx: NotifyCtx) -> View { let NotifyCtx { enabled, permission, alive } = ctx; let on_click = move |_| { if enabled.get() { enabled.set(false); notify::set_enabled(false); return; } match permission.get() { Permission::Default => { let alive = alive.clone(); spawn_local(async move { let granted = notify::request_permission().await; if !alive.get() { return; } permission.set(granted); if granted == Permission::Granted { enabled.set(true); notify::set_enabled(true); } }) } Permission::Granted => { enabled.set(true); notify::set_enabled(true); } Permission::Denied | Permission::Unsupported => {} } }; view! { button( class="btn btn-outline-light", disabled=move || face(enabled.get(), permission.get()).1, title="Desktop notification when another player plays a card, \ while this tab is in the background", on:click=on_click, ) { (move || face(enabled.get(), permission.get()).0) } } }