/** * Conversation Storage Utility * Handles conversation persistence with localStorage */ const STORAGE_KEY = 'ca_study_conversations'; const MAX_CONVERSATIONS = 50; // Limit to prevent localStorage overflow export class ConversationStorage { /** * Load all conversations from localStorage */ static loadConversations() { try { const stored = localStorage.getItem(STORAGE_KEY); if (!stored) return []; const conversations = JSON.parse(stored); // Convert date strings back to Date objects return conversations.map(conv => ({ ...conv, createdAt: new Date(conv.createdAt), messages: conv.messages.map(msg => ({ ...msg, timestamp: new Date(msg.timestamp) })) })); } catch (error) { console.error('Error loading conversations:', error); return []; } } /** * Save conversations to localStorage */ static saveConversations(conversations) { try { // Limit the number of conversations to prevent localStorage overflow const limitedConversations = conversations.slice(0, MAX_CONVERSATIONS); localStorage.setItem(STORAGE_KEY, JSON.stringify(limitedConversations)); return true; } catch (error) { console.error('Error saving conversations:', error); // Handle localStorage quota exceeded if (error.name === 'QuotaExceededError') { // Try to save with fewer conversations const reducedConversations = conversations.slice(0, 25); try { localStorage.setItem(STORAGE_KEY, JSON.stringify(reducedConversations)); return true; } catch (retryError) { console.error('Error saving reduced conversations:', retryError); } } return false; } } /** * Add a new conversation */ static addConversation(conversation) { const conversations = this.loadConversations(); const newConversations = [conversation, ...conversations]; return this.saveConversations(newConversations); } /** * Update an existing conversation */ static updateConversation(conversationId, updates) { const conversations = this.loadConversations(); const updatedConversations = conversations.map(conv => conv.id === conversationId ? { ...conv, ...updates } : conv ); return this.saveConversations(updatedConversations); } /** * Delete a conversation */ static deleteConversation(conversationId) { const conversations = this.loadConversations(); const filteredConversations = conversations.filter(conv => conv.id !== conversationId); return this.saveConversations(filteredConversations); } /** * Add a message to a conversation */ static addMessage(conversationId, message) { const conversations = this.loadConversations(); const updatedConversations = conversations.map(conv => { if (conv.id === conversationId) { return { ...conv, messages: [...conv.messages, message], updatedAt: new Date() }; } return conv; }); return this.saveConversations(updatedConversations); } /** * Search conversations by title or content */ static searchConversations(query) { const conversations = this.loadConversations(); const lowercaseQuery = query.toLowerCase(); return conversations.filter(conv => conv.title.toLowerCase().includes(lowercaseQuery) || conv.messages.some(msg => msg.content.toLowerCase().includes(lowercaseQuery) ) ); } /** * Get conversation statistics */ static getStatistics() { const conversations = this.loadConversations(); const totalMessages = conversations.reduce((sum, conv) => sum + conv.messages.length, 0); return { totalConversations: conversations.length, totalMessages, storageSize: this.getStorageSize(), oldestConversation: conversations.length > 0 ? conversations[conversations.length - 1].createdAt : null, newestConversation: conversations.length > 0 ? conversations[0].createdAt : null }; } /** * Get storage size in KB */ static getStorageSize() { try { const stored = localStorage.getItem(STORAGE_KEY); return stored ? Math.round(new Blob([stored]).size / 1024) : 0; } catch (error) { return 0; } } /** * Export conversations as JSON */ static exportConversations() { const conversations = this.loadConversations(); const exportData = { exportDate: new Date().toISOString(), version: '1.0', conversations }; const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `ca_study_conversations_${new Date().toISOString().split('T')[0]}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } /** * Import conversations from JSON file */ static importConversations(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => { try { const importData = JSON.parse(e.target.result); if (!importData.conversations || !Array.isArray(importData.conversations)) { reject(new Error('Invalid conversation file format')); return; } const existingConversations = this.loadConversations(); const mergedConversations = [...importData.conversations, ...existingConversations]; // Remove duplicates based on ID const uniqueConversations = mergedConversations.filter((conv, index, self) => index === self.findIndex(c => c.id === conv.id) ); const success = this.saveConversations(uniqueConversations); resolve({ success, count: importData.conversations.length }); } catch (error) { reject(error); } }; reader.onerror = () => reject(new Error('Error reading file')); reader.readAsText(file); }); } /** * Clear all conversations */ static clearAllConversations() { try { localStorage.removeItem(STORAGE_KEY); return true; } catch (error) { console.error('Error clearing conversations:', error); return false; } } } export default ConversationStorage;