mirror of
https://github.com/HeyPuter/puter.git
synced 2026-09-12 00:05:38 +00:00
Leave worker notifications out of the dashboard entirely
This commit is contained in:
@@ -31,7 +31,6 @@ import {
|
||||
planBurstToasts,
|
||||
reconcileWithServer,
|
||||
setReadAt,
|
||||
shouldToast,
|
||||
titleWithBadge,
|
||||
toEntry,
|
||||
unreadCount,
|
||||
@@ -71,7 +70,6 @@ const checkAllIcon = `<svg ${STROKE_ICON} stroke-width="2"><path d="M2 13l4 4L14
|
||||
/** The glyph in front of an entry, by who sent it. */
|
||||
const GLYPHS = {
|
||||
sharing: `<svg ${STROKE_ICON}><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="M8.6 13.5l6.8 4M15.4 6.5l-6.8 4"/></svg>`,
|
||||
worker: `<svg ${STROKE_ICON}><path d="M16 18l6-6-6-6M8 6l-6 6 6 6"/></svg>`,
|
||||
default: bellIcon,
|
||||
};
|
||||
|
||||
@@ -515,15 +513,11 @@ export default function UIDashboardNotifications ({ $el_window, socket }) {
|
||||
for ( const entry of fresh ) surfaced.add(entry.uid);
|
||||
for ( const entry of result.added ) justAdded.add(entry.uid);
|
||||
render();
|
||||
|
||||
// Quiet arrivals only update the badge and the list; the toast and
|
||||
// its spoken counterpart stay silent alike.
|
||||
const loud = fresh.filter((entry) => shouldToast(entry.notification));
|
||||
announce(loud);
|
||||
announce(fresh);
|
||||
|
||||
// Open, the panel is already showing them.
|
||||
if ( isOpen ) return;
|
||||
const { shown, folded } = planBurstToasts(loud);
|
||||
const { shown, folded } = planBurstToasts(fresh);
|
||||
for ( const entry of [...shown].reverse() ) toast(entry);
|
||||
if ( folded > 0 ) toastSummary(folded);
|
||||
};
|
||||
|
||||
@@ -45,6 +45,12 @@
|
||||
/** Notifications announcing a share; the two templates the backend writes. */
|
||||
const SHARE_TEMPLATES = new Set(['file-shared-with-you', 'file-shared-before-you-joined']);
|
||||
|
||||
/**
|
||||
* Senders the notification center leaves out entirely — a worker's deploy
|
||||
* confirmations are noise next to things that need the user.
|
||||
*/
|
||||
const HIDDEN_SOURCES = new Set(['worker']);
|
||||
|
||||
// SQLite's CURRENT_TIMESTAMP: UTC with no zone marker, which `Date.parse`
|
||||
// would otherwise read as local time.
|
||||
const SQL_TIMESTAMP = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?$/;
|
||||
@@ -81,7 +87,8 @@ export const parseCreatedAt = (value) => {
|
||||
* (`{ uid, value, created_at, acknowledged }`) or a socket item (`{ uid,
|
||||
* notification, created_at? }`). A push carries no `acknowledged` and is
|
||||
* unread — the server only pushes what the user hasn't dismissed. `null`
|
||||
* for anything without a uid — there is nothing to acknowledge it by.
|
||||
* for anything without a uid — there is nothing to acknowledge it by — and
|
||||
* for senders the center doesn't show.
|
||||
*
|
||||
* @param {Object} raw
|
||||
* @param {number} now - Epoch ms; stands in for `created_at` when absent
|
||||
@@ -92,6 +99,7 @@ export const toEntry = (raw, now) => {
|
||||
if ( typeof uid !== 'string' || uid === '' ) return null;
|
||||
const payload = raw.notification ?? raw.value;
|
||||
const notification = payload && typeof payload === 'object' ? payload : {};
|
||||
if ( HIDDEN_SOURCES.has(notification.source) ) return null;
|
||||
const createdAt = parseCreatedAt(raw.created_at);
|
||||
return {
|
||||
uid,
|
||||
@@ -202,28 +210,14 @@ export const isUnread = (entry) => entry.readAt === null;
|
||||
export const unreadCount = (entries) => entries.filter(isUnread).length;
|
||||
|
||||
/** The senders that have a glyph of their own; anything else gets the bell. */
|
||||
const GLYPH_SOURCES = new Set(['sharing', 'worker']);
|
||||
|
||||
/**
|
||||
* Senders whose notifications go straight to the panel and badge without a
|
||||
* toast — a worker finishing a deploy is routine, not an interruption.
|
||||
*/
|
||||
const QUIET_SOURCES = new Set(['worker']);
|
||||
|
||||
/**
|
||||
* Whether an arrival should interrupt with a toast, or just wait in the panel.
|
||||
*
|
||||
* @param {NotificationPayload} notification
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const shouldToast = (notification) => ! QUIET_SOURCES.has(notification?.source);
|
||||
const GLYPH_SOURCES = new Set(['sharing']);
|
||||
|
||||
/**
|
||||
* Which glyph an entry shows, by who sent it. A plain property lookup
|
||||
* would let a `source` like `constructor` reach into the prototype.
|
||||
*
|
||||
* @param {NotificationPayload} notification
|
||||
* @returns {'sharing'|'worker'|'default'}
|
||||
* @returns {'sharing'|'default'}
|
||||
*/
|
||||
export const glyphKey = (notification) => (
|
||||
GLYPH_SOURCES.has(notification?.source) ? notification.source : 'default'
|
||||
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
planBurstToasts,
|
||||
reconcileWithServer,
|
||||
setReadAt,
|
||||
shouldToast,
|
||||
sortEntries,
|
||||
titleWithBadge,
|
||||
toEntry,
|
||||
@@ -123,6 +122,13 @@ describe('toEntry', () => {
|
||||
expect(toEntry(null, NOW)).toBeNull();
|
||||
expect(toEntry(undefined, NOW)).toBeNull();
|
||||
});
|
||||
|
||||
it('leaves out worker notifications, listed or pushed', () => {
|
||||
const worker = { title: 'Successfully deployed https://x.puter.work', source: 'worker', template: 'user-requesting-share' };
|
||||
expect(toEntry({ uid: 'a', value: worker, created_at: '2026-08-27 12:00:00' }, NOW)).toBeNull();
|
||||
expect(toEntry({ uid: 'b', notification: worker }, NOW)).toBeNull();
|
||||
expect(toEntry({ uid: 'c', notification: { ...worker, source: 'sharing' } }, NOW)).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('sortEntries', () => {
|
||||
@@ -289,7 +295,6 @@ describe('notificationTarget', () => {
|
||||
describe('glyphKey', () => {
|
||||
it('names the glyph for known senders', () => {
|
||||
expect(glyphKey({ source: 'sharing' })).toBe('sharing');
|
||||
expect(glyphKey({ source: 'worker' })).toBe('worker');
|
||||
});
|
||||
|
||||
it('falls back to the bell for anything else', () => {
|
||||
@@ -402,16 +407,3 @@ describe('planBurstToasts', () => {
|
||||
expect(folded).toBe(9);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldToast', () => {
|
||||
it('keeps worker notifications out of the toasts', () => {
|
||||
expect(shouldToast({ source: 'worker', title: 'Successfully deployed https://x.puter.work' })).toBe(false);
|
||||
});
|
||||
|
||||
it('toasts everything else', () => {
|
||||
expect(shouldToast({ source: 'sharing' })).toBe(true);
|
||||
expect(shouldToast({ source: 'billing' })).toBe(true);
|
||||
expect(shouldToast({})).toBe(true);
|
||||
expect(shouldToast(null)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user