ultisuite-client/lib/notifications/desktop-notifications.ts
2026-05-25 13:52:40 +02:00

86 lines
2.3 KiB
TypeScript

"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<DesktopNotificationPermission> {
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
}
}