import { ChatState } from "@/types/bot"; import { ConversationStep, Message } from "@/types/chat"; import { addBreadcrumb, captureStructuredError } from "@/utils/sentry"; import { hasValidSession, loadSessionData, saveChatState, saveCurrentStep, saveMessages, loadMessages, updateSessionTimestamp, getDoriThreadId, getThreadIdWithTimestamp, clearDoriThreadId, markLoadedFromStorage, WidgetType, THREAD_ID_EXPIRY_TIME, } from "@/utils/session-storage"; import { useEffect, useState } from "react"; type PropsType = { chatState: ChatState; messages: Message[]; currentStep: ConversationStep; setChatState: (chatState: ChatState) => void; setMessages: (messages: Message[]) => void; setCurrentStep: (currentStep: ConversationStep) => void; widgetType: WidgetType; }; export function useSessionStorage({ chatState, messages, currentStep, setChatState, setMessages, setCurrentStep, widgetType, }: PropsType) { const [isSessionRestored, setIsSessionRestored] = useState(false); useEffect(() => { const restoreSession = () => { try { if (hasValidSession(widgetType)) { const sessionData = loadSessionData(widgetType); if (sessionData) { const threadData = getThreadIdWithTimestamp(widgetType); let storedThreadId: string | null = null; if (threadData) { const now = Date.now(); if (now - threadData.timestamp < THREAD_ID_EXPIRY_TIME) { storedThreadId = threadData.threadId; } else { addBreadcrumb( "Thread ID expired based on timestamp - clearing", "session.thread_expired", "warning", { thread_id: threadData.threadId, age_hours: (now - threadData.timestamp) / (1000 * 60 * 60), } ); clearDoriThreadId(widgetType); } } else { storedThreadId = getDoriThreadId(widgetType); } const mergedChatState = { ...sessionData.chatState, threadId: storedThreadId || sessionData.chatState.threadId, }; setChatState(mergedChatState); setMessages( sessionData.messages.filter( (message) => message.type !== "loading" ) ); setCurrentStep( sessionData.currentStep === "initial-greeting" ? "initial-greeting" : "ongoing" ); markLoadedFromStorage(widgetType); setIsSessionRestored(true); } } else { addBreadcrumb( "Session fallback triggered - no valid session found", "session.fallback", "info", { widget_type: widgetType, reason: "no_valid_session", } ); const threadData = getThreadIdWithTimestamp(widgetType); let storedThreadId: string | null = null; if (threadData) { const now = Date.now(); if (now - threadData.timestamp < THREAD_ID_EXPIRY_TIME) { storedThreadId = threadData.threadId; } else { addBreadcrumb( "Thread ID expired based on timestamp - clearing", "session.thread_expired_fallback", "warning", { thread_id: threadData.threadId, age_hours: (now - threadData.timestamp) / (1000 * 60 * 60), } ); clearDoriThreadId(widgetType); } } else { storedThreadId = getDoriThreadId(widgetType); } if (storedThreadId) { // For floating widget, try to load messages from localStorage const isFloatingWidget = widgetType === "floating"; const storedMessages = isFloatingWidget ? loadMessages(widgetType) : []; setChatState({ repliedProduct: null, threadId: storedThreadId, isRequestInProgress: false, }); if (storedMessages.length > 0) { setMessages( storedMessages.filter((message) => message.type !== "loading") ); setCurrentStep("ongoing"); // Show chat screen when messages exist } markLoadedFromStorage(widgetType); } else { addBreadcrumb( "Session fallback complete - no thread ID found", "session.fallback_complete", "info", { widget_type: widgetType, reason: "no_thread_id", } ); } setIsSessionRestored(true); } } catch (error) { // If session restore fails, log error and continue with fresh state const err = error instanceof Error ? error : new Error(String(error)); console.error("[Mori Widget] Session restore failed:", err); captureStructuredError(err, { operation: "restoreSession", additionalData: { context: "useSessionStorage", widgetType, }, }); // Continue with fresh state - widget will work, just without history setIsSessionRestored(true); } }; restoreSession(); }, [setChatState, setMessages, setCurrentStep, widgetType]); useEffect(() => { if (isSessionRestored) { updateSessionTimestamp(widgetType); saveChatState(chatState, widgetType); saveMessages(messages, widgetType); saveCurrentStep(currentStep, widgetType); } }, [chatState, messages, currentStep, isSessionRestored, widgetType]); }