'use client';
import {
useEffect,
useReducer,
useSyncExternalStore,
} from 'react';
import { Bell, BellOff } from 'lucide-react';
import { FACTIONS } from '@/game/catalog';
import { initialActionNotices, updateActionNotices, type AutomaticActionEvent, type QueuedActionNotice } from '@/lib/action-notice-queue';
import { Button } from './ui/button';
export type { AutomaticActionEvent } from '@/lib/action-notice-queue';
const PREFERENCE_KEY = 'dune.automatic-action-notices';
const PREFERENCE_EVENT = 'dune-automatic-action-notices-preference';
let memoryPreference: boolean | undefined;
function readPreference() {
if (memoryPreference !== undefined) return memoryPreference;
try {
return localStorage.getItem(PREFERENCE_KEY) !== 'false';
} catch {
return true;
}
}
function subscribePreference(listener: () => void) {
window.addEventListener(PREFERENCE_EVENT, listener);
return () => window.removeEventListener(PREFERENCE_EVENT, listener);
}
const serverPreference = () => true;
/** Cosmetic feedback only. Supply public events with increasing, room-local sequences. */
export function AutomaticActionNotice({
events,
}: {
events: readonly AutomaticActionEvent[];
}) {
const enabled = useSyncExternalStore(
subscribePreference,
readPreference,
serverPreference,
);
const [presentation, updatePresentation] = useReducer(updateActionNotices, events, initialActionNotices);
useEffect(() => {
updatePresentation({
type: 'events', events, enabled,
visible: document.visibilityState !== 'hidden',
});
}, [events, enabled]);
useEffect(() => {
const hide = () => {
if (document.visibilityState !== 'hidden') return;
updatePresentation({ type: 'hide' });
};
document.addEventListener('visibilitychange', hide);
return () => document.removeEventListener('visibilitychange', hide);
}, []);
const toggle = () => {
const next = !enabled;
memoryPreference = next;
updatePresentation({ type: 'clear' });
try {
localStorage.setItem(PREFERENCE_KEY, String(next));
memoryPreference = undefined;
} catch {
// Do not turn a cosmetic preference into an error or blocking dialog.
}
window.dispatchEvent(new Event(PREFERENCE_EVENT));
};
return (
<>
>
);
}
/** The notice alone accepts input; it does not trap or move keyboard focus. */
export function ActionNoticeCard({ notice, onContinue }: { notice: QueuedActionNotice; onContinue: () => void }) {
const house = FACTIONS.find((candidate) => candidate.id === notice.faction);
return
{house?.name ?? 'Automatic action'}
{notice.name}
{notice.count > 1 &&
{notice.count - 1} earlier {notice.count === 2 ? 'action' : 'actions'} also completed. Details are in the table chronicle.
}
;
}