feat: chat ui

This commit is contained in:
archer
2023-07-26 11:01:25 +08:00
parent 2b993b926a
commit 248be38939
19 changed files with 153 additions and 54 deletions

View File

@@ -3,18 +3,31 @@ import { jsonRes } from '@/service/response';
import { connectToDatabase, Chat } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
/* 获取历史记录 */
type Props = {
chatId?: string;
appId?: string;
};
/* clear chat history */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { chatId } = req.query;
const { chatId, appId } = req.query as Props;
const { userId } = await authUser({ req, authToken: true });
await connectToDatabase();
await Chat.findOneAndRemove({
chatId,
userId
});
if (chatId) {
await Chat.findOneAndRemove({
chatId,
userId
});
}
if (appId) {
await Chat.deleteMany({
appId,
userId
});
}
jsonRes(res);
} catch (err) {

View File

@@ -96,7 +96,7 @@ const ChatTest = (
<IconButton
className="chat"
size={'sm'}
icon={<MyIcon name={'clearLight'} w={'14px'} />}
icon={<MyIcon name={'clear'} w={'14px'} />}
variant={'base'}
borderRadius={'md'}
aria-label={'delete'}

View File

@@ -596,7 +596,7 @@ const ChatTest = ({ appId }: { appId: string }) => {
<IconButton
className="chat"
size={'sm'}
icon={<MyIcon name={'clearLight'} w={'14px'} />}
icon={<MyIcon name={'clear'} w={'14px'} />}
variant={'base'}
borderRadius={'md'}
aria-label={'delete'}

View File

@@ -33,7 +33,7 @@ const MyApps = () => {
const { t } = useTranslation();
const theme = useTheme();
const router = useRouter();
const { myApps, loadMyModels } = useUserStore();
const { myApps, loadMyApps } = useUserStore();
const { openConfirm, ConfirmChild } = useConfirm({
title: '删除提示',
content: '确认删除该应用所有信息?'
@@ -53,7 +53,7 @@ const MyApps = () => {
title: '删除成功',
status: 'success'
});
loadMyModels();
loadMyApps();
} catch (err: any) {
toast({
title: err?.message || '删除失败',
@@ -61,11 +61,11 @@ const MyApps = () => {
});
}
},
[toast, loadMyModels]
[toast, loadMyApps]
);
/* 加载模型 */
useQuery(['loadModels'], loadMyModels, {
useQuery(['loadModels'], loadMyApps, {
refetchOnMount: true
});
@@ -166,7 +166,7 @@ const MyApps = () => {
))}
</Grid>
<ConfirmChild />
{isOpenCreateModal && <CreateModal onClose={onCloseCreateModal} onSuccess={loadMyModels} />}
{isOpenCreateModal && <CreateModal onClose={onCloseCreateModal} onSuccess={loadMyApps} />}
</PageContainer>
);
};

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react';
import React, { useMemo } from 'react';
import {
Box,
Button,
@@ -16,6 +16,8 @@ import { useRouter } from 'next/router';
import Avatar from '@/components/Avatar';
import MyTooltip from '@/components/MyTooltip';
import MyIcon from '@/components/Icon';
import { useTranslation } from 'react-i18next';
import { useConfirm } from '@/hooks/useConfirm';
type HistoryItemType = {
id: string;
@@ -32,6 +34,7 @@ const ChatHistorySlider = ({
activeChatId,
onChangeChat,
onDelHistory,
onClearHistory,
onSetHistoryTop,
onSetCustomTitle
}: {
@@ -42,17 +45,22 @@ const ChatHistorySlider = ({
activeChatId: string;
onChangeChat: (chatId?: string) => void;
onDelHistory: (chatId: string) => void;
onClearHistory: () => void;
onSetHistoryTop?: (e: { chatId: string; top: boolean }) => void;
onSetCustomTitle?: (e: { chatId: string; title: string }) => void;
}) => {
const theme = useTheme();
const router = useRouter();
const { t } = useTranslation();
const { isPc } = useGlobalStore();
// custom title edit
const { onOpenModal, EditModal: EditTitleModal } = useEditInfo({
title: '自定义历史记录标题',
placeholder: '如果设置为空,会自动跟随聊天记录。'
});
const { openConfirm, ConfirmChild } = useConfirm({
content: t('chat.Confirm to clear history')
});
const concatHistory = useMemo<HistoryItemType[]>(
() => (!activeChatId ? [{ id: activeChatId, title: '新对话' }].concat(history) : history),
@@ -70,7 +78,7 @@ const ChatHistorySlider = ({
whiteSpace={'nowrap'}
>
{isPc && (
<MyTooltip label={appId ? '应用详情' : ''} offset={[0, 0]}>
<MyTooltip label={appId ? t('app.App Detail') : ''} offset={[0, 0]}>
<Flex
pt={5}
pb={2}
@@ -92,11 +100,11 @@ const ChatHistorySlider = ({
</Flex>
</MyTooltip>
)}
{/* 新对话 */}
<Box w={'100%'} px={[2, 5]} h={'36px'} my={5}>
{/* btn */}
<Flex w={'100%'} px={[2, 5]} h={'36px'} my={5}>
<Button
variant={'base'}
w={'100%'}
flex={1}
h={'100%'}
color={'myBlue.700'}
borderRadius={'xl'}
@@ -104,9 +112,20 @@ const ChatHistorySlider = ({
overflow={'hidden'}
onClick={() => onChangeChat()}
>
{t('chat.New Chat')}
</Button>
</Box>
<IconButton
ml={3}
h={'100%'}
variant={'base'}
aria-label={''}
borderRadius={'xl'}
onClick={openConfirm(onClearHistory)}
>
<MyIcon name={'clear'} w={'16px'} />
</IconButton>
</Flex>
{/* chat history */}
<Box flex={'1 0 0'} h={0} px={[2, 5]} overflow={'overlay'}>
@@ -230,6 +249,7 @@ const ChatHistorySlider = ({
</Flex>
)}
<EditTitleModal />
<ConfirmChild />
</Flex>
);
};

View File

@@ -8,9 +8,9 @@ import Avatar from '@/components/Avatar';
const SliderApps = ({ appId }: { appId: string }) => {
const router = useRouter();
const { myApps, loadMyModels } = useUserStore();
const { myApps, loadMyApps } = useUserStore();
useQuery(['loadModels'], loadMyModels);
useQuery(['loadModels'], loadMyApps);
return (
<>

View File

@@ -19,6 +19,7 @@ import { useToast } from '@/hooks/useToast';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
import type { ChatHistoryItemType } from '@/types/chat';
import { useTranslation } from 'react-i18next';
import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox';
import PageContainer from '@/components/PageContainer';
@@ -33,6 +34,7 @@ import { serviceSideProps } from '@/utils/i18n';
const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
const router = useRouter();
const theme = useTheme();
const { t } = useTranslation();
const { toast } = useToast();
const ChatBoxRef = useRef<ComponentRef>(null);
@@ -47,10 +49,11 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
loadHistory,
updateHistory,
delHistory,
clearHistory,
chatData,
setChatData
} = useChatStore();
const { myApps, userInfo } = useUserStore();
const { myApps, loadMyApps, userInfo } = useUserStore();
const { isPc } = useGlobalStore();
const { Loading, setIsLoading } = useLoading();
@@ -200,6 +203,26 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
}
});
}
if (!appId) {
(async () => {
const apps = await loadMyApps();
if (apps.length === 0) {
toast({
status: 'error',
title: t('chat.You need to a chat app')
});
router.replace('/app/list');
} else {
router.replace({
query: {
appId: apps[0]._id,
chatId: lastChatId
}
});
}
})();
return;
}
// store id
appId && setLastChatAppId(appId);
@@ -210,15 +233,11 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
return null;
}
if (appId) {
return loadChatInfo({
appId,
chatId,
loading: appId !== chatData.appId
});
}
return null;
return loadChatInfo({
appId,
chatId,
loading: appId !== chatData.appId
});
});
useQuery(['loadHistory', appId], () => (appId ? loadHistory({ appId }) : null));
@@ -268,6 +287,14 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
}
}}
onDelHistory={delHistory}
onClearHistory={() => {
clearHistory(appId);
router.replace({
query: {
appId
}
});
}}
onSetHistoryTop={async (e) => {
try {
await putChatHistory(e);

View File

@@ -99,8 +99,6 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) =>
const loadAppInfo = useCallback(
async (shareId: string, chatId: string) => {
console.log(shareId, chatId);
if (!shareId) return null;
const history = shareChatHistory.find((item) => item.chatId === chatId) || defaultHistory;
@@ -183,6 +181,14 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) =>
}
}}
onDelHistory={delOneShareHistoryByChatId}
onClearHistory={() => {
delManyShareChatHistoryByShareId(shareId);
router.replace({
query: {
shareId
}
});
}}
/>
)}