feat: implement tenant OAuth flow and guest achievements
This commit is contained in:
104
resources/js/guest/hooks/useGuestTaskProgress.ts
Normal file
104
resources/js/guest/hooks/useGuestTaskProgress.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user