mirror of
https://github.com/usememos/memos.git
synced 2025-01-06 23:36:31 +03:00
chore: remove memo chat components (#2073)
This commit is contained in:
parent
1ce82ba0d6
commit
aa26cc30d7
@ -115,22 +115,6 @@ const Header = () => {
|
|||||||
|
|
||||||
{!isVisitorMode && (
|
{!isVisitorMode && (
|
||||||
<>
|
<>
|
||||||
{globalStore.isDev() && (
|
|
||||||
<NavLink
|
|
||||||
to="/memo-chat"
|
|
||||||
id="header-memo-chat"
|
|
||||||
className={({ isActive }) =>
|
|
||||||
classNames(
|
|
||||||
"px-4 pr-5 py-2 rounded-full border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
|
||||||
isActive ? "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
<Icon.Bot className="mr-3 w-6 h-auto opacity-70" /> {t("memo-chat.title")}
|
|
||||||
</>
|
|
||||||
</NavLink>
|
|
||||||
)}
|
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/archived"
|
to="/archived"
|
||||||
id="header-archived"
|
id="header-archived"
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
import Textarea from "@mui/joy/Textarea/Textarea";
|
|
||||||
import Icon from "@/components/Icon";
|
|
||||||
import { useTranslate } from "@/utils/i18n";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
question: string;
|
|
||||||
handleQuestionTextareaChange: any;
|
|
||||||
setIsInIME: any;
|
|
||||||
handleKeyDown: any;
|
|
||||||
handleSendQuestionButtonClick: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ChatInput = ({ question, handleQuestionTextareaChange, setIsInIME, handleKeyDown, handleSendQuestionButtonClick }: Props) => {
|
|
||||||
const t = useTranslate();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full relative mt-4">
|
|
||||||
<Textarea
|
|
||||||
className="w-full"
|
|
||||||
placeholder={t("memo-chat.placeholder")}
|
|
||||||
value={question}
|
|
||||||
minRows={1}
|
|
||||||
maxRows={5}
|
|
||||||
onChange={handleQuestionTextareaChange}
|
|
||||||
onCompositionStart={() => setIsInIME(true)}
|
|
||||||
onCompositionEnd={() => setIsInIME(false)}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
/>
|
|
||||||
<Icon.Send
|
|
||||||
className="cursor-pointer w-7 p-1 h-auto rounded-md bg-gray-100 dark:bg-zinc-800 absolute right-2 bottom-1.5 shadow hover:opacity-80"
|
|
||||||
onClick={handleSendQuestionButtonClick}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ChatInput;
|
|
@ -1,60 +0,0 @@
|
|||||||
import toast from "react-hot-toast";
|
|
||||||
import Icon from "@/components/Icon";
|
|
||||||
import { marked } from "@/labs/marked";
|
|
||||||
import { useMemoStore } from "@/store/module";
|
|
||||||
import { Message } from "@/store/v1/message";
|
|
||||||
import { useTranslate } from "@/utils/i18n";
|
|
||||||
import Dropdown from "../kit/Dropdown";
|
|
||||||
|
|
||||||
interface MessageProps {
|
|
||||||
index: number;
|
|
||||||
message: Message;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ChatMessage = ({ index, message }: MessageProps) => {
|
|
||||||
const memoStore = useMemoStore();
|
|
||||||
const t = useTranslate();
|
|
||||||
|
|
||||||
const handleSaveAsMemos = async () => {
|
|
||||||
await memoStore.createMemo({
|
|
||||||
content: message.content,
|
|
||||||
visibility: "PRIVATE",
|
|
||||||
resourceIdList: [],
|
|
||||||
relationList: [],
|
|
||||||
});
|
|
||||||
toast.success(t("memo-chat.save-as-memo-success"));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={index} className="w-full flex flex-col justify-start items-start space-y-2">
|
|
||||||
{message.role === "user" ? (
|
|
||||||
<div className="w-full flex flex-row justify-end items-start pl-6">
|
|
||||||
<span className="word-break shadow rounded-lg rounded-tr-none px-3 py-2 opacity-80 bg-white dark:bg-zinc-800">
|
|
||||||
{message.content}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="w-full flex flex-row justify-start items-start pr-8 space-x-2">
|
|
||||||
<Icon.Bot className="mt-2 shrink-0 mr-1 w-6 h-auto opacity-80" />
|
|
||||||
<div className="memo-content-wrapper !w-auto flex flex-col justify-start items-start shadow rounded-lg rounded-tl-none px-3 py-2 bg-white dark:bg-zinc-800">
|
|
||||||
<div className="memo-content-text">{marked(message.content)}</div>
|
|
||||||
</div>
|
|
||||||
<Dropdown
|
|
||||||
actions={
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
className="w-full m-auto text-left text-sm whitespace-nowrap leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
|
|
||||||
onClick={() => handleSaveAsMemos()}
|
|
||||||
>
|
|
||||||
{t("memo-chat.save-as-memo")}
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ChatMessage;
|
|
@ -1,33 +0,0 @@
|
|||||||
import Icon from "@/components/Icon";
|
|
||||||
import { Conversation } from "@/store/v1/conversation";
|
|
||||||
|
|
||||||
interface ConversationTabProps {
|
|
||||||
item: Conversation;
|
|
||||||
selectedConversationId: string;
|
|
||||||
setSelectedConversationId: (id: string) => void;
|
|
||||||
closeConversation: (e: any) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ConversationTab = ({ item, selectedConversationId, setSelectedConversationId, closeConversation }: ConversationTabProps) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`flex rounded-lg h-8 px-3 cursor-pointer border dark:border-zinc-600 ${
|
|
||||||
selectedConversationId === item.messageStorageId ? "bg-white dark:bg-zinc-700" : "bg-gray-200 dark:bg-zinc-800 opacity-60"
|
|
||||||
}`}
|
|
||||||
key={item.messageStorageId}
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedConversationId(item.messageStorageId);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="truncate m-auto">{item.name}</div>
|
|
||||||
<Icon.X
|
|
||||||
className="ml-1 w-4 h-auto m-auto cursor-pointer opacity-60"
|
|
||||||
onClick={(e: any) => {
|
|
||||||
closeConversation(e);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ConversationTab;
|
|
@ -390,18 +390,6 @@
|
|||||||
"sat": "Sat",
|
"sat": "Sat",
|
||||||
"sun": "Sun"
|
"sun": "Sun"
|
||||||
},
|
},
|
||||||
"memo-chat": {
|
|
||||||
"title": "Memo Chat",
|
|
||||||
"save-as-memo": "Save as memo",
|
|
||||||
"save-as-memo-success": "Saved as memo successfully.",
|
|
||||||
"not-enabled": "You have not set up your OpenAI API key.",
|
|
||||||
"go-to-settings": "Go to settings",
|
|
||||||
"placeholder": "Ask anything…",
|
|
||||||
"no-message": "No Message",
|
|
||||||
"default-message-group-title": "Default Session",
|
|
||||||
"create-message-group-title": "Create Session",
|
|
||||||
"label-message-group-name-title": "Session Name"
|
|
||||||
},
|
|
||||||
"embed-memo": {
|
"embed-memo": {
|
||||||
"title": "Embed Memo",
|
"title": "Embed Memo",
|
||||||
"text": "Copy and paste the below codes into your blog or website.",
|
"text": "Copy and paste the below codes into your blog or website.",
|
||||||
|
@ -385,18 +385,6 @@
|
|||||||
"sat": "शनिवार",
|
"sat": "शनिवार",
|
||||||
"sun": "रविवार"
|
"sun": "रविवार"
|
||||||
},
|
},
|
||||||
"memo-chat": {
|
|
||||||
"title": "मेमो चैट",
|
|
||||||
"save-as-memo": "मेमो के रूप में सहेजें",
|
|
||||||
"save-as-memo-success": "मेमो के रूप में सफलतापूर्वक सहेजा गया।",
|
|
||||||
"not-enabled": "आपने अपना OpenAI API कुंजी सेट नहीं किया है।",
|
|
||||||
"go-to-settings": "सेटिंग्स पर जाएं",
|
|
||||||
"placeholder": "कुछ भी पूछें...",
|
|
||||||
"no-message": "कोई संदेश नहीं",
|
|
||||||
"default-message-group-title": "डिफ़ॉल्ट सत्र",
|
|
||||||
"create-message-group-title": "सत्र बनाएँ",
|
|
||||||
"label-message-group-name-title": "सत्र का नाम"
|
|
||||||
},
|
|
||||||
"embed-memo": {
|
"embed-memo": {
|
||||||
"title": "मेमो एम्बेड करें",
|
"title": "मेमो एम्बेड करें",
|
||||||
"text": "नीचे दिए गए कोड को अपने ब्लॉग या वेबसाइट में कॉपी और पेस्ट करें।",
|
"text": "नीचे दिए गए कोड को अपने ब्लॉग या वेबसाइट में कॉपी और पेस्ट करें।",
|
||||||
|
@ -369,16 +369,6 @@
|
|||||||
"sat": "土",
|
"sat": "土",
|
||||||
"sun": "日"
|
"sun": "日"
|
||||||
},
|
},
|
||||||
"memo-chat": {
|
|
||||||
"title": "AIに尋ねる",
|
|
||||||
"not-enabled": "OpenAIのAPI keyが設定されていません",
|
|
||||||
"go-to-settings": "設定へ",
|
|
||||||
"placeholder": "何か聞いてみてください...",
|
|
||||||
"default-message-group-title": "デフォルトセッション",
|
|
||||||
"create-message-group-title": "セッションを作成する",
|
|
||||||
"label-message-group-name-title": "セッションの名前"
|
|
||||||
},
|
|
||||||
|
|
||||||
"embed-memo": {
|
"embed-memo": {
|
||||||
"title": "メモを埋め込む",
|
"title": "メモを埋め込む",
|
||||||
"text": "コードをあなたのサイズにコピーペーストすればメモを埋め込めます",
|
"text": "コードをあなたのサイズにコピーペーストすればメモを埋め込めます",
|
||||||
|
@ -95,17 +95,6 @@
|
|||||||
"fetching-data": "请求数据中...",
|
"fetching-data": "请求数据中...",
|
||||||
"no-archived-memos": "没有归档的备忘录"
|
"no-archived-memos": "没有归档的备忘录"
|
||||||
},
|
},
|
||||||
"memo-chat": {
|
|
||||||
"go-to-settings": "前往设置",
|
|
||||||
"save-as-memo": "保存为 Memo",
|
|
||||||
"save-as-memo-success": "保存为 Memo 成功",
|
|
||||||
"not-enabled": "您尚未设置 OpenAI API 密钥。",
|
|
||||||
"placeholder": "随便问",
|
|
||||||
"title": "问 AI",
|
|
||||||
"default-message-group-title": "默认会话",
|
|
||||||
"create-message-group-title": "新建会话",
|
|
||||||
"label-message-group-name-title": "会话名称"
|
|
||||||
},
|
|
||||||
"auth": {
|
"auth": {
|
||||||
"host-tip": "你正在注册为管理员用户账号。",
|
"host-tip": "你正在注册为管理员用户账号。",
|
||||||
"new-password": "新密码",
|
"new-password": "新密码",
|
||||||
|
@ -378,16 +378,6 @@
|
|||||||
"friday": "星期五",
|
"friday": "星期五",
|
||||||
"wednesday": "星期三"
|
"wednesday": "星期三"
|
||||||
},
|
},
|
||||||
"memo-chat": {
|
|
||||||
"title": "問 AI",
|
|
||||||
"not-enabled": "您尚未設置 OpenAI API 密鑰。",
|
|
||||||
"go-to-settings": "前往設定",
|
|
||||||
"placeholder": "凡事皆可問…",
|
|
||||||
"default-message-group-title": "預設會話",
|
|
||||||
"create-message-group-title": "建立對話",
|
|
||||||
"label-message-group-name-title": "對話名稱"
|
|
||||||
},
|
|
||||||
|
|
||||||
"embed-memo": {
|
"embed-memo": {
|
||||||
"title": "嵌入備忘錄",
|
"title": "嵌入備忘錄",
|
||||||
"text": "將以下程式碼複製並貼上到您的網誌或網站中。",
|
"text": "將以下程式碼複製並貼上到您的網誌或網站中。",
|
||||||
|
@ -1,218 +0,0 @@
|
|||||||
import { fetchEventSource } from "@microsoft/fetch-event-source";
|
|
||||||
import { Button, Stack } from "@mui/joy";
|
|
||||||
import { head } from "lodash-es";
|
|
||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import { toast } from "react-hot-toast";
|
|
||||||
import Empty from "@/components/Empty";
|
|
||||||
import Icon from "@/components/Icon";
|
|
||||||
import ChatInput from "@/components/MemoChat/ChatInput";
|
|
||||||
import ChatMessage from "@/components/MemoChat/ChatMessage";
|
|
||||||
import ConversationTab from "@/components/MemoChat/ConversationTab";
|
|
||||||
import MobileHeader from "@/components/MobileHeader";
|
|
||||||
import * as api from "@/helpers/api";
|
|
||||||
import useLoading from "@/hooks/useLoading";
|
|
||||||
import { Conversation, useConversationStore } from "@/store/v1/conversation";
|
|
||||||
import { Message, useMessageStore } from "@/store/v1/message";
|
|
||||||
import { useTranslate } from "@/utils/i18n";
|
|
||||||
import { generateUUID } from "@/utils/uuid";
|
|
||||||
|
|
||||||
const chatStreaming = async (messageList: Array<Message>, onmessage: any, onclose: any) => {
|
|
||||||
await fetchEventSource("/api/v1/openai/chat-streaming", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(messageList),
|
|
||||||
async onopen() {
|
|
||||||
// to do nth
|
|
||||||
},
|
|
||||||
onmessage(event: any) {
|
|
||||||
onmessage(event);
|
|
||||||
},
|
|
||||||
onclose() {
|
|
||||||
onclose();
|
|
||||||
},
|
|
||||||
onerror(error: any) {
|
|
||||||
console.log("error", error);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const MemoChat = () => {
|
|
||||||
const t = useTranslate();
|
|
||||||
const fetchingState = useLoading(false);
|
|
||||||
const [isEnabled, setIsEnabled] = useState<boolean>(true);
|
|
||||||
const [isInIME, setIsInIME] = useState(false);
|
|
||||||
const [question, setQuestion] = useState<string>("");
|
|
||||||
const conversationStore = useConversationStore();
|
|
||||||
const conversationList = conversationStore.conversationList;
|
|
||||||
const [selectedConversationId, setSelectedConversationId] = useState<string>(head(conversationList)?.messageStorageId || "");
|
|
||||||
const messageStore = useMessageStore(selectedConversationId)();
|
|
||||||
const messageList = messageStore.messageList;
|
|
||||||
// The state didn't show in component, just for trigger re-render
|
|
||||||
const [message, setMessage] = useState<string>("");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
api.checkOpenAIEnabled().then(({ data }) => {
|
|
||||||
setIsEnabled(data);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// to new a conversation when no conversation
|
|
||||||
useEffect(() => {
|
|
||||||
if (conversationList.length === 0) {
|
|
||||||
newConversation();
|
|
||||||
}
|
|
||||||
}, [conversationList]);
|
|
||||||
|
|
||||||
// to select head message conversation(conversation) when conversation be deleted
|
|
||||||
useEffect(() => {
|
|
||||||
setSelectedConversationId(head(conversationList)?.messageStorageId || "");
|
|
||||||
}, [conversationList]);
|
|
||||||
|
|
||||||
const handleGotoSystemSetting = () => {
|
|
||||||
window.open(`/setting`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleQuestionTextareaChange = async (event: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
||||||
setQuestion(event.currentTarget.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyDown = (event: React.KeyboardEvent) => {
|
|
||||||
if (event.key === "Enter" && !event.shiftKey && !isInIME) {
|
|
||||||
event.preventDefault();
|
|
||||||
handleSendQuestionButtonClick().then();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSendQuestionButtonClick = async () => {
|
|
||||||
if (!question) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchingState.setLoading();
|
|
||||||
setQuestion("");
|
|
||||||
messageStore.addMessage({
|
|
||||||
id: generateUUID(),
|
|
||||||
role: "user",
|
|
||||||
content: question,
|
|
||||||
});
|
|
||||||
|
|
||||||
const messageId = generateUUID();
|
|
||||||
messageStore.addMessage({
|
|
||||||
id: messageId,
|
|
||||||
role: "assistant",
|
|
||||||
content: "",
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
fetchChatStreaming(messageId);
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error(error);
|
|
||||||
toast.error(error.response.data.error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchChatStreaming = async (messageId: string) => {
|
|
||||||
const messageList = messageStore.getState().messageList;
|
|
||||||
await chatStreaming(
|
|
||||||
messageList,
|
|
||||||
async (event: any) => {
|
|
||||||
messageStore.updateMessage(messageId, event.data);
|
|
||||||
// to trigger re-render
|
|
||||||
setMessage(message + event.data);
|
|
||||||
},
|
|
||||||
async () => {
|
|
||||||
fetchingState.setFinish();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const newConversation = () => {
|
|
||||||
const uuid = generateUUID();
|
|
||||||
// get the time HH:mm as the default name
|
|
||||||
const name = new Date().toLocaleTimeString("en-US", {
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
});
|
|
||||||
conversationStore.addConversation({
|
|
||||||
name: name,
|
|
||||||
messageStorageId: uuid,
|
|
||||||
});
|
|
||||||
setSelectedConversationId(uuid);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
|
|
||||||
<MobileHeader showSearch={false} />
|
|
||||||
<div className="w-full flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-700 text-black dark:text-gray-300">
|
|
||||||
<div className="w-full flex">
|
|
||||||
<div className="w-auto flex flex-row justify-start items-center select-none shrink-0 mr-4">
|
|
||||||
<Icon.Bot className="w-5 h-auto mr-1" /> {t("memo-chat.title")}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-row w-auto justify-start items-center overflow-y-hidden overflow-x-auto">
|
|
||||||
<div className="flex space-x-2 overflow-x-auto">
|
|
||||||
{conversationList.map((item: Conversation) => (
|
|
||||||
<ConversationTab
|
|
||||||
key={item.messageStorageId}
|
|
||||||
item={item}
|
|
||||||
selectedConversationId={selectedConversationId}
|
|
||||||
setSelectedConversationId={setSelectedConversationId}
|
|
||||||
closeConversation={(e) => {
|
|
||||||
// this is very important. otherwise, the select event also be clicked.
|
|
||||||
e.stopPropagation();
|
|
||||||
conversationStore.removeConversation(item);
|
|
||||||
toast.success("Remove successfully");
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<button className="btn-text px-1 ml-1 shrink-0">
|
|
||||||
<Icon.Plus
|
|
||||||
className="w-4 h-auto"
|
|
||||||
onClick={() => {
|
|
||||||
newConversation();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="dialog-content-container w-full">
|
|
||||||
<Stack spacing={2} style={{ width: "100%" }}>
|
|
||||||
{messageList.length == 0 && (
|
|
||||||
<div className="w-full mt-8 mb-8 flex flex-col justify-center items-center italic">
|
|
||||||
<Empty />
|
|
||||||
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("memo-chat.no-message")}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{messageList.map((message, index) => (
|
|
||||||
<ChatMessage key={index} message={message} index={index} />
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
{fetchingState.isLoading && (
|
|
||||||
<p className="w-full py-2 mt-4 flex flex-row justify-center items-center">
|
|
||||||
<Icon.Loader className="w-5 h-auto animate-spin" />
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{!isEnabled && (
|
|
||||||
<div className="w-full flex flex-col justify-center items-center mt-4 space-y-2">
|
|
||||||
<p>{t("memo-chat.not-enabled")}</p>
|
|
||||||
<Button onClick={() => handleGotoSystemSetting()}>{t("memo-chat.go-to-settings")}</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<ChatInput
|
|
||||||
question={question}
|
|
||||||
handleQuestionTextareaChange={handleQuestionTextareaChange}
|
|
||||||
setIsInIME={setIsInIME}
|
|
||||||
handleKeyDown={handleKeyDown}
|
|
||||||
handleSendQuestionButtonClick={handleSendQuestionButtonClick}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MemoChat;
|
|
@ -16,7 +16,6 @@ const Home = lazy(() => import("@/pages/Home"));
|
|||||||
const MemoDetail = lazy(() => import("@/pages/MemoDetail"));
|
const MemoDetail = lazy(() => import("@/pages/MemoDetail"));
|
||||||
const EmbedMemo = lazy(() => import("@/pages/EmbedMemo"));
|
const EmbedMemo = lazy(() => import("@/pages/EmbedMemo"));
|
||||||
const NotFound = lazy(() => import("@/pages/NotFound"));
|
const NotFound = lazy(() => import("@/pages/NotFound"));
|
||||||
const MemoChat = lazy(() => import("@/pages/MemoChat"));
|
|
||||||
|
|
||||||
const initialGlobalStateLoader = (() => {
|
const initialGlobalStateLoader = (() => {
|
||||||
let done = false;
|
let done = false;
|
||||||
@ -162,27 +161,6 @@ const router = createBrowserRouter([
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "memo-chat",
|
|
||||||
element: <MemoChat />,
|
|
||||||
loader: async () => {
|
|
||||||
await initialGlobalStateLoader();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await initialUserState();
|
|
||||||
} catch (error) {
|
|
||||||
// do nth
|
|
||||||
}
|
|
||||||
|
|
||||||
const { user } = store.getState().user;
|
|
||||||
|
|
||||||
if (isNullorUndefined(user)) {
|
|
||||||
return redirect("/auth");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
path: "archived",
|
path: "archived",
|
||||||
element: <Archived />,
|
element: <Archived />,
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
import { t } from "i18next";
|
|
||||||
import { create } from "zustand";
|
|
||||||
import { persist } from "zustand/middleware";
|
|
||||||
|
|
||||||
export interface Conversation {
|
|
||||||
name: string;
|
|
||||||
messageStorageId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ConversationState {
|
|
||||||
conversationList: Conversation[];
|
|
||||||
getState: () => ConversationState;
|
|
||||||
addConversation: (conversation: Conversation) => void;
|
|
||||||
removeConversation: (conversation: Conversation) => Conversation;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultConversation: Conversation = {
|
|
||||||
name: t("ask-ai.default-message-conversation-title"),
|
|
||||||
messageStorageId: "message-storage",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useConversationStore = create<ConversationState>()(
|
|
||||||
persist(
|
|
||||||
(set, get) => ({
|
|
||||||
conversationList: [],
|
|
||||||
getState: () => get(),
|
|
||||||
addConversation: (conversation: Conversation) => set((state) => ({ conversationList: [...state.conversationList, conversation] })),
|
|
||||||
removeConversation: (conversation: Conversation) => {
|
|
||||||
set((state) => ({
|
|
||||||
conversationList: state.conversationList.filter(
|
|
||||||
(i) => i.name != conversation.name || i.messageStorageId != conversation.messageStorageId
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
localStorage.removeItem(conversation.messageStorageId);
|
|
||||||
const conversationList = get().conversationList;
|
|
||||||
return conversationList.length > 0 ? conversationList[conversationList.length - 1] : defaultConversation;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: "message-conversation-storage",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
@ -1,37 +0,0 @@
|
|||||||
import { create } from "zustand";
|
|
||||||
import { persist } from "zustand/middleware";
|
|
||||||
|
|
||||||
export interface Message {
|
|
||||||
id: string;
|
|
||||||
role: "user" | "assistant";
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MessageState {
|
|
||||||
messageList: Message[];
|
|
||||||
getState: () => MessageState;
|
|
||||||
addMessage: (message: Message) => void;
|
|
||||||
updateMessage: (id: string, appendContent: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useMessageStore = (messageStorageId: string) => {
|
|
||||||
return create<MessageState>()(
|
|
||||||
persist(
|
|
||||||
(set, get) => ({
|
|
||||||
messageList: [] as Message[],
|
|
||||||
getState: () => get(),
|
|
||||||
addMessage: (message: Message) => {
|
|
||||||
return set((state) => ({ messageList: [...state.messageList, message] }));
|
|
||||||
},
|
|
||||||
updateMessage: (id: string, appendContent: string) =>
|
|
||||||
set((state) => ({
|
|
||||||
...state,
|
|
||||||
messageList: state.messageList.map((item) => (item.id === id ? { ...item, content: item.content + appendContent } : item)),
|
|
||||||
})),
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: messageStorageId,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user