From c1ee215b8790a17fd91004f413f4040b4dcd6718 Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Fri, 15 Sep 2023 07:44:25 -0700 Subject: [PATCH] Merged #158 --- .../components/applayout/ChatDrawerItems.tsx | 3 +- .../components/applayout/ChatMenuItems.tsx | 1 - src/common/state/store-chats.ts | 61 +++++++++---------- src/common/util/idbUtils.ts | 14 +++++ 4 files changed, 45 insertions(+), 34 deletions(-) create mode 100644 src/common/util/idbUtils.ts diff --git a/src/apps/chat/components/applayout/ChatDrawerItems.tsx b/src/apps/chat/components/applayout/ChatDrawerItems.tsx index 5ec7939d2..77dfa8305 100644 --- a/src/apps/chat/components/applayout/ChatDrawerItems.tsx +++ b/src/apps/chat/components/applayout/ChatDrawerItems.tsx @@ -44,6 +44,7 @@ export function ChatDrawerItems(props: { const hasChats = conversationIDs.length > 0; const singleChat = conversationIDs.length === 1; + const softMaxReached = conversationIDs.length >= 50; const closeDrawerMenu = () => setLayoutDrawerAnchor(null); @@ -124,7 +125,7 @@ export function ChatDrawerItems(props: { isActive={conversationId === props.conversationId} isSingle={singleChat} showSymbols={showSymbols} - maxChatMessages={(experimentalLabs || maxReached) ? maxChatMessages : 0} + maxChatMessages={(experimentalLabs || softMaxReached) ? maxChatMessages : 0} conversationActivate={handleConversationActivate} conversationDelete={handleConversationDelete} />)} diff --git a/src/apps/chat/components/applayout/ChatMenuItems.tsx b/src/apps/chat/components/applayout/ChatMenuItems.tsx index 0a67a8e75..fa4d036cd 100644 --- a/src/apps/chat/components/applayout/ChatMenuItems.tsx +++ b/src/apps/chat/components/applayout/ChatMenuItems.tsx @@ -10,7 +10,6 @@ import FileDownloadIcon from '@mui/icons-material/FileDownload'; import ForkRightIcon from '@mui/icons-material/ForkRight'; import SettingsSuggestIcon from '@mui/icons-material/SettingsSuggest'; -import { useChatStore } from '~/common/state/store-chats'; import { setLayoutMenuAnchor } from '~/common/layout/store-applayout'; import { useUIPreferencesStore } from '~/common/state/store-ui'; diff --git a/src/common/state/store-chats.ts b/src/common/state/store-chats.ts index 0cd01cf5a..03657415f 100644 --- a/src/common/state/store-chats.ts +++ b/src/common/state/store-chats.ts @@ -1,6 +1,5 @@ import { create } from 'zustand'; -import { devtools, persist, createJSONStorage, StateStorage } from 'zustand/middleware'; -import { get, set, del } from 'idb-keyval' +import { createJSONStorage, devtools, persist } from 'zustand/middleware'; import { v4 as uuidv4 } from 'uuid'; import { DLLMId } from '~/modules/llms/llm.types'; @@ -8,6 +7,7 @@ import { useModelsStore } from '~/modules/llms/store-llms'; import { countModelTokens } from '../util/token-counter'; import { defaultSystemPurposeId, SystemPurposeId } from '../../data'; +import { idbStateStorage } from '../util/idbUtils'; /** @@ -104,38 +104,9 @@ export function createDEphemeral(title: string, initialText: string): DEphemeral }; } -const storage: StateStorage = { - getItem: async (name: string): Promise => { - return (await get(name)) || null - }, - setItem: async (name: string, value: string): Promise => { - await set(name, value) - }, - removeItem: async (name: string): Promise => { - await del(name) - }, -} - -function _migrateLocalStorageToIndexedDB(state: ChatStore) { - const key = "app-chats" - const value = localStorage.getItem(key); - - // Check if migration has already been done - if (!value) return state; - - // Mark migration as done - localStorage.removeItem(key); - - // Migrate data to IndexedDB - const localStorageState = JSON.parse(value)?.state; - - state.conversations = localStorageState?.conversations; - state.activeConversationId = localStorageState?.activeConversationId; -} /// Conversations Store - export interface ChatStore { conversations: DConversation[]; activeConversationId: string | null; @@ -434,7 +405,7 @@ export const useChatStore = create()(devtools( // - 2: [2023-04-10] multi-chat version - invalidating data to be sure // - 3: [2023-08-30] switch to IndexedDB version: 3, - storage: createJSONStorage(() => storage), + storage: createJSONStorage(() => idbStateStorage), // omit the transient property from the persisted state partialize: (state) => ({ @@ -450,7 +421,9 @@ export const useChatStore = create()(devtools( onRehydrateStorage: () => (state) => { if (state) { + // one-time: move localStorage data (version: 2) to the 'state' _migrateLocalStorageToIndexedDB(state); + // if nothing is selected, select the first conversation if (!state.activeConversationId && state.conversations.length) state.activeConversationId = state.conversations[0].id; @@ -473,6 +446,30 @@ export const useChatStore = create()(devtools( }), ); +/** + * Migrate data from localStorage (version=2) to IndexedDB (version=3+) + * This is a one-time migration, and should be removed in the future + */ +function _migrateLocalStorageToIndexedDB(state: ChatStore) { + const key = 'app-chats'; + const value = localStorage.getItem(key); + + // Check if migration has already been done + if (!value) return; + + // Migrate data to IndexedDB + try { + const localStorageState = JSON.parse(value)?.state; + + // Mark migration as done + localStorage.removeItem(key); + + state.conversations = localStorageState?.conversations ?? []; + state.activeConversationId = localStorageState?.activeConversationId ?? null; + } catch (e) { + console.error('Failed to migrate localStorage to IndexedDB', e); + } +} /** * Convenience function to count the tokens in a DMessage object diff --git a/src/common/util/idbUtils.ts b/src/common/util/idbUtils.ts new file mode 100644 index 000000000..061d56da0 --- /dev/null +++ b/src/common/util/idbUtils.ts @@ -0,0 +1,14 @@ +import type { StateStorage } from 'zustand/middleware'; +import { del, get, set } from 'idb-keyval'; + +export const idbStateStorage: StateStorage = { + getItem: async (name: string): Promise => { + return (await get(name)) || null; + }, + setItem: async (name: string, value: string): Promise => { + await set(name, value); + }, + removeItem: async (name: string): Promise => { + await del(name); + }, +}; \ No newline at end of file