From eb09e7c475b6ccce614e013fcaf9538dacc557fa Mon Sep 17 00:00:00 2001 From: Frank Karlitschek Date: Tue, 28 Apr 2026 14:56:37 +0200 Subject: [PATCH] feat(ui): Do Not Disturb toggle and while-DND summary banner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a quick Do Not Disturb toggle to the top of the notification panel. While DND is on, the bell dot indicator is suppressed and browser notifications are not raised. The state is persisted in localStorage so it survives reloads. When DND is turned back off, a one-shot banner reports how many new notifications arrived during the DND window so they aren't silently missed. * NotificationsApp.vue: panel header with toggle, banner, isDnd data, toggleDnd method, dndBannerText computed, showBrowserNotifications now honors isDnd * styles.scss: panel header and warning-tinted banner Note: this PR only suppresses the dot indicator, not a numeric count badge — the count badge ships in a separate PR. Once both are merged, DND should also zero the badge count. Signed-off-by: Frank Karlitschek --- src/NotificationsApp.vue | 52 +++++++++++++++++++++++++++++++++++++++- src/styles/styles.scss | 31 ++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/NotificationsApp.vue b/src/NotificationsApp.vue index d494e609a..296e39356 100644 --- a/src/NotificationsApp.vue +++ b/src/NotificationsApp.vue @@ -15,12 +15,34 @@
+ +
+ {{ t('notifications', 'Notifications') }} + + + +
+ +
+ {{ dndBannerText }} + + + +
+ 0 shows banner) */ + dndBanner: 0, } }, @@ -195,9 +224,17 @@ export default { return this.backgroundFetching && this.webNotificationsGranted && this.userStatus !== 'dnd' + && !this.isDnd && this.tabId === this.lastTabId }, + dndBannerText() { + const c = this.dndBanner + return c === 1 + ? t('notifications', '1 new notification received while in Do Not Disturb') + : t('notifications', '{count} new notifications received while in Do Not Disturb', { count: c }) + }, + emptyContentMessage() { if (this.webNotificationsGranted === null) { return t('notifications', 'Requesting browser permissions to show notifications') @@ -347,6 +384,19 @@ export default { setCurrentTabAsActive(this.tabId) }, + toggleDnd() { + if (!this.isDnd) { + // Turning ON: snapshot current max ID so we can count new arrivals on re-enable + this._dndMaxIdAtEnable = this.notifications.reduce((max, n) => Math.max(max, n.notificationId), 0) + } else { + // Turning OFF: surface a banner for any notifications that arrived while DND was active + const newCount = this.notifications.filter(n => n.notificationId > (this._dndMaxIdAtEnable || 0)).length + if (newCount > 0) this.dndBanner = newCount + } + this.isDnd = !this.isDnd + localStorage.setItem('notifications:dnd', JSON.stringify(this.isDnd)) + }, + /** * Update the title to show * if there are new notifications * diff --git a/src/styles/styles.scss b/src/styles/styles.scss index 96524b4a9..2e4acd305 100644 --- a/src/styles/styles.scss +++ b/src/styles/styles.scss @@ -123,3 +123,34 @@ svg { } } } + +// Panel header with the Do Not Disturb toggle +.notification-panel-header { + display: flex; + align-items: center; + padding: 2px 4px 2px 16px; + border-bottom: 1px solid var(--color-border); + + .notification-panel-title { + flex: 1; + font-size: 13px; + font-weight: 600; + color: var(--color-text-maxcontrast); + } + + .dnd-active { + color: var(--color-error) !important; + } +} + +// "New while DND" summary banner shown after disabling DND +.notification-dnd-banner { + display: flex; + align-items: center; + justify-content: space-between; + padding: 4px 4px 4px 16px; + border-bottom: 1px solid var(--color-border); + background: color-mix(in srgb, var(--color-warning) 10%, var(--color-main-background)); + font-size: 12px; + color: var(--color-text-maxcontrast); +}