fix: historical chats appear in the new user's chat box #256 (#282)

### What problem does this PR solve?

historical chats appear in the new user's chat box

Issue link:#256

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
balibabu 2024-04-09 19:01:57 +08:00 committed by GitHub
parent 653c759d8d
commit 28d29949c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 251 additions and 162 deletions

166
web/src/hooks/chatHooks.ts Normal file
View File

@ -0,0 +1,166 @@
import { IConversation, IDialog } from '@/interfaces/database/chat';
import { useCallback } from 'react';
import { useDispatch, useSelector } from 'umi';
export const useFetchDialogList = () => {
const dispatch = useDispatch();
const fetchDialogList = useCallback(() => {
return dispatch<any>({ type: 'chatModel/listDialog' });
}, [dispatch]);
return fetchDialogList;
};
export const useSelectDialogList = () => {
const dialogList: IDialog[] = useSelector(
(state: any) => state.chatModel.dialogList,
);
return dialogList;
};
export const useFetchConversationList = () => {
const dispatch = useDispatch();
const fetchConversationList = useCallback(
async (dialogId: string) => {
if (dialogId) {
dispatch({
type: 'chatModel/listConversation',
payload: { dialog_id: dialogId },
});
}
},
[dispatch],
);
return fetchConversationList;
};
export const useSelectConversationList = () => {
const conversationList: IConversation[] = useSelector(
(state: any) => state.chatModel.conversationList,
);
return conversationList;
};
export const useFetchConversation = () => {
const dispatch = useDispatch();
const fetchConversation = useCallback(
(conversationId: string, needToBeSaved = true) => {
return dispatch<any>({
type: 'chatModel/getConversation',
payload: {
needToBeSaved,
conversation_id: conversationId,
},
});
},
[dispatch],
);
return fetchConversation;
};
export const useFetchDialog = () => {
const dispatch = useDispatch();
const fetchDialog = useCallback(
(dialogId: string, needToBeSaved = true) => {
if (dialogId) {
return dispatch<any>({
type: 'chatModel/getDialog',
payload: { dialog_id: dialogId, needToBeSaved },
});
}
},
[dispatch],
);
return fetchDialog;
};
export const useRemoveDialog = () => {
const dispatch = useDispatch();
const removeDocument = useCallback(
(dialogIds: Array<string>) => {
return dispatch({
type: 'chatModel/removeDialog',
payload: {
dialog_ids: dialogIds,
},
});
},
[dispatch],
);
return removeDocument;
};
export const useUpdateConversation = () => {
const dispatch = useDispatch();
const updateConversation = useCallback(
(payload: any) => {
return dispatch<any>({
type: 'chatModel/setConversation',
payload,
});
},
[dispatch],
);
return updateConversation;
};
export const useSetDialog = () => {
const dispatch = useDispatch();
const setDialog = useCallback(
(payload: IDialog) => {
return dispatch<any>({ type: 'chatModel/setDialog', payload });
},
[dispatch],
);
return setDialog;
};
export const useRemoveConversation = () => {
const dispatch = useDispatch();
const removeConversation = useCallback(
(conversationIds: Array<string>, dialogId: string) => {
return dispatch<any>({
type: 'chatModel/removeConversation',
payload: {
dialog_id: dialogId,
conversation_ids: conversationIds,
},
});
},
[dispatch],
);
return removeConversation;
};
export const useCompleteConversation = () => {
const dispatch = useDispatch();
const completeConversation = useCallback(
(payload: any) => {
return dispatch<any>({
type: 'chatModel/completeConversation',
payload,
});
},
[dispatch],
);
return completeConversation;
};

View File

