Files
fotospiel-app/resources/js/guest/hooks/useGuestTaskProgress.ts

94 lines
2.3 KiB
TypeScript

import React from 'react';
export const TASK_BADGE_TARGET = 5;
function storageKey(eventKey: string) {
return `guestTasks_${eventKey}`;
}
function parseStored(value: string | null) {
if (!value) {
return [] as number[];
}
try {
const parsed = JSON.parse(value);
if (Array.isArray(parsed)) {
return parsed.filter((item) => Number.isInteger(item)) as number[];
}
return [];
} catch (error) {
console.warn('Failed to parse task progress from storage', error);
return [];
}
}
export function useGuestTaskProgress(eventKey: string | undefined) {
const [completed, setCompleted] = React.useState<number[]>([]);
const [hydrated, setHydrated] = React.useState(false);
React.useEffect(() => {
if (!eventKey) {
setCompleted([]);
setHydrated(true);
return;
}
try {
const stored = window.localStorage.getItem(storageKey(eventKey));
setCompleted(parseStored(stored));
} catch (error) {
console.warn('Failed to read task progress', error);
setCompleted([]);
} finally {
setHydrated(true);
}
}, [eventKey]);
const markCompleted = React.useCallback(
(taskId: number) => {
if (!eventKey || !Number.isInteger(taskId)) {
return;
}
setCompleted((prev) => {
if (prev.includes(taskId)) {
return prev;
}
const next = [...prev, taskId];
try {
window.localStorage.setItem(storageKey(eventKey), JSON.stringify(next));
} catch (error) {
console.warn('Failed to persist task progress', error);
}
return next;
});
},
[eventKey]
);
const clearProgress = React.useCallback(() => {
if (!eventKey) return;
setCompleted([]);
try {
window.localStorage.removeItem(storageKey(eventKey));
} catch (error) {
console.warn('Failed to clear task progress', error);
}
}, [eventKey]);
const isCompleted = React.useCallback(
(taskId: number | null | undefined) => {
if (!Number.isInteger(taskId)) return false;
return completed.includes(taskId as number);
},
[completed]
);
return {
hydrated,
completed,
completedCount: completed.length,
markCompleted,
clearProgress,
isCompleted,
};
}