First commit
This commit is contained in:
86
lib/progress/localProgress.ts
Normal file
86
lib/progress/localProgress.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
export type CourseProgress = {
|
||||
lastLessonId: string | null;
|
||||
completedLessonIds: string[];
|
||||
};
|
||||
|
||||
const STORAGE_KEY = "acve.local-progress.v1";
|
||||
const PROGRESS_UPDATED_EVENT = "acve:progress-updated";
|
||||
|
||||
type ProgressStore = Record<string, Record<string, CourseProgress>>;
|
||||
|
||||
const defaultProgress: CourseProgress = {
|
||||
lastLessonId: null,
|
||||
completedLessonIds: [],
|
||||
};
|
||||
|
||||
const isBrowser = () => typeof window !== "undefined";
|
||||
|
||||
const readStore = (): ProgressStore => {
|
||||
if (!isBrowser()) return {};
|
||||
|
||||
const raw = window.localStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) return {};
|
||||
|
||||
try {
|
||||
return JSON.parse(raw) as ProgressStore;
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const writeStore = (store: ProgressStore) => {
|
||||
if (!isBrowser()) return;
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(store));
|
||||
window.dispatchEvent(new Event(PROGRESS_UPDATED_EVENT));
|
||||
};
|
||||
|
||||
const getOrCreateProgress = (store: ProgressStore, userId: string, courseSlug: string) => {
|
||||
if (!store[userId]) {
|
||||
store[userId] = {};
|
||||
}
|
||||
if (!store[userId][courseSlug]) {
|
||||
store[userId][courseSlug] = { ...defaultProgress };
|
||||
}
|
||||
return store[userId][courseSlug];
|
||||
};
|
||||
|
||||
export const getCourseProgress = (userId: string, courseSlug: string): CourseProgress => {
|
||||
const store = readStore();
|
||||
return store[userId]?.[courseSlug] ?? { ...defaultProgress };
|
||||
};
|
||||
|
||||
export const getCourseProgressPercent = (
|
||||
userId: string,
|
||||
courseSlug: string,
|
||||
totalLessons: number,
|
||||
): number => {
|
||||
if (totalLessons <= 0) return 0;
|
||||
const progress = getCourseProgress(userId, courseSlug);
|
||||
return Math.round((progress.completedLessonIds.length / totalLessons) * 100);
|
||||
};
|
||||
|
||||
export const setLastLesson = (userId: string, courseSlug: string, lessonId: string) => {
|
||||
const store = readStore();
|
||||
const progress = getOrCreateProgress(store, userId, courseSlug);
|
||||
progress.lastLessonId = lessonId;
|
||||
writeStore(store);
|
||||
};
|
||||
|
||||
export const markLessonComplete = (userId: string, courseSlug: string, lessonId: string) => {
|
||||
const store = readStore();
|
||||
const progress = getOrCreateProgress(store, userId, courseSlug);
|
||||
if (!progress.completedLessonIds.includes(lessonId)) {
|
||||
progress.completedLessonIds = [...progress.completedLessonIds, lessonId];
|
||||
}
|
||||
progress.lastLessonId = lessonId;
|
||||
writeStore(store);
|
||||
};
|
||||
|
||||
export const clearCourseProgress = (userId: string, courseSlug: string) => {
|
||||
const store = readStore();
|
||||
if (!store[userId]) return;
|
||||
delete store[userId][courseSlug];
|
||||
writeStore(store);
|
||||
};
|
||||
|
||||
export const progressUpdatedEventName = PROGRESS_UPDATED_EVENT;
|
||||
Reference in New Issue
Block a user