refactor: migrate snapshot storage from localStorage to IndexedDB
To improve data consistency and reliability, snapshot storage has been migrated from localStorage to IndexedDB. This change includes adding a new 'snapshots' object store, updating database version to 2, and modifying related functions to use IndexedDB for snapshot operations. The migration ensures better handling of snapshots alongside chat data and removes dependency on localStorage preventing UI lag.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { Message } from 'ai';
|
||||
import { createScopedLogger } from '~/utils/logger';
|
||||
import type { ChatHistoryItem } from './useChatHistory';
|
||||
import type { Snapshot } from './types'; // Import Snapshot type
|
||||
|
||||
export interface IChatMetadata {
|
||||
gitUrl: string;
|
||||
@@ -18,15 +19,24 @@ export async function openDatabase(): Promise<IDBDatabase | undefined> {
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const request = indexedDB.open('boltHistory', 1);
|
||||
const request = indexedDB.open('boltHistory', 2);
|
||||
|
||||
request.onupgradeneeded = (event: IDBVersionChangeEvent) => {
|
||||
const db = (event.target as IDBOpenDBRequest).result;
|
||||
const oldVersion = event.oldVersion;
|
||||
|
||||
if (!db.objectStoreNames.contains('chats')) {
|
||||
const store = db.createObjectStore('chats', { keyPath: 'id' });
|
||||
store.createIndex('id', 'id', { unique: true });
|
||||
store.createIndex('urlId', 'urlId', { unique: true });
|
||||
if (oldVersion < 1) {
|
||||
if (!db.objectStoreNames.contains('chats')) {
|
||||
const store = db.createObjectStore('chats', { keyPath: 'id' });
|
||||
store.createIndex('id', 'id', { unique: true });
|
||||
store.createIndex('urlId', 'urlId', { unique: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (oldVersion < 2) {
|
||||
if (!db.objectStoreNames.contains('snapshots')) {
|
||||
db.createObjectStore('snapshots', { keyPath: 'chatId' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -113,12 +123,46 @@ export async function getMessagesById(db: IDBDatabase, id: string): Promise<Chat
|
||||
|
||||
export async function deleteById(db: IDBDatabase, id: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction('chats', 'readwrite');
|
||||
const store = transaction.objectStore('chats');
|
||||
const request = store.delete(id);
|
||||
const transaction = db.transaction(['chats', 'snapshots'], 'readwrite'); // Add snapshots store to transaction
|
||||
const chatStore = transaction.objectStore('chats');
|
||||
const snapshotStore = transaction.objectStore('snapshots');
|
||||
|
||||
request.onsuccess = () => resolve(undefined);
|
||||
request.onerror = () => reject(request.error);
|
||||
const deleteChatRequest = chatStore.delete(id);
|
||||
const deleteSnapshotRequest = snapshotStore.delete(id); // Also delete snapshot
|
||||
|
||||
let chatDeleted = false;
|
||||
let snapshotDeleted = false;
|
||||
|
||||
const checkCompletion = () => {
|
||||
if (chatDeleted && snapshotDeleted) {
|
||||
resolve(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
deleteChatRequest.onsuccess = () => {
|
||||
chatDeleted = true;
|
||||
checkCompletion();
|
||||
};
|
||||
deleteChatRequest.onerror = () => reject(deleteChatRequest.error);
|
||||
|
||||
deleteSnapshotRequest.onsuccess = () => {
|
||||
snapshotDeleted = true;
|
||||
checkCompletion();
|
||||
};
|
||||
|
||||
deleteSnapshotRequest.onerror = (event) => {
|
||||
if ((event.target as IDBRequest).error?.name === 'NotFoundError') {
|
||||
snapshotDeleted = true;
|
||||
checkCompletion();
|
||||
} else {
|
||||
reject(deleteSnapshotRequest.error);
|
||||
}
|
||||
};
|
||||
|
||||
transaction.oncomplete = () => {
|
||||
// This might resolve before checkCompletion if one operation finishes much faster
|
||||
};
|
||||
transaction.onerror = () => reject(transaction.error);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -257,3 +301,43 @@ export async function updateChatMetadata(
|
||||
|
||||
await setMessages(db, id, chat.messages, chat.urlId, chat.description, chat.timestamp, metadata);
|
||||
}
|
||||
|
||||
export async function getSnapshot(db: IDBDatabase, chatId: string): Promise<Snapshot | undefined> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction('snapshots', 'readonly');
|
||||
const store = transaction.objectStore('snapshots');
|
||||
const request = store.get(chatId);
|
||||
|
||||
request.onsuccess = () => resolve(request.result?.snapshot as Snapshot | undefined);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function setSnapshot(db: IDBDatabase, chatId: string, snapshot: Snapshot): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction('snapshots', 'readwrite');
|
||||
const store = transaction.objectStore('snapshots');
|
||||
const request = store.put({ chatId, snapshot });
|
||||
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteSnapshot(db: IDBDatabase, chatId: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction('snapshots', 'readwrite');
|
||||
const store = transaction.objectStore('snapshots');
|
||||
const request = store.delete(chatId);
|
||||
|
||||
request.onsuccess = () => resolve();
|
||||
|
||||
request.onerror = (event) => {
|
||||
if ((event.target as IDBRequest).error?.name === 'NotFoundError') {
|
||||
resolve();
|
||||
} else {
|
||||
reject(request.error);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user