From c6419044cb9ee495bbf4c90df08e5d4f29a019f4 Mon Sep 17 00:00:00 2001 From: Lotus CI Date: Fri, 16 Oct 2026 22:19:24 -0400 Subject: [PATCH] fix(notifications): quiet hours start and end while the app stays open (#325) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit useNotificationsQuiet evaluated the quiet-hours window (and the "Pause notifications" expiry) only at render time, and the long-lived message/invite notifiers rarely re-render, so someone leaving Lotus open overnight with quiet hours 22:00–07:00 got every sound all night (and a 30-minute pause could outlast itself). The hook now re-evaluates every 30 s. Verified headless (notifications granted, window unfocused): quiet hours set to start ~75 s after the app opened; a DM before the window notified; one inside the window, with the app left alone: before it still notified, now it's silent. Fixes #325 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5 --- src/app/hooks/useNotificationsQuiet.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/app/hooks/useNotificationsQuiet.ts b/src/app/hooks/useNotificationsQuiet.ts index 93b33ec08..d66fe89b8 100644 --- a/src/app/hooks/useNotificationsQuiet.ts +++ b/src/app/hooks/useNotificationsQuiet.ts @@ -1,3 +1,4 @@ +import { useEffect, useState } from 'react'; import { useAtomValue } from 'jotai'; import { focusAssistActiveAtom } from '../state/focusAssist'; import { manualDndAtom } from '../state/manualDnd'; @@ -6,6 +7,8 @@ import { isWithinTimeWindow } from '../utils/timeWindow'; import { useSetting } from '../state/hooks/settings'; import { settingsAtom } from '../state/settings'; +const QUIET_TICK_MS = 30_000; + export type NotificationsQuietInputs = { focusAssistActive: boolean; manualDnd: boolean; @@ -40,6 +43,15 @@ export function isNotificationsQuiet({ // Live-atoms/settings-backed version of `isNotificationsQuiet` for use in // components. export function useNotificationsQuiet(): boolean { + // [Gitea #325] The quiet-hours window and the snooze expiry depend on the + // clock, but the long-lived notifiers using this hook rarely re-render, so a + // window starting (or a 30-minute pause ending) while the app stayed open was + // never noticed. Re-evaluate on a tick, like the night-light overlay. + const [, setTick] = useState(0); + useEffect(() => { + const id = setInterval(() => setTick((t) => t + 1), QUIET_TICK_MS); + return () => clearInterval(id); + }, []); const focusAssistActive = useAtomValue(focusAssistActiveAtom); const manualDnd = useAtomValue(manualDndAtom); const snoozeUntil = useAtomValue(notificationSnoozeUntilAtom); -- 2.47.3