From 6acb94b5af989f0c0ef42db3a1ee084b70223f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 20 Feb 2025 20:05:57 +0100 Subject: [PATCH] Wrap database in async suspense value (#36) --- app/components/chat/Messages.client.tsx | 45 ++++++++++++------------ app/components/settings/data/DataTab.tsx | 3 +- app/components/sidebar/Menu.client.tsx | 3 +- app/lib/asyncSuspenseValue.ts | 15 ++++++++ app/lib/hooks/useEditChatDescription.ts | 3 +- app/lib/persistence/useChatHistory.ts | 10 ++++-- 6 files changed, 51 insertions(+), 28 deletions(-) diff --git a/app/components/chat/Messages.client.tsx b/app/components/chat/Messages.client.tsx index 3253f28d..eec429c0 100644 --- a/app/components/chat/Messages.client.tsx +++ b/app/components/chat/Messages.client.tsx @@ -4,7 +4,6 @@ import { classNames } from '~/utils/classNames'; import { AssistantMessage, getAnnotationsTokensUsage } from './AssistantMessage'; import { UserMessage } from './UserMessage'; import { useLocation } from '@remix-run/react'; -import { db, chatId } from '~/lib/persistence/useChatHistory'; import { forkChat } from '~/lib/persistence/db'; import { toast } from 'react-toastify'; import WithTooltip from '~/components/ui/Tooltip'; @@ -29,7 +28,7 @@ export function saveProjectContents(messageId: string, contents: ProjectContents } function hasFileModifications(content: string) { - return content.includes("__boltArtifact__"); + return content.includes('__boltArtifact__'); } export const Messages = React.forwardRef((props: MessagesProps, ref) => { @@ -95,27 +94,27 @@ export const Messages = React.forwardRef((props: )} {!isUserMessage && - messageId && - onRewind && - getLastMessageProjectContents(index) && - hasFileModifications(content) && ( -
- -
- )} + messageId && + onRewind && + getLastMessageProjectContents(index) && + hasFileModifications(content) && ( +
+ +
+ )} ); diff --git a/app/components/settings/data/DataTab.tsx b/app/components/settings/data/DataTab.tsx index 9219d015..acdcac23 100644 --- a/app/components/settings/data/DataTab.tsx +++ b/app/components/settings/data/DataTab.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { useNavigate } from '@remix-run/react'; import Cookies from 'js-cookie'; import { toast } from 'react-toastify'; -import { db, deleteById, getAll, setMessages } from '~/lib/persistence'; +import { database, deleteById, getAll, setMessages } from '~/lib/persistence'; import { logStore } from '~/lib/stores/logs'; import { classNames } from '~/utils/classNames'; import type { Message } from 'ai'; @@ -31,6 +31,7 @@ interface ApiKeys { } export default function DataTab() { + const db = database?.read(); const navigate = useNavigate(); const [isDeleting, setIsDeleting] = useState(false); diff --git a/app/components/sidebar/Menu.client.tsx b/app/components/sidebar/Menu.client.tsx index 8eec07c9..5549e236 100644 --- a/app/components/sidebar/Menu.client.tsx +++ b/app/components/sidebar/Menu.client.tsx @@ -5,7 +5,7 @@ import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from import { ThemeSwitch } from '~/components/ui/ThemeSwitch'; import { SettingsWindow } from '~/components/settings/SettingsWindow'; import { SettingsButton } from '~/components/ui/SettingsButton'; -import { db, deleteById, getAll, chatId, type ChatHistoryItem, useChatHistory } from '~/lib/persistence'; +import { database, deleteById, getAll, chatId, type ChatHistoryItem, useChatHistory } from '~/lib/persistence'; import { cubicEasingFn } from '~/utils/easings'; import { logger } from '~/utils/logger'; import { HistoryItem } from './HistoryItem'; @@ -39,6 +39,7 @@ const menuVariants = { type DialogContent = { type: 'delete'; item: ChatHistoryItem } | null; export const Menu = () => { + const db = database?.read(); const { duplicateCurrentChat, exportChat } = useChatHistory(); const menuRef = useRef(null); const [list, setList] = useState([]); diff --git a/app/lib/asyncSuspenseValue.ts b/app/lib/asyncSuspenseValue.ts index 22185fd3..dd88bafe 100644 --- a/app/lib/asyncSuspenseValue.ts +++ b/app/lib/asyncSuspenseValue.ts @@ -32,6 +32,21 @@ export function createAsyncSuspenseValue(getValue: () => Promise) { }; const asyncValue = { + load: async () => { + if (!record) { + return load(); + } + + switch (record.status) { + case 'pending': + return record.promise; + case 'resolved': + return record.value; + case 'rejected': + throw record.error; + } + }, + read() { if (!record) { throw load(); diff --git a/app/lib/hooks/useEditChatDescription.ts b/app/lib/hooks/useEditChatDescription.ts index bb1e8dcc..bc0ecd7b 100644 --- a/app/lib/hooks/useEditChatDescription.ts +++ b/app/lib/hooks/useEditChatDescription.ts @@ -3,7 +3,7 @@ import { useCallback, useEffect, useState } from 'react'; import { toast } from 'react-toastify'; import { chatId as chatIdStore, - db, + database, description as descriptionStore, getMessages, updateChatDescription, @@ -44,6 +44,7 @@ export function useEditChatDescription({ customChatId, syncWithGlobalStore, }: EditChatDescriptionOptions): EditChatDescriptionHook { + const db = database?.read(); const chatIdFromStore = useStore(chatIdStore); const [editing, setEditing] = useState(false); const [currentDescription, setCurrentDescription] = useState(initialDescription); diff --git a/app/lib/persistence/useChatHistory.ts b/app/lib/persistence/useChatHistory.ts index 2b296e6c..26d02e31 100644 --- a/app/lib/persistence/useChatHistory.ts +++ b/app/lib/persistence/useChatHistory.ts @@ -15,6 +15,7 @@ import { createChatFromMessages, } from './db'; import { loadProblem } from '~/components/chat/LoadProblemButton'; +import { createAsyncSuspenseValue } from '../asyncSuspenseValue'; export interface ChatHistoryItem { id: string; @@ -26,14 +27,19 @@ export interface ChatHistoryItem { const persistenceEnabled = !import.meta.env.VITE_DISABLE_PERSISTENCE; -export const db = persistenceEnabled ? await openDatabase() : undefined; +export const database = persistenceEnabled ? createAsyncSuspenseValue(openDatabase) : undefined; + +if (typeof document !== 'undefined') { + database?.preload(); +} export const chatId = atom(undefined); export const description = atom(undefined); export function useChatHistory() { + const db = database?.read(); const navigate = useNavigate(); - const { id: mixedId, problemId } = useLoaderData<{ id?: string, problemId?: string }>() ?? {}; + const { id: mixedId, problemId } = useLoaderData<{ id?: string; problemId?: string }>() ?? {}; const [searchParams] = useSearchParams(); const [initialMessages, setInitialMessages] = useState([]);