@ -269,6 +269,7 @@ export default {
chatConfigurationDescription: chatConfigurationDescription:
' Here, dress up a dedicated assistant for your special knowledge bases! 💕', ' Here, dress up a dedicated assistant for your special knowledge bases! 💕',
assistantName: 'Assistant name', assistantName: 'Assistant name',
assistantNameMessage: 'Assistant name is required',
namePlaceholder: 'e.g. Resume Jarvis', namePlaceholder: 'e.g. Resume Jarvis',
assistantAvatar: 'Assistant avatar', assistantAvatar: 'Assistant avatar',
language: 'Language', language: 'Language',

View File

@ -260,6 +260,7 @@ export default {
chatConfiguration: '聊天配置', chatConfiguration: '聊天配置',
chatConfigurationDescription: '在这里,为你的专业知识库装扮专属助手! 💕', chatConfigurationDescription: '在这里,为你的专业知识库装扮专属助手! 💕',
assistantName: '助理姓名', assistantName: '助理姓名',
assistantNameMessage: '助理姓名是必填项',
namePlaceholder: '例如 贾维斯简历', namePlaceholder: '例如 贾维斯简历',
assistantAvatar: '助理头像', assistantAvatar: '助理头像',
language: '语言', language: '语言',

View File

@ -31,9 +31,9 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
<Form.Item <Form.Item
name={'name'} name={'name'}
label={t('assistantName')} label={t('assistantName')}
rules={[{ required: true }]} rules={[{ required: true, message: t('assistantNameMessage') }]}
> >
<Input placeholder="e.g. Resume Jarvis" /> <Input placeholder={t('namePlaceholder')} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="icon" name="icon"
@ -90,7 +90,7 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
rules={[ rules={[
{ {
required: true, required: true,
message: 'Please select!', message: t('knowledgeBasesMessage'),
type: 'array', type: 'array',
}, },
]} ]}

View File

@ -1,5 +1,18 @@
import { MessageType } from '@/constants/chat'; import { MessageType } from '@/constants/chat';
import { fileIconMap } from '@/constants/common'; import { fileIconMap } from '@/constants/common';
import {
useCompleteConversation,
useFetchConversation,
useFetchConversationList,
useFetchDialog,
useFetchDialogList,
useRemoveConversation,
useRemoveDialog,
useSelectConversationList,
useSelectDialogList,
useSetDialog,
useUpdateConversation,
} from '@/hooks/chatHooks';
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks'; import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks';
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
import { IConversation, IDialog } from '@/interfaces/database/chat'; import { IConversation, IDialog } from '@/interfaces/database/chat';
@ -25,32 +38,6 @@ import {
import { ChatModelState } from './model'; import { ChatModelState } from './model';
import { isConversationIdExist } from './utils'; import { isConversationIdExist } from './utils';
export const useFetchDialogList = () => {
const dispatch = useDispatch();
const dialogList: IDialog[] = useSelector(
(state: any) => state.chatModel.dialogList,
);
useEffect(() => {
dispatch({ type: 'chatModel/listDialog' });
}, [dispatch]);
return dialogList;
};
export const useSetDialog = () => {
const dispatch = useDispatch();
const setDialog = useCallback(
(payload: IDialog) => {
return dispatch<any>({ type: 'chatModel/setDialog', payload });
},
[dispatch],
);
return setDialog;
};
export const useSelectCurrentDialog = () => { export const useSelectCurrentDialog = () => {
const currentDialog: IDialog = useSelector( const currentDialog: IDialog = useSelector(
(state: any) => state.chatModel.currentDialog, (state: any) => state.chatModel.currentDialog,
@ -59,24 +46,6 @@ export const useSelectCurrentDialog = () => {
return currentDialog; return currentDialog;
}; };
export const useFetchDialog = () => {
const dispatch = useDispatch();
const fetchDialog = useCallback(
(dialogId: string, needToBeSaved = true) => {
if (dialogId) {
return dispatch<any>({
type: 'chatModel/getDialog',
payload: { dialog_id: dialogId, needToBeSaved },
});
}
},
[dispatch],
);
return fetchDialog;
};
export const useFetchDialogOnMount = ( export const useFetchDialogOnMount = (
dialogId: string, dialogId: string,
visible: boolean, visible: boolean,
@ -147,21 +116,13 @@ export const useSelectPromptConfigParameters = (): VariableTableDataType[] => {
return finalParameters; return finalParameters;
}; };
export const useRemoveDialog = () => { export const useDeleteDialog = () => {
const dispatch = useDispatch();
const showDeleteConfirm = useShowDeleteConfirm(); const showDeleteConfirm = useShowDeleteConfirm();
const removeDocument = (dialogIds: Array<string>) => () => { const removeDocument = useRemoveDialog();
return dispatch({
type: 'chatModel/removeDialog',
payload: {
dialog_ids: dialogIds,
},
});
};
const onRemoveDialog = (dialogIds: Array<string>) => { const onRemoveDialog = (dialogIds: Array<string>) => {
showDeleteConfirm({ onOk: removeDocument(dialogIds) }); showDeleteConfirm({ onOk: () => removeDocument(dialogIds) });
}; };
return { onRemoveDialog }; return { onRemoveDialog };
@ -216,16 +177,21 @@ export const useClickDialogCard = () => {
}; };
export const useSelectFirstDialogOnMount = () => { export const useSelectFirstDialogOnMount = () => {
const dialogList = useFetchDialogList(); const fetchDialogList = useFetchDialogList();
const { dialogId } = useGetChatSearchParams(); const dialogList = useSelectDialogList();
const { handleClickDialog } = useClickDialogCard(); const { handleClickDialog } = useClickDialogCard();
useEffect(() => { const fetchList = useCallback(async () => {
if (dialogList.length > 0 && !dialogId) { const data = await fetchDialogList();
handleClickDialog(dialogList[0].id); if (data.retcode === 0 && data.data.length > 0) {
handleClickDialog(data.data[0].id);
} }
}, [dialogList, handleClickDialog, dialogId]); }, [fetchDialogList, handleClickDialog]);
useEffect(() => {
fetchList();
}, [fetchList]);
return dialogList; return dialogList;
}; };
@ -301,30 +267,19 @@ export const useEditDialog = () => {
//#region conversation //#region conversation
export const useFetchConversationList = () => { export const useFetchConversationListOnMount = () => {
const dispatch = useDispatch(); const conversationList = useSelectConversationList();
const conversationList: any[] = useSelector(
(state: any) => state.chatModel.conversationList,
);
const { dialogId } = useGetChatSearchParams(); const { dialogId } = useGetChatSearchParams();
const fetchConversationList = useFetchConversationList();
const fetchConversationList = useCallback(async () => {
if (dialogId) {
dispatch({
type: 'chatModel/listConversation',
payload: { dialog_id: dialogId },
});
}
}, [dispatch, dialogId]);
useEffect(() => { useEffect(() => {
fetchConversationList(); fetchConversationList(dialogId);
}, [fetchConversationList]); }, [fetchConversationList, dialogId]);
return conversationList; return conversationList;
}; };
export const useSelectConversationList = () => { export const useSelectDerivedConversationList = () => {
const [list, setList] = useState<Array<IConversation>>([]); const [list, setList] = useState<Array<IConversation>>([]);
let chatModel: ChatModelState = useSelector((state: any) => state.chatModel); let chatModel: ChatModelState = useSelector((state: any) => state.chatModel);
const { conversationList, currentDialog } = chatModel; const { conversationList, currentDialog } = chatModel;
@ -381,27 +336,23 @@ export const useClickConversationCard = () => {
}; };
export const useSetConversation = () => { export const useSetConversation = () => {
const dispatch = useDispatch();
const { dialogId } = useGetChatSearchParams(); const { dialogId } = useGetChatSearchParams();
const updateConversation = useUpdateConversation();
const setConversation = useCallback( const setConversation = useCallback(
(message: string) => { (message: string) => {
return dispatch<any>({ return updateConversation({
type: 'chatModel/setConversation', dialog_id: dialogId,
payload: { name: message,
// conversation_id: '', message: [
dialog_id: dialogId, {
name: message, role: MessageType.Assistant,
message: [ content: message,
{ },
role: MessageType.Assistant, ],
content: message,
},
],
},
}); });
}, },
[dispatch, dialogId], [updateConversation, dialogId],
); );
return { setConversation }; return { setConversation };
@ -473,31 +424,14 @@ export const useSelectCurrentConversation = () => {
}, [addPrologue]); }, [addPrologue]);
useEffect(() => { useEffect(() => {
setCurrentConversation(conversation); if (conversationId) {
}, [conversation]); setCurrentConversation(conversation);
}
}, [conversation, conversationId]);
return { currentConversation, addNewestConversation, removeLatestMessage }; return { currentConversation, addNewestConversation, removeLatestMessage };
}; };
export const useFetchConversation = () => {
const dispatch = useDispatch();
const fetchConversation = useCallback(
(conversationId: string, needToBeSaved = true) => {
return dispatch<any>({
type: 'chatModel/getConversation',
payload: {
needToBeSaved,
conversation_id: conversationId,
},
});
},
[dispatch],
);
return fetchConversation;
};
export const useScrollToBottom = (currentConversation: IClientConversation) => { export const useScrollToBottom = (currentConversation: IClientConversation) => {
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
@ -564,33 +498,26 @@ export const useSendMessage = (
const loading = useOneNamespaceEffectsLoading('chatModel', [ const loading = useOneNamespaceEffectsLoading('chatModel', [
'completeConversation', 'completeConversation',
]); ]);
const dispatch = useDispatch();
const { setConversation } = useSetConversation(); const { setConversation } = useSetConversation();
const { conversationId } = useGetChatSearchParams(); const { conversationId } = useGetChatSearchParams();
const { handleInputChange, value, setValue } = useHandleMessageInputChange(); const { handleInputChange, value, setValue } = useHandleMessageInputChange();
// const conversation: IClientConversation = useSelector(
// (state: any) => state.chatModel.currentConversation,
// );
const fetchConversation = useFetchConversation(); const fetchConversation = useFetchConversation();
const completeConversation = useCompleteConversation();
const { handleClickConversation } = useClickConversationCard(); const { handleClickConversation } = useClickConversationCard();
const sendMessage = useCallback( const sendMessage = useCallback(
async (message: string, id?: string) => { async (message: string, id?: string) => {
const retcode = await dispatch<any>({ const retcode = await completeConversation({
type: 'chatModel/completeConversation', conversation_id: id ?? conversationId,
payload: { messages: [
conversation_id: id ?? conversationId, ...(conversation?.message ?? []).map((x: IMessage) => omit(x, 'id')),
messages: [ {
...(conversation?.message ?? []).map((x: IMessage) => role: MessageType.User,
omit(x, 'id'), content: message,
), },
{ ],
role: MessageType.User,
content: message,
},
],
},
}); });
if (retcode === 0) { if (retcode === 0) {
@ -607,13 +534,13 @@ export const useSendMessage = (
} }
}, },
[ [
dispatch,
conversation?.message, conversation?.message,
conversationId, conversationId,
fetchConversation, fetchConversation,
handleClickConversation, handleClickConversation,
removeLatestMessage, removeLatestMessage,
setValue, setValue,
completeConversation,
], ],
); );
@ -664,37 +591,28 @@ export const useGetFileIcon = () => {
return getFileIcon; return getFileIcon;
}; };
export const useRemoveConversation = () => { export const useDeleteConversation = () => {
const dispatch = useDispatch();
const { dialogId } = useGetChatSearchParams(); const { dialogId } = useGetChatSearchParams();
const { handleClickConversation } = useClickConversationCard(); const { handleClickConversation } = useClickConversationCard();
const showDeleteConfirm = useShowDeleteConfirm(); const showDeleteConfirm = useShowDeleteConfirm();
const removeConversation = useRemoveConversation();
const removeConversation = (conversationIds: Array<string>) => async () => { const deleteConversation = (conversationIds: Array<string>) => async () => {
const ret = await dispatch<any>({ const ret = await removeConversation(conversationIds, dialogId);
type: 'chatModel/removeConversation',
payload: {
dialog_id: dialogId,
conversation_ids: conversationIds,
},
});
if (ret === 0) { if (ret === 0) {
handleClickConversation(''); handleClickConversation('');
} }
return ret; return ret;
}; };
const onRemoveConversation = (conversationIds: Array<string>) => { const onRemoveConversation = (conversationIds: Array<string>) => {
showDeleteConfirm({ onOk: removeConversation(conversationIds) }); showDeleteConfirm({ onOk: deleteConversation(conversationIds) });
}; };
return { onRemoveConversation }; return { onRemoveConversation };
}; };
export const useRenameConversation = () => { export const useRenameConversation = () => {
const dispatch = useDispatch();
const [conversation, setConversation] = useState<IClientConversation>( const [conversation, setConversation] = useState<IClientConversation>(
{} as IClientConversation, {} as IClientConversation,
); );
@ -704,19 +622,21 @@ export const useRenameConversation = () => {
hideModal: hideConversationRenameModal, hideModal: hideConversationRenameModal,
showModal: showConversationRenameModal, showModal: showConversationRenameModal,
} = useSetModalState(); } = useSetModalState();
const updateConversation = useUpdateConversation();
const onConversationRenameOk = useCallback( const onConversationRenameOk = useCallback(
async (name: string) => { async (name: string) => {
const ret = await dispatch<any>({ const ret = await updateConversation({
type: 'chatModel/setConversation', ...conversation,
payload: { ...conversation, conversation_id: conversation.id, name }, conversation_id: conversation.id,
name,
}); });
if (ret.retcode === 0) { if (ret.retcode === 0) {
hideConversationRenameModal(); hideConversationRenameModal();
} }
}, },
[dispatch, conversation, hideConversationRenameModal], [updateConversation, conversation, hideConversationRenameModal],
); );
const loading = useOneNamespaceEffectsLoading('chatModel', [ const loading = useOneNamespaceEffectsLoading('chatModel', [

View File

@ -21,16 +21,16 @@ import ChatContainer from './chat-container';
import { import {
useClickConversationCard, useClickConversationCard,
useClickDialogCard, useClickDialogCard,
useDeleteConversation,
useDeleteDialog,
useEditDialog, useEditDialog,
useFetchConversationList, useFetchConversationListOnMount,
useFetchDialogOnMount, useFetchDialogOnMount,
useGetChatSearchParams, useGetChatSearchParams,
useHandleItemHover, useHandleItemHover,
useRemoveConversation,
useRemoveDialog,
useRenameConversation, useRenameConversation,
useSelectConversationList,
useSelectConversationListLoading, useSelectConversationListLoading,
useSelectDerivedConversationList,
useSelectDialogListLoading, useSelectDialogListLoading,
useSelectFirstDialogOnMount, useSelectFirstDialogOnMount,
} from './hooks'; } from './hooks';
@ -40,13 +40,13 @@ import styles from './index.less';
const Chat = () => { const Chat = () => {
const dialogList = useSelectFirstDialogOnMount(); const dialogList = useSelectFirstDialogOnMount();
const { onRemoveDialog } = useRemoveDialog(); const { onRemoveDialog } = useDeleteDialog();
const { onRemoveConversation } = useRemoveConversation(); const { onRemoveConversation } = useDeleteConversation();
const { handleClickDialog } = useClickDialogCard(); const { handleClickDialog } = useClickDialogCard();
const { handleClickConversation } = useClickConversationCard(); const { handleClickConversation } = useClickConversationCard();
const { dialogId, conversationId } = useGetChatSearchParams(); const { dialogId, conversationId } = useGetChatSearchParams();
const { list: conversationList, addTemporaryConversation } = const { list: conversationList, addTemporaryConversation } =
useSelectConversationList(); useSelectDerivedConversationList();
const { activated, handleItemEnter, handleItemLeave } = useHandleItemHover(); const { activated, handleItemEnter, handleItemLeave } = useHandleItemHover();
const { const {
activated: conversationActivated, activated: conversationActivated,
@ -197,7 +197,7 @@ const Chat = () => {
return appItems; return appItems;
}; };
useFetchConversationList(); useFetchConversationListOnMount();
return ( return (
<Flex className={styles.chatWrapper}> <Flex className={styles.chatWrapper}>

View File

@ -97,6 +97,7 @@ const model: DvaModel<ChatModelState> = {
if (data.retcode === 0) { if (data.retcode === 0) {
yield put({ type: 'setDialogList', payload: data.data }); yield put({ type: 'setDialogList', payload: data.data });
} }
return data;
}, },
*listConversation({ payload }, { call, put }) { *listConversation({ payload }, { call, put }) {
const { data } = yield call(chatService.listConversation, payload); const { data } = yield call(chatService.listConversation, payload);