import type { FunnelAnswers } from "./types"; /** * Структура данных для хранения состояния воронки */ export interface FunnelRuntimeState { answers: FunnelAnswers; history: string[]; version: number; timestamp?: number; // Для отслеживания времени последнего обновления } /** * Префикс для ключей в localStorage */ const STORAGE_PREFIX = "funnel_state_"; /** * Время жизни данных в localStorage (7 дней) */ const STORAGE_TTL = 7 * 24 * 60 * 60 * 1000; /** * Проверяет доступность localStorage */ function isLocalStorageAvailable(): boolean { try { const testKey = "__storage_test__"; localStorage.setItem(testKey, "test"); localStorage.removeItem(testKey); return true; } catch { return false; } } /** * Получает ключ для localStorage по ID воронки */ function getStorageKey(funnelId: string): string { return `${STORAGE_PREFIX}${funnelId}`; } /** * Сохраняет состояние воронки в localStorage */ export function saveFunnelState( funnelId: string, state: FunnelRuntimeState ): void { if (!isLocalStorageAvailable()) { return; } try { const dataToSave: FunnelRuntimeState = { ...state, timestamp: Date.now(), }; localStorage.setItem(getStorageKey(funnelId), JSON.stringify(dataToSave)); } catch (error) { console.warn("Failed to save funnel state to localStorage:", error); } } /** * Загружает состояние воронки из localStorage */ export function loadFunnelState(funnelId: string): FunnelRuntimeState | null { if (!isLocalStorageAvailable()) { return null; } try { const stored = localStorage.getItem(getStorageKey(funnelId)); if (!stored) { return null; } const data = JSON.parse(stored) as FunnelRuntimeState; // Проверяем TTL if (data.timestamp) { const age = Date.now() - data.timestamp; if (age > STORAGE_TTL) { // Данные устарели, удаляем clearFunnelState(funnelId); return null; } } return data; } catch (error) { console.warn("Failed to load funnel state from localStorage:", error); return null; } } /** * Очищает состояние воронки из localStorage */ export function clearFunnelState(funnelId: string): void { if (!isLocalStorageAvailable()) { return; } try { localStorage.removeItem(getStorageKey(funnelId)); } catch (error) { console.warn("Failed to clear funnel state from localStorage:", error); } } /** * Очищает все устаревшие состояния воронок */ export function clearExpiredFunnelStates(): void { if (!isLocalStorageAvailable()) { return; } try { const now = Date.now(); const keysToRemove: string[] = []; // Проходим по всем ключам в localStorage for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (!key?.startsWith(STORAGE_PREFIX)) { continue; } const value = localStorage.getItem(key); if (!value) { continue; } try { const data = JSON.parse(value) as FunnelRuntimeState; if (data.timestamp && now - data.timestamp > STORAGE_TTL) { keysToRemove.push(key); } } catch { // Если не удалось распарсить - удаляем keysToRemove.push(key); } } // Удаляем устаревшие ключи for (const key of keysToRemove) { localStorage.removeItem(key); } } catch (error) { console.warn("Failed to clear expired funnel states:", error); } }