/** * 学习进度、继续学习、收藏、笔记 - localStorage(来自 nomadlms) */ const PREFIX = "lms-"; const KEY_LAST = `${PREFIX}last`; const KEY_COMPLETED = `${PREFIX}completed`; const KEY_BOOKMARKS = `${PREFIX}bookmarks`; const KEY_NOTES = `${PREFIX}notes`; const KEY_RATING = `${PREFIX}rating`; export type LastWatched = { moduleIndex: number; lessonIndex: number; name: string }; export function getLastWatched(): LastWatched | null { if (typeof window === "undefined") return null; try { const raw = localStorage.getItem(KEY_LAST); if (!raw) return null; const d = JSON.parse(raw); if (typeof d?.moduleIndex !== "number" || typeof d?.lessonIndex !== "number") return null; return { moduleIndex: d.moduleIndex, lessonIndex: d.lessonIndex, name: d.name || "" }; } catch { return null; } } export function setLastWatched(m: number, l: number, name: string): void { if (typeof window === "undefined") return; try { localStorage.setItem(KEY_LAST, JSON.stringify({ moduleIndex: m, lessonIndex: l, name })); } catch { /* ignore */ } } export function getCompletedLessons(): Set { if (typeof window === "undefined") return new Set(); try { const raw = localStorage.getItem(KEY_COMPLETED); if (!raw) return new Set(); const arr = JSON.parse(raw); return new Set(Array.isArray(arr) ? arr : []); } catch { return new Set(); } } export function markCompleted(moduleIndex: number, lessonIndex: number): void { if (typeof window === "undefined") return; try { const set = getCompletedLessons(); set.add(`${moduleIndex}-${lessonIndex}`); localStorage.setItem(KEY_COMPLETED, JSON.stringify([...set])); window.dispatchEvent(new CustomEvent("learning:progress")); } catch { /* ignore */ } } export function getProgress(totalLessons: number): { completed: number; percent: number } { const set = getCompletedLessons(); const completed = set.size; return { completed, percent: totalLessons > 0 ? Math.round((completed / totalLessons) * 100) : 0 }; } export function getBookmarks(): Set { if (typeof window === "undefined") return new Set(); try { const raw = localStorage.getItem(KEY_BOOKMARKS); if (!raw) return new Set(); const arr = JSON.parse(raw); return new Set(Array.isArray(arr) ? arr : []); } catch { return new Set(); } } export function toggleBookmark(moduleIndex: number, lessonIndex: number): boolean { if (typeof window === "undefined") return false; try { const set = getBookmarks(); const key = `${moduleIndex}-${lessonIndex}`; if (set.has(key)) set.delete(key); else set.add(key); localStorage.setItem(KEY_BOOKMARKS, JSON.stringify([...set])); window.dispatchEvent(new CustomEvent("learning:bookmarks")); return set.has(key); } catch { return false; } } export function isBookmarked(moduleIndex: number, lessonIndex: number): boolean { return getBookmarks().has(`${moduleIndex}-${lessonIndex}`); } export function getNote(moduleIndex: number, lessonIndex: number): string { if (typeof window === "undefined") return ""; try { const raw = localStorage.getItem(KEY_NOTES); if (!raw) return ""; const map = JSON.parse(raw); return map[`${moduleIndex}-${lessonIndex}`] ?? ""; } catch { return ""; } } export function setNote(moduleIndex: number, lessonIndex: number, text: string): void { if (typeof window === "undefined") return; try { const raw = localStorage.getItem(KEY_NOTES); const map = raw ? JSON.parse(raw) : {}; map[`${moduleIndex}-${lessonIndex}`] = text; localStorage.setItem(KEY_NOTES, JSON.stringify(map)); } catch { /* ignore */ } } export function getRating(): { stars: number; comment: string } | null { if (typeof window === "undefined") return null; try { const raw = localStorage.getItem(KEY_RATING); if (!raw) return null; const d = JSON.parse(raw); return { stars: d?.stars ?? 0, comment: d?.comment ?? "" }; } catch { return null; } } export function setRating(stars: number, comment: string): void { if (typeof window === "undefined") return; try { localStorage.setItem(KEY_RATING, JSON.stringify({ stars, comment })); window.dispatchEvent(new CustomEvent("learning:rating")); } catch { /* ignore */ } }