SkateSpots/src/hooks/useNotificationPrefs.ts
SkateSpots 63dc4bee7d feat(notify): opt-in on-device OS notifications (Phase 1)
Mirrors the activity the app already computes (bell, crews, DMs, newsletter)
to the OS notification shade, with no new relay traffic and no new network
destination.

- src/lib/osNotify.ts: one delivery API. Web uses the Notification API; native
  uses @capacitor/local-notifications (lazily imported, so the web bundle never
  pays for it). Tap-through navigates in-app.
- src/components/OsNotificationBridge.tsx: mounted once in Layout, inert until
  opted in. Per-source silent priming + a seen-id set means history is never
  replayed as a flood, and it stays quiet while the app is visible (the bell
  already covers that).
- DMs notify from the gift-wrap ENVELOPE only (src/hooks/useDmEnvelopes.ts,
  no decryption) — so Amber is never prompted just to be told you have a
  message. Preview is honestly generic.
- src/lib/notificationPrefs.ts + useNotificationPrefs: device-local, opt-in,
  default off, per-category toggles. Settings -> Notifications UI.

Deliberately local-only: a fully closed app needs a server push, which is not
built (Phase 2 = on-device background runner; Phase 3 = content-free FCM).
2026-09-29 12:59:13 +07:00

30 lines
996 B
TypeScript

import { useMemo } from 'react';
import { useLocalStorage } from '@/hooks/useLocalStorage';
import {
DEFAULT_NOTIFICATION_PREFS,
NOTIFICATION_PREFS_KEY,
type NotificationPrefs,
} from '@/lib/notificationPrefs';
/**
* Read/write the device-local notification preferences.
*
* Returns a memoized object (stable identity until the stored value changes) so
* callers can safely put it in effect dependency arrays — an object literal that
* is fresh every render would otherwise re-run those effects on every render.
* Missing keys fall back to the defaults, so adding a new category later does
* not leave existing users with `undefined`.
*/
export function useNotificationPrefs() {
const [stored, setPrefs] = useLocalStorage<NotificationPrefs>(
NOTIFICATION_PREFS_KEY,
DEFAULT_NOTIFICATION_PREFS,
);
const prefs = useMemo<NotificationPrefs>(
() => ({ ...DEFAULT_NOTIFICATION_PREFS, ...(stored ?? {}) }),
[stored],
);
return [prefs, setPrefs] as const;
}