/** * Frontend Storage Utility * * Provides a centralized API for storing and retrieving data in browser storage. * Supports both sessionStorage and localStorage with type-safe operations. * * Features: * - Type-safe storage operations * - Automatic JSON serialization/deserialization * - Error handling with fallback values * - Namespaced keys to avoid collisions * - Support for both session and local storage */ /** * Storage type enum */ export enum StorageType { SESSION = "session", LOCAL = "local" } /** * Storage keys enum - Add new keys here */ export enum StorageKey { // Game state GAME_STATE = "trigoGameState", // AI settings AI_PLAYER_COLOR = "trigoAIPlayerColor", // User preferences (examples for future use) // THEME = "trigoTheme", // LANGUAGE = "trigoLanguage", // BOARD_SHAPE = "trigoBoardShape", // CAMERA_POSITION = "trigoCameraPosition", } /** * Storage configuration */ interface StorageConfig { type: StorageType; prefix?: string; // Optional namespace prefix for all keys } /** * Default configuration */ const DEFAULT_CONFIG: StorageConfig = { type: StorageType.SESSION, prefix: "" // No prefix by default, keys already have 'trigo' prefix }; /** * Get the appropriate storage instance */ function getStorage(type: StorageType): Storage { return type === StorageType.SESSION ? sessionStorage : localStorage; } /** * Build full storage key with optional prefix */ function buildKey(key: StorageKey, prefix?: string): string { return prefix ? `${prefix}${key}` : key; } /** * Storage Manager Class */ export class StorageManager { private config: StorageConfig; constructor(config: Partial = {}) { this.config = { ...DEFAULT_CONFIG, ...config }; } /** * Get storage instance based on configuration */ private getStorageInstance(): Storage { return getStorage(this.config.type); } /** * Set a value in storage * @param key - Storage key * @param value - Value to store (will be JSON stringified) * @returns true if successful, false otherwise */ set(key: StorageKey, value: T): boolean { try { const storage = this.getStorageInstance(); const fullKey = buildKey(key, this.config.prefix); const serialized = JSON.stringify(value); storage.setItem(fullKey, serialized); return true; } catch (error) { console.warn(`[Storage] Failed to set ${key}:`, error); return false; } } /** * Get a value from storage * @param key - Storage key * @param defaultValue - Default value if key doesn't exist or parsing fails * @returns Stored value or default value */ get(key: StorageKey, defaultValue: T): T { try { const storage = this.getStorageInstance(); const fullKey = buildKey(key, this.config.prefix); const item = storage.getItem(fullKey); if (item === null) { return defaultValue; } return JSON.parse(item) as T; } catch (error) { console.warn(`[Storage] Failed to get ${key}:`, error); return defaultValue; } } /** * Get a string value directly (no JSON parsing) * Useful for simple string values */ getString(key: StorageKey, defaultValue: string = ""): string { try { const storage = this.getStorageInstance(); const fullKey = buildKey(key, this.config.prefix); return storage.getItem(fullKey) ?? defaultValue; } catch (error) { console.warn(`[Storage] Failed to get string ${key}:`, error); return defaultValue; } } /** * Set a string value directly (no JSON stringification) * Useful for simple string values */ setString(key: StorageKey, value: string): boolean { try { const storage = this.getStorageInstance(); const fullKey = buildKey(key, this.config.prefix); storage.setItem(fullKey, value); return true; } catch (error) { console.warn(`[Storage] Failed to set string ${key}:`, error); return false; } } /** * Remove a value from storage * @param key - Storage key * @returns true if successful, false otherwise */ remove(key: StorageKey): boolean { try { const storage = this.getStorageInstance(); const fullKey = buildKey(key, this.config.prefix); storage.removeItem(fullKey); return true; } catch (error) { console.warn(`[Storage] Failed to remove ${key}:`, error); return false; } } /** * Check if a key exists in storage */ has(key: StorageKey): boolean { try { const storage = this.getStorageInstance(); const fullKey = buildKey(key, this.config.prefix); return storage.getItem(fullKey) !== null; } catch (error) { console.warn(`[Storage] Failed to check ${key}:`, error); return false; } } /** * Clear all items from storage * WARNING: This clears ALL items in the storage, not just trigo-related ones */ clear(): boolean { try { const storage = this.getStorageInstance(); storage.clear(); return true; } catch (error) { console.warn("[Storage] Failed to clear storage:", error); return false; } } /** * Get the current storage type */ getStorageType(): StorageType { return this.config.type; } /** * Switch storage type */ setStorageType(type: StorageType): void { this.config.type = type; } } /** * Default storage manager instance (sessionStorage) */ export const storage = new StorageManager(); /** * Local storage manager instance * Note: Named localStorageManager to avoid conflict with built-in localStorage */ export const localStorageManager = new StorageManager({ type: StorageType.LOCAL }); /** * Example helper functions for future use: * * export function saveTheme(theme: "light" | "dark"): boolean { * return localStorageManager.setString(StorageKey.THEME, theme); * } * * export function loadTheme(): "light" | "dark" { * const stored = localStorageManager.getString(StorageKey.THEME); * return stored === "dark" ? "dark" : "light"; * } * * export function saveBoardShape(shape: { x: number; y: number; z: number }): boolean { * return storage.set(StorageKey.BOARD_SHAPE, shape); * } * * export function loadBoardShape(): { x: number; y: number; z: number } | null { * return storage.get(StorageKey.BOARD_SHAPE, null); * } */