From ee20272a9ede9f45bd04eb2789da92c3d96d9d95 Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 28 Mar 2023 18:31:56 +0800 Subject: [PATCH] feat: implement edit chat title modal --- components/ChatView/index.tsx | 9 ++++- components/ConnectionSidebar.tsx | 48 ++++++++++++++++++++----- components/EditChatTitleModal.tsx | 60 +++++++++++++++++++++++++++++++ components/Icon.tsx | 2 ++ store/chat.ts | 12 +++++++ 5 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 components/EditChatTitleModal.tsx diff --git a/components/ChatView/index.tsx b/components/ChatView/index.tsx index 74798f2..08d7776 100644 --- a/components/ChatView/index.tsx +++ b/components/ChatView/index.tsx @@ -58,6 +58,13 @@ const ChatView = () => { }, [lastMessage?.isGenerated, lastMessage?.content]); useEffect(() => { + if ( + currentChat?.connectionId === connectionStore.currentConnectionCtx?.connection.id && + currentChat?.databaseName === connectionStore.currentConnectionCtx?.database?.name + ) { + return; + } + // Auto select the first chat when the current connection changes. const chatList = chatStore.chatList.filter( (chat) => @@ -65,7 +72,7 @@ const ChatView = () => { chat.databaseName === connectionStore.currentConnectionCtx?.database?.name ); chatStore.setCurrentChat(head(chatList)); - }, [connectionStore.currentConnectionCtx]); + }, [currentChat, connectionStore.currentConnectionCtx]); const sendMessageToCurrentChat = async () => { const currentChat = chatStore.getState().currentChat; diff --git a/components/ConnectionSidebar.tsx b/components/ConnectionSidebar.tsx index 721e194..69ac1ac 100644 --- a/components/ConnectionSidebar.tsx +++ b/components/ConnectionSidebar.tsx @@ -8,11 +8,13 @@ import EngineIcon from "./EngineIcon"; import CreateConnectionModal from "./CreateConnectionModal"; import SettingModal from "./SettingModal"; import ActionConfirmModal, { ActionConfirmModalProps } from "./ActionConfirmModal"; +import EditChatTitleModal from "./EditChatTitleModal"; interface State { showCreateConnectionModal: boolean; showSettingModal: boolean; showDeleteConnectionModal: boolean; + showEditChatTitleModal: boolean; } const ConnectionSidebar = () => { @@ -23,8 +25,10 @@ const ConnectionSidebar = () => { showCreateConnectionModal: false, showSettingModal: false, showDeleteConnectionModal: false, + showEditChatTitleModal: false, }); const [deleteConnectionModalContext, setDeleteConnectionModalContext] = useState(); + const [editChatTitleModalContext, setEditChatTitleModalContext] = useState(); const connectionList = connectionStore.connectionList; const currentConnectionCtx = connectionStore.currentConnectionCtx; const databaseList = connectionStore.databaseList.filter((database) => database.connectionId === currentConnectionCtx?.connection.id); @@ -46,6 +50,13 @@ const ConnectionSidebar = () => { }); }; + const toggleEditChatTitleModal = (show = true) => { + setState({ + ...state, + showEditChatTitleModal: show, + }); + }; + const handleConnectionSelect = async (connection: Connection) => { const databaseList = await connectionStore.getOrFetchDatabaseList(connection); connectionStore.setCurrentConnectionCtx({ @@ -103,6 +114,14 @@ const ConnectionSidebar = () => { layoutStore.toggleSidebar(false); }; + const handleEditChatTitle = (chat: Chat) => { + setEditChatTitleModalContext(chat); + setState({ + ...state, + showEditChatTitleModal: true, + }); + }; + const handleDeleteChat = (chat: Chat) => { chatStore.clearChat((item) => item.id !== chat.id); if (chatStore.currentChat?.id === chat.id) { @@ -181,7 +200,7 @@ const ConnectionSidebar = () => { {chatList.map((chat) => (
handleChatSelect(chat)} @@ -192,14 +211,21 @@ const ConnectionSidebar = () => { )} {chat.title || "SQL Chat"} - { - e.stopPropagation(); - handleDeleteChat(chat); - }} - > - + + { + e.stopPropagation(); + handleEditChatTitle(chat); + }} + /> + { + e.stopPropagation(); + handleDeleteChat(chat); + }} + />
))} @@ -232,6 +258,10 @@ const ConnectionSidebar = () => { />, document.body )} + + {state.showEditChatTitleModal && + editChatTitleModalContext && + createPortal( toggleEditChatTitleModal(false)} chat={editChatTitleModalContext} />, document.body)} ); }; diff --git a/components/EditChatTitleModal.tsx b/components/EditChatTitleModal.tsx new file mode 100644 index 0000000..90c4575 --- /dev/null +++ b/components/EditChatTitleModal.tsx @@ -0,0 +1,60 @@ +import { useState } from "react"; +import { toast } from "react-hot-toast"; +import { useChatStore } from "@/store"; +import { Chat } from "@/types"; +import Icon from "./Icon"; + +interface Props { + chat: Chat; + close: () => void; +} + +const EditMessageTitleModal = (props: Props) => { + const { close, chat } = props; + const chatStore = useChatStore(); + const [title, setTitle] = useState(chat.title); + const allowSave = title !== ""; + + const handleSaveEdit = () => { + const formatedTitle = title.trim(); + if (formatedTitle === "") { + return; + } + + chatStore.updateChat(chat.id, { + title: formatedTitle, + }); + toast.success("Chat title updated"); + close(); + }; + + return ( +
+
+

Edit chat title

+ +
+ setTitle(e.target.value)} + /> +
+
+ + +
+
+
+ ); +}; + +export default EditMessageTitleModal; diff --git a/components/Icon.tsx b/components/Icon.tsx index 97e20b5..f5ddd80 100644 --- a/components/Icon.tsx +++ b/components/Icon.tsx @@ -2,6 +2,7 @@ import * as Ai from "react-icons/ai"; import * as Bi from "react-icons/bi"; import * as Bs from "react-icons/bs"; import * as Di from "react-icons/di"; +import * as Fi from "react-icons/fi"; import * as Io from "react-icons/io"; import * as Io5 from "react-icons/io5"; @@ -10,6 +11,7 @@ const Icon = { ...Bi, ...Bs, ...Di, + ...Fi, ...Io, ...Io5, }; diff --git a/store/chat.ts b/store/chat.ts index 74ea3f8..bce0335 100644 --- a/store/chat.ts +++ b/store/chat.ts @@ -1,4 +1,5 @@ import dayjs from "dayjs"; +import { uniqBy } from "lodash-es"; import { create } from "zustand"; import { persist } from "zustand/middleware"; import { Chat, Id } from "@/types"; @@ -19,6 +20,7 @@ interface ChatState { getState: () => ChatState; createChat: (connectionId?: Id, databaseName?: string) => Chat; setCurrentChat: (chat: Chat | undefined) => void; + updateChat: (chatId: Id, chat: Partial) => void; clearChat: (filter: (chat: Chat) => boolean) => void; } @@ -40,6 +42,16 @@ export const useChatStore = create()( return chat; }, setCurrentChat: (chat: Chat | undefined) => set(() => ({ currentChat: chat })), + updateChat: (chatId: Id, chat: Partial) => { + const rawChat = get().chatList.find((chat) => chat.id === chatId); + if (!rawChat) { + return; + } + Object.assign(rawChat, chat); + set((state) => ({ + chatList: uniqBy([...state.chatList], (chat) => chat.id), + })); + }, clearChat: (filter: (chat: Chat) => boolean) => { set((state) => ({ ...state,