"use client" export type DesktopNotificationPermission = NotificationPermission | "unsupported" export function desktopNotificationsSupported(): boolean { return typeof window !== "undefined" && "Notification" in window } export function getDesktopNotificationPermission(): DesktopNotificationPermission { if (!desktopNotificationsSupported()) return "unsupported" return Notification.permission } export async function requestDesktopNotificationPermission(): Promise { if (!desktopNotificationsSupported()) return "unsupported" if (Notification.permission === "granted") return "granted" if (Notification.permission === "denied") return "denied" try { return await Notification.requestPermission() } catch { return Notification.permission } } export function isReplyOrMentionSubject(subject: string): boolean { const normalized = subject.trim().toLowerCase() return ( normalized.startsWith("re:") || normalized.startsWith("ré:") || normalized.startsWith("fwd:") || normalized.startsWith("tr:") ) } let audioContext: AudioContext | null = null function playNotificationTone() { if (typeof window === "undefined") return try { audioContext ??= new AudioContext() const ctx = audioContext const oscillator = ctx.createOscillator() const gain = ctx.createGain() oscillator.type = "sine" oscillator.frequency.value = 880 gain.gain.value = 0.04 oscillator.connect(gain) gain.connect(ctx.destination) oscillator.start() oscillator.stop(ctx.currentTime + 0.12) } catch {} } export function showDesktopNotification(options: { title: string body?: string tag?: string onClick?: () => void playSound?: boolean }) { if (!desktopNotificationsSupported()) return false if (Notification.permission !== "granted") return false if (typeof document !== "undefined" && document.visibilityState === "visible") { return false } try { const notification = new Notification(options.title, { body: options.body, tag: options.tag, icon: "/brand/ultimail-mark.png", }) if (options.onClick) { notification.onclick = () => { window.focus() options.onClick?.() notification.close() } } if (options.playSound) playNotificationTone() return true } catch { return false } }