diff --git a/app/lib/persistence/db.ts b/app/lib/persistence/db.ts index 80d895567..d12d7e5dc 100644 --- a/app/lib/persistence/db.ts +++ b/app/lib/persistence/db.ts @@ -4,14 +4,30 @@ import type { ChatHistoryItem } from './useChatHistory'; const logger = createScopedLogger('ChatHistory'); +let dbInitAttempted = false; + function isBrowserEnvironment(): boolean { - return typeof window !== 'undefined' && typeof window.indexedDB !== 'undefined'; + try { + return typeof window !== 'undefined' && + typeof window.indexedDB !== 'undefined' && + typeof window.IDBDatabase !== 'undefined' && + typeof window.IDBTransaction !== 'undefined'; + } catch (error) { + logger.error('Error checking browser environment:', error); + return false; + } } export async function openDatabase(): Promise { + if (dbInitAttempted) { + logger.debug('Database initialization already attempted'); + return undefined; + } + + dbInitAttempted = true; + return new Promise((resolve) => { try { - // Check if we're in a browser environment with IndexedDB support if (!isBrowserEnvironment()) { logger.debug('Not in browser environment or IndexedDB not available'); resolve(undefined); @@ -36,13 +52,21 @@ export async function openDatabase(): Promise { const db = (event.target as IDBOpenDBRequest).result; logger.debug('Successfully opened database'); - // Add error handler for database - db.onerror = (event: Event) => { - const target = event.target as IDBDatabase; - logger.error('Database error:', target.name); - }; - - resolve(db); + // Test if we can actually use the database + try { + const transaction = db.transaction(['chats'], 'readonly'); + transaction.oncomplete = () => { + logger.debug('Database test successful'); + resolve(db); + }; + transaction.onerror = () => { + logger.error('Database test failed'); + resolve(undefined); + }; + } catch (error) { + logger.error('Error testing database:', error); + resolve(undefined); + } }; request.onerror = (event: Event) => { diff --git a/app/lib/persistence/useChatHistory.ts b/app/lib/persistence/useChatHistory.ts index bf02a39b1..18bda021d 100644 --- a/app/lib/persistence/useChatHistory.ts +++ b/app/lib/persistence/useChatHistory.ts @@ -2,9 +2,11 @@ import { useLoaderData, useNavigate } from '@remix-run/react'; import { useState, useEffect } from 'react'; import { atom } from 'nanostores'; import type { Message } from 'ai'; -import { toast } from 'react-toastify'; import { workbenchStore } from '~/lib/stores/workbench'; import { getMessages, getNextId, getUrlId, openDatabase, setMessages } from './db'; +import { createScopedLogger } from '~/utils/logger'; + +const logger = createScopedLogger('ChatHistory'); export interface ChatHistoryItem { id: string; @@ -14,8 +16,9 @@ export interface ChatHistoryItem { timestamp: string; } -// Remove environment check and persistence flag +// Initialize database lazily when needed let db: IDBDatabase | undefined; +let dbInitialized = false; export const chatId = atom(undefined); export const description = atom(undefined); @@ -27,41 +30,53 @@ export function useChatHistory() { const [initialMessages, setInitialMessages] = useState([]); const [ready, setReady] = useState(false); const [urlId, setUrlId] = useState(); - const [dbInitialized, setDbInitialized] = useState(false); // Initialize database when component mounts useEffect(() => { const initDb = async () => { - if (!db && !dbInitialized) { - db = await openDatabase(); - setDbInitialized(true); - } + try { + // Only attempt to initialize once + if (!dbInitialized) { + logger.debug('Initializing database'); + db = await openDatabase(); + dbInitialized = true; + } - if (!db) { - setReady(true); - return; - } + // If we have a mixedId but no database, navigate home + if (mixedId && !db) { + logger.debug('No database available, navigating home'); + navigate('/', { replace: true }); + setReady(true); + return; + } - if (mixedId) { - try { - const storedMessages = await getMessages(db, mixedId); - if (storedMessages && storedMessages.messages.length > 0) { - setInitialMessages(storedMessages.messages); - setUrlId(storedMessages.urlId); - description.set(storedMessages.description); - chatId.set(storedMessages.id); - } else { + // If we have both mixedId and database, try to load messages + if (mixedId && db) { + try { + const storedMessages = await getMessages(db, mixedId); + if (storedMessages && storedMessages.messages.length > 0) { + setInitialMessages(storedMessages.messages); + setUrlId(storedMessages.urlId); + description.set(storedMessages.description); + chatId.set(storedMessages.id); + } else { + navigate('/', { replace: true }); + } + } catch (error) { + logger.error('Failed to load messages:', error); navigate('/', { replace: true }); } - } catch (error) { - console.error('Failed to load messages:', error); } + + setReady(true); + } catch (error) { + logger.error('Failed to initialize:', error); + setReady(true); } - setReady(true); }; initDb(); - }, [mixedId, navigate, dbInitialized]); + }, [mixedId, navigate]); return { ready: !mixedId || ready, @@ -71,31 +86,31 @@ export function useChatHistory() { return; } - const { firstArtifact } = workbenchStore; + try { + const { firstArtifact } = workbenchStore; - if (!urlId && firstArtifact?.id) { - const newUrlId = await getUrlId(db, firstArtifact.id); - navigateChat(newUrlId); - setUrlId(newUrlId); - } + if (!urlId && firstArtifact?.id) { + const newUrlId = await getUrlId(db, firstArtifact.id); + navigateChat(newUrlId); + setUrlId(newUrlId); + } - if (!description.get() && firstArtifact?.title) { - description.set(firstArtifact?.title); - } + if (!description.get() && firstArtifact?.title) { + description.set(firstArtifact?.title); + } - if (initialMessages.length === 0 && !chatId.get()) { - const nextId = await getNextId(db); - chatId.set(nextId); + if (initialMessages.length === 0 && !chatId.get()) { + const nextId = await getNextId(db); + chatId.set(nextId); - if (!urlId) { - navigateChat(nextId); + if (!urlId) { + navigateChat(nextId); + } } - } - try { await setMessages(db, chatId.get() as string, messages, urlId, description.get()); } catch (error) { - console.error('Failed to store messages:', error); + logger.error('Failed to store messages:', error); } }, };