NW.js Notification API floods users with alerts — where should throttling live?
0 reputation · 04 Sept 2024, 19:28 UTC
0 reputation · 04 Sept 2024, 19:28 UTC
I am building a desktop app on NW.js (assume a recent 0.8x release; I will confirm the bundled Chromium version via nw.process.versions) that surfaces background events from the Node context — file watcher hits and scheduled sync results — as desktop notifications using the standard HTML5 Notification constructor in the window context.
The design concern is noise. When a watcher fires dozens of events in a few seconds, users get a stack of identical pop-ups. NW.js appears to offer no built-in rate limiting or notification channels, so any batching, collapsing, or summary-counter logic has to be implemented in app code. I am also weighing the Tray API (nw.Tray) as a passive status channel, reserving pop-ups for actionable events, but I am unsure where the community draws that line in practice.
A second uncertainty is user control: since NW.js delegates display to the OS, I cannot reliably detect or override OS-level do-not-disturb settings, so an in-app quiet-hours option would only suppress what my code sends, not what the OS shows.
Should throttling live in the Node event layer before messages reach the window context, or in the DOM layer where notifications are constructed? Is a tray-icon-plus-tooltip pattern considered sufficient for passive status, or do users expect a notification history? And is an in-app quiet-hours toggle worth building given the OS delegation limits?
Throttle the burst of events in the Node context before they reach the window context. Collapse or coalesce the events there, then send a single, already‑summarised Notification payload to the DOM. Use the tray icon for steady‑state status and a small in‑app history panel for missed alerts. An in‑app quiet‑hours toggle is useful, but it only suppresses the notifications your code sends; it cannot override the OS’s own Do‑Not‑Disturb setting.
Notification constructor in NW.js (Chromium‑based) has no built‑in rate limiting, grouping, or channels. Any collapsing logic must live in app code.Notification objects, which is more efficient and keeps the DOM side simple.Show the first event immediately, then buffer subsequent events for a few seconds. When the buffer times out, emit a single summary notification such as "12 files changed". This pattern keeps the user from seeing a flood of identical pop‑ups while still conveying the volume of activity.
// Node side – collapse before sending to the window
let pending = 0, timer = null;
function onWatcherChange(file) {
if (!timer) {
// First event – send immediately
sendToWindow({ type: 'notify', body: file });
}
pending++;
clearTimeout(timer);
timer = setTimeout(() => {
if (pending > 1) {
sendToWindow({ type: 'notify', body: `${pending} files changed` });
}
pending = 0;
timer = null;
}, 4000); // 4‑second burst window
}
Replace sendToWindow with your chosen IPC mechanism (e.g., window.postMessage, a shared nw.Window.open channel, or a local server). The window simply renders whatever it receives.
nw.Tray icon is ideal for showing a lightweight status (e.g., a counter of pending syncs or a simple "idle" icon).Implementing an in‑app quiet‑hours setting is worthwhile because:
nw.process.versions on startup to note the Chromium version; repeat tests after any NW.js upgrade.If you need to differentiate between event types (e.g., watcher hits vs. sync failures), you might want separate burst windows per severity. Let us know if that is a requirement, and we can adjust the throttling strategy accordingly.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.