105 lines
2.5 KiB
TypeScript
105 lines
2.5 KiB
TypeScript
import React from 'react';
|
|
|
|
function storageKey(slug: string) {
|
|
return `guestTasks_${slug}`;
|
|
}
|
|
|
|
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(slug: string | undefined) {
|
|
const [completed, setCompleted] = React.useState<number[]>([]);
|
|
const [hydrated, setHydrated] = React.useState(false);
|
|
|
|
React.useEffect(() => {
|
|
if (!slug) {
|
|
setCompleted([]);
|
|
setHydrated(true);
|
|
return;
|
|
}
|
|
try {
|
|
const stored = window.localStorage.getItem(storageKey(slug));
|
|
setCompleted(parseStored(stored));
|
|
} catch (error) {
|
|
console.warn('Failed to read task progress', error);
|
|
setCompleted([]);
|
|
} finally {
|
|
setHydrated(true);
|
|
}
|
|
}, [slug]);
|
|
|
|
const persist = React.useCallback(
|
|
(next: number[]) => {
|
|
if (!slug) return;
|
|
setCompleted(next);
|
|
try {
|
|
window.localStorage.setItem(storageKey(slug), JSON.stringify(next));
|
|
} catch (error) {
|
|
console.warn('Failed to persist task progress', error);
|
|
}
|
|
},
|
|
[slug]
|
|
);
|
|
|
|
const markCompleted = React.useCallback(
|
|
(taskId: number) => {
|
|
if (!slug || !Number.isInteger(taskId)) {
|
|
return;
|
|
}
|
|
setCompleted((prev) => {
|
|
if (prev.includes(taskId)) {
|
|
return prev;
|
|
}
|
|
const next = [...prev, taskId];
|
|
try {
|
|
window.localStorage.setItem(storageKey(slug), JSON.stringify(next));
|
|
} catch (error) {
|
|
console.warn('Failed to persist task progress', error);
|
|
}
|
|
return next;
|
|
});
|
|
},
|
|
[slug]
|
|
);
|
|
|
|
const clearProgress = React.useCallback(() => {
|
|
if (!slug) return;
|
|
setCompleted([]);
|
|
try {
|
|
window.localStorage.removeItem(storageKey(slug));
|
|
} catch (error) {
|
|
console.warn('Failed to clear task progress', error);
|
|
}
|
|
}, [slug]);
|
|
|
|
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,
|
|
};
|
|
}
|