/** * Централизованная работа с localStorage * Все ключи и операции в одном месте */ import { clearAllFinalAnswers, listDrafts, clearDraft } from './drafts' const isBrowser = () => typeof window !== 'undefined' // Ключи localStorage export const STORAGE_KEYS = { USER_ID: 'challengeUserId', NICKNAME: 'challengeNickname', WORKPLACE_NUMBER: 'challengeWorkplaceNumber', SELECTED_CHAIN_ID: 'challengeSelectedChainId', SELECTED_TASK_ID: 'challengeSelectedTaskId', } as const // Вспомогательные функции для ключей const getFurthestTaskKey = (chainId: string) => `challengeFurthestTask_${chainId}` const getSkippedTasksKey = (chainId: string) => `challengeSkippedTasks_${chainId}` // Получение значений export const storage = { getUserId: (): string | null => { if (!isBrowser()) return null return localStorage.getItem(STORAGE_KEYS.USER_ID) }, getNickname: (): string | null => { if (!isBrowser()) return null return localStorage.getItem(STORAGE_KEYS.NICKNAME) }, getWorkplaceNumber: (): string | null => { if (!isBrowser()) return null return localStorage.getItem(STORAGE_KEYS.WORKPLACE_NUMBER) }, getSelectedChainId: (): string | null => { if (!isBrowser()) return null return localStorage.getItem(STORAGE_KEYS.SELECTED_CHAIN_ID) }, getSelectedTaskId: (): string | null => { if (!isBrowser()) return null return localStorage.getItem(STORAGE_KEYS.SELECTED_TASK_ID) }, // Установка значений setUserId: (value: string): void => { if (!isBrowser()) return localStorage.setItem(STORAGE_KEYS.USER_ID, value) }, setNickname: (value: string): void => { if (!isBrowser()) return localStorage.setItem(STORAGE_KEYS.NICKNAME, value) }, setWorkplaceNumber: (value: string): void => { if (!isBrowser()) return localStorage.setItem(STORAGE_KEYS.WORKPLACE_NUMBER, value) }, setSelectedChainId: (value: string): void => { if (!isBrowser()) return localStorage.setItem(STORAGE_KEYS.SELECTED_CHAIN_ID, value) }, setSelectedTaskId: (value: string): void => { if (!isBrowser()) return localStorage.setItem(STORAGE_KEYS.SELECTED_TASK_ID, value) }, // Удаление значений removeUserId: (): void => { if (!isBrowser()) return localStorage.removeItem(STORAGE_KEYS.USER_ID) }, removeNickname: (): void => { if (!isBrowser()) return localStorage.removeItem(STORAGE_KEYS.NICKNAME) }, removeWorkplaceNumber: (): void => { if (!isBrowser()) return localStorage.removeItem(STORAGE_KEYS.WORKPLACE_NUMBER) }, removeSelectedChainId: (): void => { if (!isBrowser()) return localStorage.removeItem(STORAGE_KEYS.SELECTED_CHAIN_ID) }, removeSelectedTaskId: (): void => { if (!isBrowser()) return localStorage.removeItem(STORAGE_KEYS.SELECTED_TASK_ID) }, // Полная очистка при выходе (кроме номера рабочего места) clearAll: (): void => { if (!isBrowser()) return localStorage.removeItem(STORAGE_KEYS.USER_ID) localStorage.removeItem(STORAGE_KEYS.NICKNAME) // Номер рабочего места НЕ удаляем localStorage.removeItem(STORAGE_KEYS.SELECTED_CHAIN_ID) localStorage.removeItem(STORAGE_KEYS.SELECTED_TASK_ID) // Очищаем все прогрессы по цепочкам storage.clearAllChainProgress() // Очищаем все финальные ответы clearAllFinalAnswers() // Очищаем все черновики const drafts = listDrafts() drafts.forEach(taskId => clearDraft(taskId)) }, // Очистка всех прогрессов по цепочкам clearAllChainProgress: (): void => { if (!isBrowser()) return // Перебираем все ключи localStorage и удаляем те, что начинаются с challengeFurthestTask_ или challengeSkippedTasks_ const keysToRemove: string[] = [] for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i) if (key && (key.startsWith('challengeFurthestTask_') || key.startsWith('challengeSkippedTasks_'))) { keysToRemove.push(key) } } keysToRemove.forEach(key => localStorage.removeItem(key)) }, // Очистка данных сессии (цепочка, задание) без выхода clearSessionData: (): void => { if (!isBrowser()) return localStorage.removeItem(STORAGE_KEYS.SELECTED_CHAIN_ID) localStorage.removeItem(STORAGE_KEYS.SELECTED_TASK_ID) }, // Получение самого дальнего достигнутого индекса задания в цепочке getFurthestTaskIndex: (chainId: string): number => { if (!isBrowser()) return 0 const value = localStorage.getItem(getFurthestTaskKey(chainId)) return value ? parseInt(value, 10) : 0 }, // Установка самого дальнего достигнутого индекса задания setFurthestTaskIndex: (chainId: string, index: number): void => { if (!isBrowser()) return const current = storage.getFurthestTaskIndex(chainId) // Обновляем только если новый индекс больше текущего if (index > current) { localStorage.setItem(getFurthestTaskKey(chainId), index.toString()) } }, // Очистка прогресса цепочки clearChainProgress: (chainId: string): void => { if (!isBrowser()) return localStorage.removeItem(getFurthestTaskKey(chainId)) }, // Получение пропущенных заданий для цепочки getSkippedTasks: (chainId: string): string[] => { if (!isBrowser()) return [] const value = localStorage.getItem(getSkippedTasksKey(chainId)) return value ? JSON.parse(value) : [] }, // Добавление задания в список пропущенных addSkippedTask: (chainId: string, taskId: string): void => { if (!isBrowser()) return const skipped = storage.getSkippedTasks(chainId) if (!skipped.includes(taskId)) { skipped.push(taskId) localStorage.setItem(getSkippedTasksKey(chainId), JSON.stringify(skipped)) } }, // Удаление задания из списка пропущенных (когда оно выполнено) removeSkippedTask: (chainId: string, taskId: string): void => { if (!isBrowser()) return const skipped = storage.getSkippedTasks(chainId) const filtered = skipped.filter(id => id !== taskId) localStorage.setItem(getSkippedTasksKey(chainId), JSON.stringify(filtered)) }, // Проверка, пропущено ли задание isTaskSkipped: (chainId: string, taskId: string): boolean => { if (!isBrowser()) return false const skipped = storage.getSkippedTasks(chainId) return skipped.includes(taskId) }, // Очистка всех пропущенных заданий цепочки clearSkippedTasks: (chainId: string): void => { if (!isBrowser()) return localStorage.removeItem(getSkippedTasksKey(chainId)) }, }