Spaces:
Running
Running
/** | |
* 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; |