notifications: per-event tags + debug logs
bug: all notifications used tag='hyperhive', so each new fire
replaced the previous — operator only ever saw one at a time and
might miss the fact that a second arrived. now per-event tags
(hyperhive:approval:<id>, hyperhive❓<id>,
hyperhive:msg:<at>:<rand>) so distinct events stack in the OS
notification center.
dropped the bogus icon (was pointing at dashboard.css) — some
browsers refuse to display a notification with an invalid icon.
added console.debug at every block point (not supported, permission
not granted, muted) and a 'shown' log on success, so the operator
can see in the browser console exactly why a notification didn't
fire.
note for the operator: most browsers also suppress notifications
while the originating tab is FOCUSED. that's a browser-level
decision, not ours.
This commit is contained in:
parent
62d1a74929
commit
3b532753b3
1 changed files with 30 additions and 7 deletions
|
|
@ -82,15 +82,30 @@
|
||||||
unmute.addEventListener('click', () => { setMuted(false); renderControls(); });
|
unmute.addEventListener('click', () => { setMuted(false); renderControls(); });
|
||||||
renderControls();
|
renderControls();
|
||||||
}
|
}
|
||||||
function show(title, body) {
|
function show(title, body, tag) {
|
||||||
if (!supported || Notification.permission !== 'granted' || isMuted()) return;
|
if (!supported) {
|
||||||
|
console.debug('notify: Notification API not supported');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Notification.permission !== 'granted') {
|
||||||
|
console.debug('notify: permission not granted', Notification.permission);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isMuted()) {
|
||||||
|
console.debug('notify: muted');
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
// Per-event tag so distinct messages stack instead of
|
||||||
|
// collapsing into one slot. Caller passes a unique tag per
|
||||||
|
// notification kind/id; we don't fall back to 'hyperhive'
|
||||||
|
// because that one tag would replace itself on every fire.
|
||||||
const n = new Notification(title, {
|
const n = new Notification(title, {
|
||||||
body,
|
body,
|
||||||
tag: 'hyperhive', // collapse rapid bursts
|
tag: tag || ('hyperhive:' + Date.now()),
|
||||||
icon: '/static/dashboard.css', // any same-origin asset works as a favicon stand-in
|
|
||||||
});
|
});
|
||||||
n.onclick = () => { window.focus(); n.close(); };
|
n.onclick = () => { window.focus(); n.close(); };
|
||||||
|
console.debug('notify: shown', title, 'tag=', tag);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('notification show failed', err);
|
console.warn('notification show failed', err);
|
||||||
}
|
}
|
||||||
|
|
@ -124,12 +139,14 @@
|
||||||
if (seenApprovals.has(a.id)) continue;
|
if (seenApprovals.has(a.id)) continue;
|
||||||
seenApprovals.add(a.id);
|
seenApprovals.add(a.id);
|
||||||
const verb = a.kind === 'spawn' ? 'spawn approval' : 'config commit';
|
const verb = a.kind === 'spawn' ? 'spawn approval' : 'config commit';
|
||||||
NOTIF.show('◆ approval #' + a.id, `${verb} for ${a.agent}`);
|
NOTIF.show('◆ approval #' + a.id, `${verb} for ${a.agent}`,
|
||||||
|
'hyperhive:approval:' + a.id);
|
||||||
}
|
}
|
||||||
for (const q of questions) {
|
for (const q of questions) {
|
||||||
if (seenQuestions.has(q.id)) continue;
|
if (seenQuestions.has(q.id)) continue;
|
||||||
seenQuestions.add(q.id);
|
seenQuestions.add(q.id);
|
||||||
NOTIF.show('◆ manager asks', q.question.slice(0, 120));
|
NOTIF.show('◆ manager asks', q.question.slice(0, 120),
|
||||||
|
'hyperhive:question:' + q.id);
|
||||||
}
|
}
|
||||||
// operator_inbox: only notify on truly new ids — sse already
|
// operator_inbox: only notify on truly new ids — sse already
|
||||||
// handles single-message notifications, but if the operator
|
// handles single-message notifications, but if the operator
|
||||||
|
|
@ -658,7 +675,13 @@
|
||||||
// the OS notification center.
|
// the OS notification center.
|
||||||
if (m.kind === 'sent' && m.to === 'operator') {
|
if (m.kind === 'sent' && m.to === 'operator') {
|
||||||
refreshState();
|
refreshState();
|
||||||
NOTIF.show('◆ ' + m.from + ' → operator', String(m.body || '').slice(0, 200));
|
NOTIF.show(
|
||||||
|
'◆ ' + m.from + ' → operator',
|
||||||
|
String(m.body || '').slice(0, 200),
|
||||||
|
// Unique-per-arrival tag so a burst stacks instead of
|
||||||
|
// overwriting itself in the OS notification center.
|
||||||
|
'hyperhive:msg:' + m.at + ':' + Math.random().toString(36).slice(2, 6),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const row = document.createElement('div');
|
const row = document.createElement('div');
|
||||||
row.className = 'msgrow ' + m.kind;
|
row.className = 'msgrow ' + m.kind;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue