SkateSpots/src/hooks/useSasquatchHidden.ts
SkateSpots 8cdc41cc15 Quieter Sasquatch bubble: no perpetual pulse, hideable, map-aware
Addresses "the floating Sasquatch flashing is annoying":

- Pulse is now state-driven: the animate-ping halo shows only when there's an
  unread tip (message), never perpetually; the unread dot matches.
- Honors prefers-reduced-motion (motion-reduce:hidden).
- Map-aware: Layout passes isFullBleed, so on the map the bubble runs compact
  (smaller, no glow, no pulse) instead of competing with map controls.
- Hideable: a ✕ on the FAB hides it (useSasquatchHidden, localStorage
  sasquatch-hidden, same-tab synced via an event), with a "Show Sasquatch
  assistant" toggle in Settings → Appearance to bring it back.
2026-10-04 14:20:46 +07:00

31 lines
1 KiB
TypeScript

import { useCallback, useEffect } from 'react';
import { useLocalStorage } from '@/hooks/useLocalStorage';
const KEY = 'sasquatch-hidden';
const EVENT = 'sasquatch:hiddenchange';
/**
* Whether the floating Sasquatch assistant is hidden. Device-local (like the
* other visibility prefs) and synced **within the same tab** via a custom event
* — `useLocalStorage` only syncs across tabs, so the Settings toggle and the
* guide itself need this bridge to stay in step.
*/
export function useSasquatchHidden(): [boolean, (value: boolean) => void] {
const [hidden, setHiddenRaw] = useLocalStorage(KEY, false);
useEffect(() => {
const handler = (e: Event) => setHiddenRaw(!!(e as CustomEvent<boolean>).detail);
window.addEventListener(EVENT, handler);
return () => window.removeEventListener(EVENT, handler);
}, [setHiddenRaw]);
const setHidden = useCallback(
(value: boolean) => {
setHiddenRaw(value);
window.dispatchEvent(new CustomEvent(EVENT, { detail: value }));
},
[setHiddenRaw],
);
return [hidden, setHidden];
}