fix(settings): check push again when the user comes back to the tab

The notification settings read the push state once, when they opened.
Push turned off meanwhile - in the browser's site settings, or from
another tab - still showed as on until the page was reloaded. The state
is now read again when the window regains focus or the tab becomes
visible, unless an enable or disable is in progress or has just failed.
A failed check reads as off instead of leaving the panel unchanged.
This commit is contained in:
lucamzanon
2026-10-01 12:43:40 +02:00
parent 410d6b3532
commit ce3b12aa21
2 changed files with 40 additions and 6 deletions
@@ -103,6 +103,20 @@ describe('NotificationSettings - pushNotifyInboxOnly', () => {
);
});
it('checks push again when the user comes back to the tab', async () => {
Object.defineProperty(document, 'visibilityState', { value: 'visible', configurable: true });
const { findByText } = render(<NotificationSettings />);
await findByText('email.inbox_only');
const check = webPush.isWebPushEnabled as ReturnType<typeof vi.fn>;
await waitFor(() => expect(check).toHaveBeenCalled());
const before = check.mock.calls.length;
// Push may have been turned off elsewhere (site settings, another tab) meanwhile.
await act(async () => { window.dispatchEvent(new Event('focus')); });
await waitFor(() => expect(check.mock.calls.length).toBeGreaterThan(before));
});
it('does not re-sync when push is disabled on this device', async () => {
(webPush.isWebPushEnabled as ReturnType<typeof vi.fn>).mockResolvedValueOnce(false);
render(<NotificationSettings />);
+26 -6
View File
@@ -80,16 +80,37 @@ export function NotificationSettings() {
const activeRelayLabel =
relayOptions.find((option) => option.url === activeRelayUrl)?.label ?? activeRelayUrl;
const busy = pushStatus.kind === 'busy';
useEffect(() => {
if (!supported) return;
if (!client) return;
const accountId = client.getAccountId();
if (!accountId) return;
void (async () => {
const enabled = await isWebPushEnabled(accountId);
setPushStatus(enabled ? { kind: 'enabled' } : { kind: 'idle' });
})();
}, [supported, client]);
let cancelled = false;
const refreshStatus = async () => {
try {
const enabled = await isWebPushEnabled(accountId);
if (!cancelled) {
setPushStatus(enabled ? { kind: 'enabled' } : { kind: 'idle' });
}
} catch {
if (!cancelled) setPushStatus({ kind: 'idle' });
}
};
const onVisible = () => {
if (document.visibilityState === 'visible') void refreshStatus();
};
if (pushStatus.kind === 'busy' || pushStatus.kind === 'error') return;
void refreshStatus();
window.addEventListener('focus', onVisible);
document.addEventListener('visibilitychange', onVisible);
return () => {
cancelled = true;
window.removeEventListener('focus', onVisible);
document.removeEventListener('visibilitychange', onVisible);
};
}, [supported, client, pushStatus.kind]);
// The device list is worth loading even when push is off on this browser -
// revoking a stale registration left on another device is exactly what you
@@ -108,7 +129,6 @@ export function NotificationSettings() {
void refreshDevices();
}, [refreshDevices]);
const busy = pushStatus.kind === 'busy';
const pushEnabled = pushStatus.kind === 'enabled';
// In the static Lite build push is off by design (no service worker, see
// components/service-worker-registration.tsx), not a browser limitation.