diff --git a/web/src/hooks/chatHooks.ts b/web/src/hooks/chatHooks.ts new file mode 100644 index 000000000..7bf9a2b9f --- /dev/null +++ b/web/src/hooks/chatHooks.ts @@ -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({ 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({ + 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({ + type: 'chatModel/getDialog', + payload: { dialog_id: dialogId, needToBeSaved }, + }); + } + }, + [dispatch], + ); + + return fetchDialog; +}; + +export const useRemoveDialog = () => { + const dispatch = useDispatch(); + + const removeDocument = useCallback( + (dialogIds: Array) => { + 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({ + type: 'chatModel/setConversation', + payload, + }); + }, + [dispatch], + ); + + return updateConversation; +}; + +export const useSetDialog = () => { + const dispatch = useDispatch(); + + const setDialog = useCallback( + (payload: IDialog) => { + return dispatch({ type: 'chatModel/setDialog', payload }); + }, + [dispatch], + ); + + return setDialog; +}; + +export const useRemoveConversation = () => { + const dispatch = useDispatch(); + + const removeConversation = useCallback( + (conversationIds: Array, dialogId: string) => { + return dispatch({ + 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({ + type: 'chatModel/completeConversation', + payload, + }); + }, + [dispatch], + ); + + return completeConversation; +}; diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index b34790c82..29c91160d 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -269,6 +269,7 @@ export default { chatConfigurationDescription: ' Here, dress up a dedicated assistant for your special knowledge bases! 💕', assistantName: 'Assistant name', + assistantNameMessage: 'Assistant name is required', namePlaceholder: 'e.g. Resume Jarvis', assistantAvatar: 'Assistant avatar', language: 'Language', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 803293063..af162fe9f 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -260,6 +260,7 @@ export default { chatConfiguration: '聊天配置', chatConfigurationDescription: '在这里,为你的专业知识库装扮专属助手! 💕', assistantName: '助理姓名', + assistantNameMessage: '助理姓名是必填项', namePlaceholder: '例如 贾维斯简历', assistantAvatar: '助理头像', language: '语言', diff --git a/web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx b/web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx index 4dfdfe6cb..d41af2697 100644 --- a/web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx +++ b/web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx @@ -31,9 +31,9 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => { - + { rules={[ { required: true, - message: 'Please select!', + message: t('knowledgeBasesMessage'), type: 'array', }, ]} diff --git a/web/src/pages/chat/hooks.ts b/web/src/pages/chat/hooks.ts index c81006e1d..b4cf9ca20 100644 --- a/web/src/pages/chat/hooks.ts +++ b/web/src/pages/chat/hooks.ts @@ -1,5 +1,18 @@ import { MessageType } from '@/constants/chat'; 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 { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; import { IConversation, IDialog } from '@/interfaces/database/chat'; @@ -25,32 +38,6 @@ import { import { ChatModelState } from './model'; 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({ type: 'chatModel/setDialog', payload }); - }, - [dispatch], - ); - - return setDialog; -}; - export const useSelectCurrentDialog = () => { const currentDialog: IDialog = useSelector( (state: any) => state.chatModel.currentDialog, @@ -59,24 +46,6 @@ export const useSelectCurrentDialog = () => { return currentDialog; }; -export const useFetchDialog = () => { - const dispatch = useDispatch(); - - const fetchDialog = useCallback( - (dialogId: string, needToBeSaved = true) => { - if (dialogId) { - return dispatch({ - type: 'chatModel/getDialog', - payload: { dialog_id: dialogId, needToBeSaved }, - }); - } - }, - [dispatch], - ); - - return fetchDialog; -}; - export const useFetchDialogOnMount = ( dialogId: string, visible: boolean, @@ -147,21 +116,13 @@ export const useSelectPromptConfigParameters = (): VariableTableDataType[] => { return finalParameters; }; -export const useRemoveDialog = () => { - const dispatch = useDispatch(); +export const useDeleteDialog = () => { const showDeleteConfirm = useShowDeleteConfirm(); - const removeDocument = (dialogIds: Array) => () => { - return dispatch({ - type: 'chatModel/removeDialog', - payload: { - dialog_ids: dialogIds, - }, - }); - }; + const removeDocument = useRemoveDialog(); const onRemoveDialog = (dialogIds: Array) => { - showDeleteConfirm({ onOk: removeDocument(dialogIds) }); + showDeleteConfirm({ onOk: () => removeDocument(dialogIds) }); }; return { onRemoveDialog }; @@ -216,16 +177,21 @@ export const useClickDialogCard = () => { }; export const useSelectFirstDialogOnMount = () => { - const dialogList = useFetchDialogList(); - const { dialogId } = useGetChatSearchParams(); + const fetchDialogList = useFetchDialogList(); + const dialogList = useSelectDialogList(); const { handleClickDialog } = useClickDialogCard(); - useEffect(() => { - if (dialogList.length > 0 && !dialogId) { - handleClickDialog(dialogList[0].id); + const fetchList = useCallback(async () => { + const data = await fetchDialogList(); + if (data.retcode === 0 && data.data.length > 0) { + handleClickDialog(data.data[0].id); } - }, [dialogList, handleClickDialog, dialogId]); + }, [fetchDialogList, handleClickDialog]); + + useEffect(() => { + fetchList(); + }, [fetchList]); return dialogList; }; @@ -301,30 +267,19 @@ export const useEditDialog = () => { //#region conversation -export const useFetchConversationList = () => { - const dispatch = useDispatch(); - const conversationList: any[] = useSelector( - (state: any) => state.chatModel.conversationList, - ); +export const useFetchConversationListOnMount = () => { + const conversationList = useSelectConversationList(); const { dialogId } = useGetChatSearchParams(); - - const fetchConversationList = useCallback(async () => { - if (dialogId) { - dispatch({ - type: 'chatModel/listConversation', - payload: { dialog_id: dialogId }, - }); - } - }, [dispatch, dialogId]); + const fetchConversationList = useFetchConversationList(); useEffect(() => { - fetchConversationList(); - }, [fetchConversationList]); + fetchConversationList(dialogId); + }, [fetchConversationList, dialogId]); return conversationList; }; -export const useSelectConversationList = () => { +export const useSelectDerivedConversationList = () => { const [list, setList] = useState>([]); let chatModel: ChatModelState = useSelector((state: any) => state.chatModel); const { conversationList, currentDialog } = chatModel; @@ -381,27 +336,23 @@ export const useClickConversationCard = () => { }; export const useSetConversation = () => { - const dispatch = useDispatch(); const { dialogId } = useGetChatSearchParams(); + const updateConversation = useUpdateConversation(); const setConversation = useCallback( (message: string) => { - return dispatch({ - type: 'chatModel/setConversation', - payload: { - // conversation_id: '', - dialog_id: dialogId, - name: message, - message: [ - { - role: MessageType.Assistant, - content: message, - }, - ], - }, + return updateConversation({ + dialog_id: dialogId, + name: message, + message: [ + { + role: MessageType.Assistant, + content: message, + }, + ], }); }, - [dispatch, dialogId], + [updateConversation, dialogId], ); return { setConversation }; @@ -473,31 +424,14 @@ export const useSelectCurrentConversation = () => { }, [addPrologue]); useEffect(() => { - setCurrentConversation(conversation); - }, [conversation]); + if (conversationId) { + setCurrentConversation(conversation); + } + }, [conversation, conversationId]); return { currentConversation, addNewestConversation, removeLatestMessage }; }; -export const useFetchConversation = () => { - const dispatch = useDispatch(); - - const fetchConversation = useCallback( - (conversationId: string, needToBeSaved = true) => { - return dispatch({ - type: 'chatModel/getConversation', - payload: { - needToBeSaved, - conversation_id: conversationId, - }, - }); - }, - [dispatch], - ); - - return fetchConversation; -}; - export const useScrollToBottom = (currentConversation: IClientConversation) => { const ref = useRef(null); @@ -564,33 +498,26 @@ export const useSendMessage = ( const loading = useOneNamespaceEffectsLoading('chatModel', [ 'completeConversation', ]); - const dispatch = useDispatch(); const { setConversation } = useSetConversation(); const { conversationId } = useGetChatSearchParams(); const { handleInputChange, value, setValue } = useHandleMessageInputChange(); - // const conversation: IClientConversation = useSelector( - // (state: any) => state.chatModel.currentConversation, - // ); + const fetchConversation = useFetchConversation(); + const completeConversation = useCompleteConversation(); const { handleClickConversation } = useClickConversationCard(); const sendMessage = useCallback( async (message: string, id?: string) => { - const retcode = await dispatch({ - type: 'chatModel/completeConversation', - payload: { - conversation_id: id ?? conversationId, - messages: [ - ...(conversation?.message ?? []).map((x: IMessage) => - omit(x, 'id'), - ), - { - role: MessageType.User, - content: message, - }, - ], - }, + const retcode = await completeConversation({ + conversation_id: id ?? conversationId, + messages: [ + ...(conversation?.message ?? []).map((x: IMessage) => omit(x, 'id')), + { + role: MessageType.User, + content: message, + }, + ], }); if (retcode === 0) { @@ -607,13 +534,13 @@ export const useSendMessage = ( } }, [ - dispatch, conversation?.message, conversationId, fetchConversation, handleClickConversation, removeLatestMessage, setValue, + completeConversation, ], ); @@ -664,37 +591,28 @@ export const useGetFileIcon = () => { return getFileIcon; }; -export const useRemoveConversation = () => { - const dispatch = useDispatch(); +export const useDeleteConversation = () => { const { dialogId } = useGetChatSearchParams(); const { handleClickConversation } = useClickConversationCard(); const showDeleteConfirm = useShowDeleteConfirm(); + const removeConversation = useRemoveConversation(); - const removeConversation = (conversationIds: Array) => async () => { - const ret = await dispatch({ - type: 'chatModel/removeConversation', - payload: { - dialog_id: dialogId, - conversation_ids: conversationIds, - }, - }); - + const deleteConversation = (conversationIds: Array) => async () => { + const ret = await removeConversation(conversationIds, dialogId); if (ret === 0) { handleClickConversation(''); } - return ret; }; const onRemoveConversation = (conversationIds: Array) => { - showDeleteConfirm({ onOk: removeConversation(conversationIds) }); + showDeleteConfirm({ onOk: deleteConversation(conversationIds) }); }; return { onRemoveConversation }; }; export const useRenameConversation = () => { - const dispatch = useDispatch(); const [conversation, setConversation] = useState( {} as IClientConversation, ); @@ -704,19 +622,21 @@ export const useRenameConversation = () => { hideModal: hideConversationRenameModal, showModal: showConversationRenameModal, } = useSetModalState(); + const updateConversation = useUpdateConversation(); const onConversationRenameOk = useCallback( async (name: string) => { - const ret = await dispatch({ - type: 'chatModel/setConversation', - payload: { ...conversation, conversation_id: conversation.id, name }, + const ret = await updateConversation({ + ...conversation, + conversation_id: conversation.id, + name, }); if (ret.retcode === 0) { hideConversationRenameModal(); } }, - [dispatch, conversation, hideConversationRenameModal], + [updateConversation, conversation, hideConversationRenameModal], ); const loading = useOneNamespaceEffectsLoading('chatModel', [ diff --git a/web/src/pages/chat/index.tsx b/web/src/pages/chat/index.tsx index 4086471c0..e18d30cd4 100644 --- a/web/src/pages/chat/index.tsx +++ b/web/src/pages/chat/index.tsx @@ -21,16 +21,16 @@ import ChatContainer from './chat-container'; import { useClickConversationCard, useClickDialogCard, + useDeleteConversation, + useDeleteDialog, useEditDialog, - useFetchConversationList, + useFetchConversationListOnMount, useFetchDialogOnMount, useGetChatSearchParams, useHandleItemHover, - useRemoveConversation, - useRemoveDialog, useRenameConversation, - useSelectConversationList, useSelectConversationListLoading, + useSelectDerivedConversationList, useSelectDialogListLoading, useSelectFirstDialogOnMount, } from './hooks'; @@ -40,13 +40,13 @@ import styles from './index.less'; const Chat = () => { const dialogList = useSelectFirstDialogOnMount(); - const { onRemoveDialog } = useRemoveDialog(); - const { onRemoveConversation } = useRemoveConversation(); + const { onRemoveDialog } = useDeleteDialog(); + const { onRemoveConversation } = useDeleteConversation(); const { handleClickDialog } = useClickDialogCard(); const { handleClickConversation } = useClickConversationCard(); const { dialogId, conversationId } = useGetChatSearchParams(); const { list: conversationList, addTemporaryConversation } = - useSelectConversationList(); + useSelectDerivedConversationList(); const { activated, handleItemEnter, handleItemLeave } = useHandleItemHover(); const { activated: conversationActivated, @@ -197,7 +197,7 @@ const Chat = () => { return appItems; }; - useFetchConversationList(); + useFetchConversationListOnMount(); return ( diff --git a/web/src/pages/chat/model.ts b/web/src/pages/chat/model.ts index 1e94be091..51eb71110 100644 --- a/web/src/pages/chat/model.ts +++ b/web/src/pages/chat/model.ts @@ -97,6 +97,7 @@ const model: DvaModel = { if (data.retcode === 0) { yield put({ type: 'setDialogList', payload: data.data }); } + return data; }, *listConversation({ payload }, { call, put }) { const { data } = yield call(chatService.listConversation, payload);