feat: navigate away when deleting current chat (#44)

This commit is contained in:
Kirjava 2024-08-21 21:18:38 +01:00 committed by GitHub
parent 1e11ab6395
commit a7b1f5046d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 41 additions and 29 deletions

View File

@ -5,6 +5,7 @@
"scripts": { "scripts": {
"playground:dev": "pnpm run --filter=playground dev", "playground:dev": "pnpm run --filter=playground dev",
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .", "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
"lint:fix": "npm run lint -- --fix",
"build": "pnpm run -r build", "build": "pnpm run -r build",
"test": "pnpm run -r test", "test": "pnpm run -r test",
"typecheck": "pnpm run -r typecheck" "typecheck": "pnpm run -r typecheck"

View File

@ -3,6 +3,7 @@ import { ClientOnly } from 'remix-utils/client-only';
import { chatStore } from '~/lib/stores/chat'; import { chatStore } from '~/lib/stores/chat';
import { classNames } from '~/utils/classNames'; import { classNames } from '~/utils/classNames';
import { HeaderActionButtons } from './HeaderActionButtons.client'; import { HeaderActionButtons } from './HeaderActionButtons.client';
import { ChatDescription } from '~/lib/persistence/ChatDescription.client';
export function Header() { export function Header() {
const chat = useStore(chatStore); const chat = useStore(chatStore);
@ -23,7 +24,9 @@ export function Header() {
<span className="i-bolt:logo-text?mask w-[46px] inline-block" /> <span className="i-bolt:logo-text?mask w-[46px] inline-block" />
</a> </a>
</div> </div>
<div className="flex-1" /> <span className="flex-1 px-4 truncate text-center text-bolt-elements-textPrimary">
<ClientOnly>{() => <ChatDescription />}</ClientOnly>
</span>
{chat.started && ( {chat.started && (
<ClientOnly> <ClientOnly>
{() => ( {() => (

View File

@ -4,7 +4,7 @@ import { toast } from 'react-toastify';
import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog'; import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
import { IconButton } from '~/components/ui/IconButton'; import { IconButton } from '~/components/ui/IconButton';
import { ThemeSwitch } from '~/components/ui/ThemeSwitch'; import { ThemeSwitch } from '~/components/ui/ThemeSwitch';
import { db, deleteId, getAll, type ChatHistoryItem } from '~/lib/persistence'; import { db, deleteById, getAll, chatId, type ChatHistoryItem } from '~/lib/persistence';
import { cubicEasingFn } from '~/utils/easings'; import { cubicEasingFn } from '~/utils/easings';
import { logger } from '~/utils/logger'; import { logger } from '~/utils/logger';
import { HistoryItem } from './HistoryItem'; import { HistoryItem } from './HistoryItem';
@ -52,8 +52,15 @@ export function Menu() {
event.preventDefault(); event.preventDefault();
if (db) { if (db) {
deleteId(db, item.id) deleteById(db, item.id)
.then(() => loadEntries()) .then(() => {
loadEntries();
if (chatId.get() === item.id) {
// hard page navigation to clear the stores
window.location.pathname = '/';
}
})
.catch((error) => { .catch((error) => {
toast.error('Failed to delete conversation'); toast.error('Failed to delete conversation');
logger.error(error); logger.error(error);

View File

@ -0,0 +1,6 @@
import { useStore } from '@nanostores/react';
import { description } from './useChatHistory';
export function ChatDescription() {
return useStore(description);
}

View File

@ -92,7 +92,7 @@ export async function getMessagesById(db: IDBDatabase, id: string): Promise<Chat
}); });
} }
export async function deleteId(db: IDBDatabase, id: string): Promise<void> { export async function deleteById(db: IDBDatabase, id: string): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const transaction = db.transaction('chats', 'readwrite'); const transaction = db.transaction('chats', 'readwrite');
const store = transaction.objectStore('chats'); const store = transaction.objectStore('chats');

View File

@ -1,6 +1,7 @@
import { useLoaderData, useNavigate } from '@remix-run/react'; import { useLoaderData, useNavigate } from '@remix-run/react';
import { useState, useEffect } from 'react';
import { atom } from 'nanostores';
import type { Message } from 'ai'; import type { Message } from 'ai';
import { useEffect, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { AnalyticsAction, sendAnalyticsEvent } from '~/lib/analytics'; import { AnalyticsAction, sendAnalyticsEvent } from '~/lib/analytics';
import { workbenchStore } from '~/lib/stores/workbench'; import { workbenchStore } from '~/lib/stores/workbench';
@ -18,16 +19,16 @@ const persistenceEnabled = !import.meta.env.VITE_DISABLE_PERSISTENCE;
export const db = persistenceEnabled ? await openDatabase() : undefined; export const db = persistenceEnabled ? await openDatabase() : undefined;
export const chatId = atom<string | undefined>(undefined);
export const description = atom<string | undefined>(undefined);
export function useChatHistory() { export function useChatHistory() {
const navigate = useNavigate(); const navigate = useNavigate();
const { id: mixedId } = useLoaderData<{ id?: string }>(); const { id: mixedId } = useLoaderData<{ id?: string }>();
const [chatId, setChatId] = useState(mixedId);
const [initialMessages, setInitialMessages] = useState<Message[]>([]); const [initialMessages, setInitialMessages] = useState<Message[]>([]);
const [ready, setReady] = useState<boolean>(false); const [ready, setReady] = useState<boolean>(false);
const [entryId, setEntryId] = useState<string | undefined>();
const [urlId, setUrlId] = useState<string | undefined>(); const [urlId, setUrlId] = useState<string | undefined>();
const [description, setDescription] = useState<string | undefined>();
useEffect(() => { useEffect(() => {
if (!db) { if (!db) {
@ -40,14 +41,14 @@ export function useChatHistory() {
return; return;
} }
if (chatId) { if (mixedId) {
getMessages(db, chatId) getMessages(db, mixedId)
.then((storedMessages) => { .then((storedMessages) => {
if (storedMessages && storedMessages.messages.length > 0) { if (storedMessages && storedMessages.messages.length > 0) {
setInitialMessages(storedMessages.messages); setInitialMessages(storedMessages.messages);
setUrlId(storedMessages.urlId); setUrlId(storedMessages.urlId);
setDescription(storedMessages.description); description.set(storedMessages.description);
setChatId(storedMessages.id); chatId.set(storedMessages.id);
} else { } else {
navigate(`/`, { replace: true }); navigate(`/`, { replace: true });
} }
@ -61,7 +62,7 @@ export function useChatHistory() {
}, []); }, []);
return { return {
ready: !chatId || ready, ready: !mixedId || ready,
initialMessages, initialMessages,
storeMessageHistory: async (messages: Message[]) => { storeMessageHistory: async (messages: Message[]) => {
if (!db || messages.length === 0) { if (!db || messages.length === 0) {
@ -77,27 +78,21 @@ export function useChatHistory() {
setUrlId(urlId); setUrlId(urlId);
} }
if (!description && firstArtifact?.title) { if (!description.get() && firstArtifact?.title) {
setDescription(firstArtifact?.title); description.set(firstArtifact?.title);
} }
if (initialMessages.length === 0) { if (initialMessages.length === 0 && !chatId.get()) {
if (!entryId) { const nextId = await getNextId(db);
const nextId = await getNextId(db);
await setMessages(db, nextId, messages, urlId, description); chatId.set(nextId);
setEntryId(nextId); if (!urlId) {
navigateChat(nextId);
if (!urlId) {
navigateChat(nextId);
}
} else {
await setMessages(db, entryId, messages, urlId, description);
} }
} else {
await setMessages(db, chatId as string, messages, urlId, description);
} }
await setMessages(db, chatId.get() as string, messages, urlId, description.get());
}, },
}; };
} }