From 4e4bf51bba22fd94defddebfb35d763ede9c5e1a Mon Sep 17 00:00:00 2001 From: Will Chen Date: Fri, 2 May 2025 15:43:40 -0700 Subject: [PATCH] Delete chat (#77) --- src/components/ChatList.tsx | 102 +++++++++++++++++++++++------- src/ipc/handlers/chat_handlers.ts | 11 ++++ src/ipc/ipc_client.ts | 14 +++- src/preload.ts | 1 + 4 files changed, 103 insertions(+), 25 deletions(-) diff --git a/src/components/ChatList.tsx b/src/components/ChatList.tsx index 2766ea7..1a74ad9 100644 --- a/src/components/ChatList.tsx +++ b/src/components/ChatList.tsx @@ -2,12 +2,12 @@ import { useEffect } from "react"; import { useNavigate, useRouterState } from "@tanstack/react-router"; import type { ChatSummary } from "@/lib/schemas"; import { formatDistanceToNow } from "date-fns"; -import { PlusCircle } from "lucide-react"; +import { PlusCircle, MoreVertical, Trash2 } from "lucide-react"; import { useAtom } from "jotai"; import { selectedChatIdAtom } from "@/atoms/chatAtoms"; import { selectedAppIdAtom } from "@/atoms/appAtoms"; import { IpcClient } from "@/ipc/ipc_client"; -import { showError } from "@/lib/toast"; +import { showError, showSuccess } from "@/lib/toast"; import { SidebarGroup, SidebarGroupContent, @@ -16,6 +16,12 @@ import { SidebarMenuItem, } from "@/components/ui/sidebar"; import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { useChats } from "@/hooks/useChats"; export function ChatList({ show }: { show?: boolean }) { @@ -82,6 +88,28 @@ export function ChatList({ show }: { show?: boolean }) { } }; + const handleDeleteChat = async (chatId: number) => { + try { + const result = await IpcClient.getInstance().deleteChat(chatId); + if (!result.success) { + showError("Failed to delete chat"); + return; + } + showSuccess("Chat deleted successfully"); + + // If the deleted chat was selected, navigate to home + if (selectedChatId === chatId) { + setSelectedChatId(null); + navigate({ to: "/chat" }); + } + + // Refresh the chat list + await refreshChats(); + } catch (error) { + showError(`Failed to delete chat: ${(error as any).toString()}`); + } + }; + return ( Recent Chats @@ -108,28 +136,54 @@ export function ChatList({ show }: { show?: boolean }) { {chats.map((chat) => ( - +
+ + + {selectedChatId === chat.id && ( + + + + + + handleDeleteChat(chat.id)} + > + + Delete Chat + + + + )} +
))}
diff --git a/src/ipc/handlers/chat_handlers.ts b/src/ipc/handlers/chat_handlers.ts index 43080c6..f9d21cf 100644 --- a/src/ipc/handlers/chat_handlers.ts +++ b/src/ipc/handlers/chat_handlers.ts @@ -63,4 +63,15 @@ export function registerChatHandlers() { return allChats; } ); + + ipcMain.handle("delete-chat", async (_, chatId: number) => { + try { + // Delete the chat and its associated messages + await db.delete(chats).where(eq(chats.id, chatId)); + return { success: true }; + } catch (error) { + console.error("Error deleting chat:", error); + return { success: false, error: (error as Error).message }; + } + }); } diff --git a/src/ipc/ipc_client.ts b/src/ipc/ipc_client.ts index a11932a..472fc02 100644 --- a/src/ipc/ipc_client.ts +++ b/src/ipc/ipc_client.ts @@ -277,7 +277,19 @@ export class IpcClient { public async createChat(appId: number): Promise { try { const chatId = await this.ipcRenderer.invoke("create-chat", appId); - return chatId; + return chatId as number; + } catch (error) { + showError(error); + throw error; + } + } + + public async deleteChat(chatId: number): Promise<{ success: boolean }> { + try { + const result = (await this.ipcRenderer.invoke("delete-chat", chatId)) as { + success: boolean; + }; + return result; } catch (error) { showError(error); throw error; diff --git a/src/preload.ts b/src/preload.ts index 14a73fa..dcbd20f 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -56,6 +56,7 @@ const validInvokeChannels = [ "window:close", "window:get-platform", "upload-to-signed-url", + "delete-chat", ] as const; // Add valid receive channels