mirror of
https://github.com/sqlchat/sqlchat.git
synced 2025-09-28 02:24:49 +08:00
refactor: move conversation list into component
This commit is contained in:
@ -2,17 +2,16 @@ import { Drawer } from "@mui/material";
|
|||||||
import { head } from "lodash-es";
|
import { head } from "lodash-es";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useConversationStore, useConnectionStore, useLayoutStore, ResponsiveWidth } from "@/store";
|
import { useConnectionStore, useLayoutStore, ResponsiveWidth } from "@/store";
|
||||||
import { Conversation, Connection } from "@/types";
|
import { Connection } from "@/types";
|
||||||
import Select from "./kit/Select";
|
import Select from "./kit/Select";
|
||||||
import Tooltip from "./kit/Tooltip";
|
import Tooltip from "./kit/Tooltip";
|
||||||
import Dropdown, { DropdownItem } from "./kit/Dropdown";
|
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import EngineIcon from "./EngineIcon";
|
import EngineIcon from "./EngineIcon";
|
||||||
import DarkModeSwitch from "./DarkModeSwitch";
|
import DarkModeSwitch from "./DarkModeSwitch";
|
||||||
import CreateConnectionModal from "./CreateConnectionModal";
|
import CreateConnectionModal from "./CreateConnectionModal";
|
||||||
import SettingModal from "./SettingModal";
|
import SettingModal from "./SettingModal";
|
||||||
import UpdateConversationModal from "./UpdateConversationModal";
|
import ConversationList from "./Sidebar/ConversationList";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
showCreateConnectionModal: boolean;
|
showCreateConnectionModal: boolean;
|
||||||
@ -24,23 +23,16 @@ const ConnectionSidebar = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
const connectionStore = useConnectionStore();
|
const connectionStore = useConnectionStore();
|
||||||
const conversationStore = useConversationStore();
|
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
showCreateConnectionModal: false,
|
showCreateConnectionModal: false,
|
||||||
showSettingModal: false,
|
showSettingModal: false,
|
||||||
showUpdateConversationModal: false,
|
showUpdateConversationModal: false,
|
||||||
});
|
});
|
||||||
const [editConnectionModalContext, setEditConnectionModalContext] = useState<Connection>();
|
const [editConnectionModalContext, setEditConnectionModalContext] = useState<Connection>();
|
||||||
const [updateConversationModalContext, setUpdateConversationModalContext] = useState<Conversation>();
|
|
||||||
const [isRequestingDatabase, setIsRequestingDatabase] = useState<boolean>(false);
|
const [isRequestingDatabase, setIsRequestingDatabase] = useState<boolean>(false);
|
||||||
const connectionList = connectionStore.connectionList;
|
const connectionList = connectionStore.connectionList;
|
||||||
const currentConnectionCtx = connectionStore.currentConnectionCtx;
|
const currentConnectionCtx = connectionStore.currentConnectionCtx;
|
||||||
const databaseList = connectionStore.databaseList.filter((database) => database.connectionId === currentConnectionCtx?.connection.id);
|
const databaseList = connectionStore.databaseList.filter((database) => database.connectionId === currentConnectionCtx?.connection.id);
|
||||||
const conversationList = conversationStore.conversationList.filter(
|
|
||||||
(conversation) =>
|
|
||||||
conversation.connectionId === currentConnectionCtx?.connection.id &&
|
|
||||||
conversation.databaseName === currentConnectionCtx?.database?.name
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleWindowResize = () => {
|
const handleWindowResize = () => {
|
||||||
@ -87,13 +79,6 @@ const ConnectionSidebar = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleUpdateConversationModal = (show = true) => {
|
|
||||||
setState({
|
|
||||||
...state,
|
|
||||||
showUpdateConversationModal: show,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleConnectionSelect = async (connection: Connection) => {
|
const handleConnectionSelect = async (connection: Connection) => {
|
||||||
const databaseList = await connectionStore.getOrFetchDatabaseList(connection);
|
const databaseList = await connectionStore.getOrFetchDatabaseList(connection);
|
||||||
connectionStore.setCurrentConnectionCtx({
|
connectionStore.setCurrentConnectionCtx({
|
||||||
@ -123,36 +108,6 @@ const ConnectionSidebar = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateConversation = () => {
|
|
||||||
if (!currentConnectionCtx) {
|
|
||||||
conversationStore.createConversation();
|
|
||||||
} else {
|
|
||||||
conversationStore.createConversation(currentConnectionCtx.connection.id, currentConnectionCtx.database?.name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleConversationSelect = (conversation: Conversation) => {
|
|
||||||
conversationStore.setCurrentConversationId(conversation.id);
|
|
||||||
if (layoutStore.isMobileView) {
|
|
||||||
layoutStore.toggleSidebar(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEditConversation = (conversation: Conversation) => {
|
|
||||||
setUpdateConversationModalContext(conversation);
|
|
||||||
setState({
|
|
||||||
...state,
|
|
||||||
showUpdateConversationModal: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteConversation = (conversation: Conversation) => {
|
|
||||||
conversationStore.clearConversation((item) => item.id !== conversation.id);
|
|
||||||
if (conversationStore.currentConversationId === conversation.id) {
|
|
||||||
conversationStore.setCurrentConversationId(undefined);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Drawer
|
<Drawer
|
||||||
@ -240,53 +195,7 @@ const ConnectionSidebar = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{conversationList.map((conversation) => (
|
<ConversationList />
|
||||||
<div
|
|
||||||
key={conversation.id}
|
|
||||||
className={`w-full mt-2 first:mt-4 py-3 pl-4 pr-2 rounded-lg flex flex-row justify-start items-center cursor-pointer dark:text-gray-300 border border-transparent group hover:bg-white dark:hover:bg-zinc-800 ${
|
|
||||||
conversation.id === conversationStore.currentConversationId && "bg-white dark:bg-zinc-800 border-gray-200 font-medium"
|
|
||||||
}`}
|
|
||||||
onClick={() => handleConversationSelect(conversation)}
|
|
||||||
>
|
|
||||||
{conversation.id === conversationStore.currentConversationId ? (
|
|
||||||
<Icon.IoChatbubble className="w-5 h-auto mr-1.5 shrink-0" />
|
|
||||||
) : (
|
|
||||||
<Icon.IoChatbubbleOutline className="w-5 h-auto mr-1.5 opacity-80 shrink-0" />
|
|
||||||
)}
|
|
||||||
<span className="truncate grow">{conversation.title || "SQL Chat"}</span>
|
|
||||||
<Dropdown
|
|
||||||
tigger={
|
|
||||||
<button className="w-4 h-4 shrink-0 group-hover:visible invisible flex justify-center items-center text-gray-400 hover:text-gray-500">
|
|
||||||
<Icon.FiMoreHorizontal className="w-full h-auto" />
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="p-1 flex flex-col justify-start items-start bg-white dark:bg-zinc-900 shadow-lg rounded-lg">
|
|
||||||
<DropdownItem
|
|
||||||
className="w-full p-1 px-2 flex flex-row justify-start items-center rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-zinc-800"
|
|
||||||
onClick={() => handleEditConversation(conversation)}
|
|
||||||
>
|
|
||||||
<Icon.FiEdit3 className="w-4 h-auto mr-2 opacity-70" />
|
|
||||||
{t("common.edit")}
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem
|
|
||||||
className="w-full p-1 px-2 flex flex-row justify-start items-center rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-zinc-800"
|
|
||||||
onClick={() => handleDeleteConversation(conversation)}
|
|
||||||
>
|
|
||||||
<Icon.IoTrash className="w-4 h-auto mr-2 opacity-70" />
|
|
||||||
{t("common.delete")}
|
|
||||||
</DropdownItem>
|
|
||||||
</div>
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<button
|
|
||||||
className="w-full my-4 py-3 px-4 border dark:border-zinc-800 rounded-lg flex flex-row justify-center items-center text-gray-500 dark:text-gray-400 hover:text-gray-700 hover:bg-gray-50 dark:hover:bg-zinc-800"
|
|
||||||
onClick={handleCreateConversation}
|
|
||||||
>
|
|
||||||
<Icon.AiOutlinePlus className="w-5 h-auto mr-1" />
|
|
||||||
{t("conversation.new-chat")}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="sticky bottom-0 w-full flex flex-col justify-center bg-gray-100 dark:bg-zinc-700 backdrop-blur bg-opacity-60 pb-4 py-2">
|
<div className="sticky bottom-0 w-full flex flex-col justify-center bg-gray-100 dark:bg-zinc-700 backdrop-blur bg-opacity-60 pb-4 py-2">
|
||||||
<a
|
<a
|
||||||
@ -333,10 +242,6 @@ const ConnectionSidebar = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{state.showSettingModal && <SettingModal close={() => toggleSettingModal(false)} />}
|
{state.showSettingModal && <SettingModal close={() => toggleSettingModal(false)} />}
|
||||||
|
|
||||||
{updateConversationModalContext && state.showUpdateConversationModal && (
|
|
||||||
<UpdateConversationModal close={() => toggleUpdateConversationModal(false)} conversation={updateConversationModalContext} />
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
123
src/components/Sidebar/ConversationList.tsx
Normal file
123
src/components/Sidebar/ConversationList.tsx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useConversationStore, useConnectionStore, useLayoutStore } from "@/store";
|
||||||
|
import { Conversation } from "@/types";
|
||||||
|
import Dropdown, { DropdownItem } from "../kit/Dropdown";
|
||||||
|
import Icon from "../Icon";
|
||||||
|
import UpdateConversationModal from "../UpdateConversationModal";
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
showUpdateConversationModal: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConversationList = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const layoutStore = useLayoutStore();
|
||||||
|
const connectionStore = useConnectionStore();
|
||||||
|
const conversationStore = useConversationStore();
|
||||||
|
const [state, setState] = useState<State>({
|
||||||
|
showUpdateConversationModal: false,
|
||||||
|
});
|
||||||
|
const [updateConversationModalContext, setUpdateConversationModalContext] = useState<Conversation>();
|
||||||
|
const currentConnectionCtx = connectionStore.currentConnectionCtx;
|
||||||
|
const conversationList = conversationStore.conversationList.filter(
|
||||||
|
(conversation) =>
|
||||||
|
conversation.connectionId === currentConnectionCtx?.connection.id &&
|
||||||
|
conversation.databaseName === currentConnectionCtx?.database?.name
|
||||||
|
);
|
||||||
|
|
||||||
|
const toggleUpdateConversationModal = (show = true) => {
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
showUpdateConversationModal: show,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreateConversation = () => {
|
||||||
|
if (!currentConnectionCtx) {
|
||||||
|
conversationStore.createConversation();
|
||||||
|
} else {
|
||||||
|
conversationStore.createConversation(currentConnectionCtx.connection.id, currentConnectionCtx.database?.name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConversationSelect = (conversation: Conversation) => {
|
||||||
|
conversationStore.setCurrentConversationId(conversation.id);
|
||||||
|
if (layoutStore.isMobileView) {
|
||||||
|
layoutStore.toggleSidebar(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditConversation = (conversation: Conversation) => {
|
||||||
|
setUpdateConversationModalContext(conversation);
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
showUpdateConversationModal: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteConversation = (conversation: Conversation) => {
|
||||||
|
conversationStore.clearConversation((item) => item.id !== conversation.id);
|
||||||
|
if (conversationStore.currentConversationId === conversation.id) {
|
||||||
|
conversationStore.setCurrentConversationId(undefined);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{conversationList.map((conversation) => (
|
||||||
|
<div
|
||||||
|
key={conversation.id}
|
||||||
|
className={`w-full mt-2 first:mt-4 py-3 pl-4 pr-2 rounded-lg flex flex-row justify-start items-center cursor-pointer dark:text-gray-300 border border-transparent group hover:bg-white dark:hover:bg-zinc-800 ${
|
||||||
|
conversation.id === conversationStore.currentConversationId && "bg-white dark:bg-zinc-800 border-gray-200 font-medium"
|
||||||
|
}`}
|
||||||
|
onClick={() => handleConversationSelect(conversation)}
|
||||||
|
>
|
||||||
|
{conversation.id === conversationStore.currentConversationId ? (
|
||||||
|
<Icon.IoChatbubble className="w-5 h-auto mr-1.5 shrink-0" />
|
||||||
|
) : (
|
||||||
|
<Icon.IoChatbubbleOutline className="w-5 h-auto mr-1.5 opacity-80 shrink-0" />
|
||||||
|
)}
|
||||||
|
<span className="truncate grow">{conversation.title || "SQL Chat"}</span>
|
||||||
|
<Dropdown
|
||||||
|
tigger={
|
||||||
|
<button className="w-4 h-4 shrink-0 group-hover:visible invisible flex justify-center items-center text-gray-400 hover:text-gray-500">
|
||||||
|
<Icon.FiMoreHorizontal className="w-full h-auto" />
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="p-1 flex flex-col justify-start items-start bg-white dark:bg-zinc-900 shadow-lg rounded-lg">
|
||||||
|
<DropdownItem
|
||||||
|
className="w-full p-1 px-2 flex flex-row justify-start items-center rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-zinc-800"
|
||||||
|
onClick={() => handleEditConversation(conversation)}
|
||||||
|
>
|
||||||
|
<Icon.FiEdit3 className="w-4 h-auto mr-2 opacity-70" />
|
||||||
|
{t("common.edit")}
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem
|
||||||
|
className="w-full p-1 px-2 flex flex-row justify-start items-center rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-zinc-800"
|
||||||
|
onClick={() => handleDeleteConversation(conversation)}
|
||||||
|
>
|
||||||
|
<Icon.IoTrash className="w-4 h-auto mr-2 opacity-70" />
|
||||||
|
{t("common.delete")}
|
||||||
|
</DropdownItem>
|
||||||
|
</div>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<button
|
||||||
|
className="w-full my-4 py-3 px-4 border dark:border-zinc-800 rounded-lg flex flex-row justify-center items-center text-gray-500 dark:text-gray-400 hover:text-gray-700 hover:bg-gray-50 dark:hover:bg-zinc-800"
|
||||||
|
onClick={handleCreateConversation}
|
||||||
|
>
|
||||||
|
<Icon.AiOutlinePlus className="w-5 h-auto mr-1" />
|
||||||
|
{t("conversation.new-chat")}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{updateConversationModalContext && state.showUpdateConversationModal && (
|
||||||
|
<UpdateConversationModal close={() => toggleUpdateConversationModal(false)} conversation={updateConversationModalContext} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConversationList;
|
Reference in New Issue
Block a user