From 053495254b781a19a3bd5aaa581657191acd4d93 Mon Sep 17 00:00:00 2001 From: heimoshuiyu Date: Wed, 8 Nov 2023 13:54:51 +0800 Subject: [PATCH] v2.0.0 migrate to indexedDB --- package.json | 1 + src/CHATGPT_API_WEB_VERSION.ts | 2 +- src/app.tsx | 218 ++++++++++++++++----------------- src/chatbox.tsx | 8 +- yarn.lock | 5 + 5 files changed, 119 insertions(+), 115 deletions(-) diff --git a/package.json b/package.json index 0ff2d66..838cda5 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@types/ungap__structured-clone": "^0.3.1", "@ungap/structured-clone": "^1.2.0", "autoprefixer": "^10.4.16", + "idb": "^7.1.1", "postcss": "^8.4.31", "preact": "^10.18.1", "preact-markdown": "^2.1.0", diff --git a/src/CHATGPT_API_WEB_VERSION.ts b/src/CHATGPT_API_WEB_VERSION.ts index 79ca382..a582d01 100644 --- a/src/CHATGPT_API_WEB_VERSION.ts +++ b/src/CHATGPT_API_WEB_VERSION.ts @@ -1,3 +1,3 @@ -const CHATGPT_API_WEB_VERSION = "v1.6.0"; +const CHATGPT_API_WEB_VERSION = "v2.0.0"; export default CHATGPT_API_WEB_VERSION; diff --git a/src/app.tsx b/src/app.tsx index c1870f1..0be193c 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,3 +1,4 @@ +import { IDBPDatabase, openDB } from "idb"; import { useEffect, useState } from "preact/hooks"; import "./global.css"; @@ -51,7 +52,7 @@ export interface ChatStore { } const _defaultAPIEndpoint = "https://api.openai.com/v1/chat/completions"; -const newChatStore = ( +export const newChatStore = ( apiKey = "", systemMessageContent = "", apiEndpoint = _defaultAPIEndpoint, @@ -97,7 +98,7 @@ const newChatStore = ( }; }; -const STORAGE_NAME = "chatgpt-api-web"; +export const STORAGE_NAME = "chatgpt-api-web"; const STORAGE_NAME_SELECTED = `${STORAGE_NAME}-selected`; const STORAGE_NAME_INDEXES = `${STORAGE_NAME}-indexes`; const STORAGE_NAME_TOTALCOST = `${STORAGE_NAME}-totalcost`; @@ -122,36 +123,45 @@ export function clearTotalCost() { } export function App() { - // init indexes - const initAllChatStoreIndexes: number[] = JSON.parse( - localStorage.getItem(STORAGE_NAME_INDEXES) ?? "[0]" - ); - const [allChatStoreIndexes, setAllChatStoreIndexes] = useState( - initAllChatStoreIndexes - ); - useEffect(() => { - if (allChatStoreIndexes.length === 0) allChatStoreIndexes.push(0); - console.log("saved all chat store indexes", allChatStoreIndexes); - localStorage.setItem( - STORAGE_NAME_INDEXES, - JSON.stringify(allChatStoreIndexes) - ); - }, [allChatStoreIndexes]); - // init selected index const [selectedChatIndex, setSelectedChatIndex] = useState( - parseInt(localStorage.getItem(STORAGE_NAME_SELECTED) ?? "0") + parseInt(localStorage.getItem(STORAGE_NAME_SELECTED) ?? "1") ); + console.log("selectedChatIndex", selectedChatIndex); useEffect(() => { console.log("set selected chat index", selectedChatIndex); localStorage.setItem(STORAGE_NAME_SELECTED, `${selectedChatIndex}`); }, [selectedChatIndex]); - const getChatStoreByIndex = (index: number): ChatStore => { - const key = `${STORAGE_NAME}-${index}`; - const val = localStorage.getItem(key); - if (val === null) return newChatStore(); - const ret = JSON.parse(val) as ChatStore; + const db = openDB(STORAGE_NAME, 1, { + upgrade(db) { + const store = db.createObjectStore(STORAGE_NAME, { + autoIncrement: true, + }); + + // copy from localStorage to indexedDB + const allChatStoreIndexes: number[] = JSON.parse( + localStorage.getItem(STORAGE_NAME_INDEXES) ?? "[]" + ); + let keyCount = 0; + for (const i of allChatStoreIndexes) { + console.log("importing chatStore from localStorage", i); + const key = `${STORAGE_NAME}-${i}`; + const val = localStorage.getItem(key); + if (val === null) continue; + store.add(JSON.parse(val)); + keyCount += 1; + } + setSelectedChatIndex(keyCount); + alert( + "v2.0.0 Update: Imported chat history from localStorage to indexedDB. 🎉" + ); + }, + }); + + const getChatStoreByIndex = async (index: number): Promise => { + const ret: ChatStore = await (await db).get(STORAGE_NAME, index); + if (ret === null || ret === undefined) return newChatStore(); // handle read from old version chatstore if (ret.model === undefined) ret.model = "gpt-3.5-turbo"; if (ret.responseModelName === undefined) ret.responseModelName = ""; @@ -168,16 +178,8 @@ export function App() { return ret; }; - const [chatStore, _setChatStore] = useState( - getChatStoreByIndex(selectedChatIndex) - ); - const setChatStore = (chatStore: ChatStore) => { - console.log("saved chat", selectedChatIndex, chatStore); - localStorage.setItem( - `${STORAGE_NAME}-${selectedChatIndex}`, - JSON.stringify(chatStore) - ); - + const [chatStore, _setChatStore] = useState(newChatStore()); + const setChatStore = async (chatStore: ChatStore) => { console.log("recalculate postBeginIndex"); const max = chatStore.maxTokens - chatStore.tokenMargin; let sum = 0; @@ -205,59 +207,75 @@ export function App() { chatStore.totalTokens += msg.token; } + console.log("saved chat", selectedChatIndex, chatStore); + (await db).put(STORAGE_NAME, chatStore, selectedChatIndex); + _setChatStore(chatStore); }; useEffect(() => { - _setChatStore(getChatStoreByIndex(selectedChatIndex)); + const run = async () => { + _setChatStore(await getChatStoreByIndex(selectedChatIndex)); + }; + run(); }, [selectedChatIndex]); - const handleNewChatStore = () => { - const max = Math.max(...allChatStoreIndexes); - const next = max + 1; - console.log("save next chat", next); - localStorage.setItem( - `${STORAGE_NAME}-${next}`, - JSON.stringify( - newChatStore( - chatStore.apiKey, - chatStore.systemMessageContent, - chatStore.apiEndpoint, - chatStore.streamMode, - chatStore.model, - chatStore.temperature, - !!chatStore.develop_mode, - chatStore.whisper_api, - chatStore.whisper_key, - chatStore.tts_api, - chatStore.tts_key, - chatStore.tts_speed, - chatStore.tts_speed_enabled - ) + // all chat store indexes + const [allChatStoreIndexes, setAllChatStoreIndexes] = useState( + [] + ); + + const handleNewChatStore = async () => { + const newKey = await ( + await db + ).add( + STORAGE_NAME, + newChatStore( + chatStore.apiKey, + chatStore.systemMessageContent, + chatStore.apiEndpoint, + chatStore.streamMode, + chatStore.model, + chatStore.temperature, + !!chatStore.develop_mode, + chatStore.whisper_api, + chatStore.whisper_key, + chatStore.tts_api, + chatStore.tts_key, + chatStore.tts_speed, + chatStore.tts_speed_enabled ) ); - allChatStoreIndexes.push(next); - setAllChatStoreIndexes([...allChatStoreIndexes]); - setSelectedChatIndex(next); + setSelectedChatIndex(newKey as number); + setAllChatStoreIndexes(await (await db).getAllKeys(STORAGE_NAME)); }; // if there are any params in URL, create a new chatStore useEffect(() => { - const api = getDefaultParams("api", ""); - const key = getDefaultParams("key", ""); - const sys = getDefaultParams("sys", ""); - const mode = getDefaultParams("mode", ""); - const model = getDefaultParams("model", ""); - // only create new chatStore if the params in URL are NOT - // equal to the current selected chatStore - if ( - (api && api !== chatStore.apiEndpoint) || - (key && key !== chatStore.apiKey) || - (sys && sys !== chatStore.systemMessageContent) || - (mode && mode !== (chatStore.streamMode ? "stream" : "fetch")) || - (model && model !== chatStore.model) - ) { - handleNewChatStore(); - } + const run = async () => { + const api = getDefaultParams("api", ""); + const key = getDefaultParams("key", ""); + const sys = getDefaultParams("sys", ""); + const mode = getDefaultParams("mode", ""); + const model = getDefaultParams("model", ""); + // only create new chatStore if the params in URL are NOT + // equal to the current selected chatStore + if ( + (api && api !== chatStore.apiEndpoint) || + (key && key !== chatStore.apiKey) || + (sys && sys !== chatStore.systemMessageContent) || + (mode && mode !== (chatStore.streamMode ? "stream" : "fetch")) || + (model && model !== chatStore.model) + ) { + handleNewChatStore(); + } + await db; + const allidx = await (await db).getAllKeys(STORAGE_NAME); + if (allidx.length === 0) { + handleNewChatStore(); + } + setAllChatStoreIndexes(await (await db).getAllKeys(STORAGE_NAME)); + }; + run(); }, []); return ( @@ -271,7 +289,7 @@ export function App() { {Tr("NEW")}
    - {allChatStoreIndexes + {(allChatStoreIndexes as number[]) .slice() .reverse() .map((i) => { @@ -295,43 +313,26 @@ export function App() {