feautre/IO-3377-Add-Notification-Tone-For-Messaging - Complete

This commit is contained in:
Dave
2025-09-24 12:02:20 -04:00
parent 33579c3e6a
commit dfd88308e0
17 changed files with 404 additions and 95 deletions

View File

@@ -0,0 +1,88 @@
let baseAudio = null;
let unlocked = false;
let queuedPlays = 0;
let installingUnlockHandlers = false;
/**
* Initialize the new-message sound.
* @param url
* @param volume
*/
export function initNewMessageSound(url, volume = 0.7) {
baseAudio = new Audio(url);
baseAudio.preload = "auto";
baseAudio.volume = volume;
}
/**
* Unlocks audio if not already unlocked.
* @returns {Promise<void>}
*/
export async function unlockAudio() {
if (unlocked) return;
try {
// Chrome/Safari: playing any media (even muted) after a gesture unlocks audio.
const a = new Audio();
a.muted = true;
await a.play().catch(() => {
//
});
unlocked = true;
// Flush exactly one queued ding (avoid spamming if many queued while locked)
if (queuedPlays > 0 && baseAudio) {
queuedPlays = 0;
const b = baseAudio.cloneNode(true);
b.play().catch(() => {
//
});
}
} finally {
removeUnlockListeners();
}
}
/**
* Installs listeners to unlock audio on first gesture.
*/
function addUnlockListeners() {
if (installingUnlockHandlers) return;
installingUnlockHandlers = true;
const handler = () => unlockAudio();
window.addEventListener("click", handler, { once: true, passive: true });
window.addEventListener("touchstart", handler, { once: true, passive: true });
window.addEventListener("keydown", handler, { once: true });
}
/**
* Removes listeners to unlock audio on first gesture.
*/
function removeUnlockListeners() {
// No need to remove explicitly with {once:true}, but keep this if you change it later
installingUnlockHandlers = false;
}
/**
* Plays the new-message ding. If blocked, queue one and wait for first gesture.
* @returns {Promise<void>}
*/
export async function playNewMessageSound() {
if (!baseAudio) return;
try {
const a = baseAudio.cloneNode(true);
await a.play();
} catch (err) {
// Most common: NotAllowedError due to missing prior gesture
if (err?.name === "NotAllowedError") {
queuedPlays = Math.min(queuedPlays + 1, 1); // cap at 1
addUnlockListeners();
// Let the app know we need user interaction (optional UI prompt)
window.dispatchEvent(new CustomEvent("sound-needs-unlock"));
return;
}
// Other errors can be logged
console.error("Audio play error:", err);
}
}