From 4289f17be229aa1541ea43f84ce99547a7066252 Mon Sep 17 00:00:00 2001 From: KVOJJJin Date: Fri, 14 Jun 2024 08:42:41 +0800 Subject: [PATCH] Chore: refactor embedded chatbot (#5125) --- .../(shareLayout)/chatbot/[token]/page.tsx | 4 +- .../app/chat/icon-component/index.tsx | 4 +- web/app/components/base/app-unavailable.tsx | 6 +- .../base/chat/chat-with-history/index.tsx | 18 +- .../chat/embedded-chatbot/chat-wrapper.tsx | 135 ++++++++ .../config-panel/form-input.tsx | 46 +++ .../embedded-chatbot/config-panel/form.tsx | 83 +++++ .../embedded-chatbot/config-panel/index.tsx | 168 ++++++++++ .../base/chat/embedded-chatbot/context.tsx | 64 ++++ .../base/chat/embedded-chatbot/header.tsx | 58 ++++ .../base/chat/embedded-chatbot/hooks.tsx | 293 ++++++++++++++++++ .../base/chat/embedded-chatbot/index.tsx | 181 +++++++++++ .../base/chat/embedded-chatbot/utils.ts | 3 + web/app/components/share/chat/index.tsx | 4 +- web/app/components/share/chatbot/index.tsx | 4 +- 15 files changed, 1051 insertions(+), 20 deletions(-) create mode 100644 web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx create mode 100644 web/app/components/base/chat/embedded-chatbot/config-panel/form-input.tsx create mode 100644 web/app/components/base/chat/embedded-chatbot/config-panel/form.tsx create mode 100644 web/app/components/base/chat/embedded-chatbot/config-panel/index.tsx create mode 100644 web/app/components/base/chat/embedded-chatbot/context.tsx create mode 100644 web/app/components/base/chat/embedded-chatbot/header.tsx create mode 100644 web/app/components/base/chat/embedded-chatbot/hooks.tsx create mode 100644 web/app/components/base/chat/embedded-chatbot/index.tsx create mode 100644 web/app/components/base/chat/embedded-chatbot/utils.ts diff --git a/web/app/(shareLayout)/chatbot/[token]/page.tsx b/web/app/(shareLayout)/chatbot/[token]/page.tsx index 0dc7b07169..b78680c503 100644 --- a/web/app/(shareLayout)/chatbot/[token]/page.tsx +++ b/web/app/(shareLayout)/chatbot/[token]/page.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import React, { useEffect } from 'react' import cn from 'classnames' import type { IMainProps } from '@/app/components/share/chat' -import Main from '@/app/components/share/chatbot' +import EmbeddedChatbot from '@/app/components/base/chat/embedded-chatbot' import Loading from '@/app/components/base/loading' import { fetchSystemFeatures } from '@/service/share' import LogoSite from '@/app/components/base/logo/logo-site' @@ -77,7 +77,7 @@ const Chatbot: FC = () => { ) - :
+ : } )} diff --git a/web/app/components/app/chat/icon-component/index.tsx b/web/app/components/app/chat/icon-component/index.tsx index f5d17a80b1..c35fb77855 100644 --- a/web/app/components/app/chat/icon-component/index.tsx +++ b/web/app/components/app/chat/icon-component/index.tsx @@ -37,7 +37,7 @@ export const TryToAskIcon = ( ) export const ReplayIcon = ({ className }: SVGProps) => ( - - + + ) diff --git a/web/app/components/base/app-unavailable.tsx b/web/app/components/base/app-unavailable.tsx index 57cf9727fc..c5ea7be0b9 100644 --- a/web/app/components/base/app-unavailable.tsx +++ b/web/app/components/base/app-unavailable.tsx @@ -5,13 +5,13 @@ import { useTranslation } from 'react-i18next' type IAppUnavailableProps = { code?: number - isUnknwonReason?: boolean + isUnknownReason?: boolean unknownReason?: string } const AppUnavailable: FC = ({ code = 404, - isUnknwonReason, + isUnknownReason, unknownReason, }) => { const { t } = useTranslation() @@ -22,7 +22,7 @@ const AppUnavailable: FC = ({ style={{ borderRight: '1px solid rgba(0,0,0,.3)', }}>{code} -
{unknownReason || (isUnknwonReason ? t('share.common.appUnkonwError') : t('share.common.appUnavailable'))}
+
{unknownReason || (isUnknownReason ? t('share.common.appUnkonwError') : t('share.common.appUnavailable'))}
) } diff --git a/web/app/components/base/chat/chat-with-history/index.tsx b/web/app/components/base/chat/chat-with-history/index.tsx index f51620b151..b02091231d 100644 --- a/web/app/components/base/chat/chat-with-history/index.tsx +++ b/web/app/components/base/chat/chat-with-history/index.tsx @@ -181,12 +181,12 @@ const ChatWithHistoryWrapWithCheckToken: FC = ({ installedAppInfo, className, }) => { - const [inited, setInited] = useState(false) + const [initialized, setInitialized] = useState(false) const [appUnavailable, setAppUnavailable] = useState(false) - const [isUnknwonReason, setIsUnknwonReason] = useState(false) + const [isUnknownReason, setIsUnknownReason] = useState(false) useAsyncEffect(async () => { - if (!inited) { + if (!initialized) { if (!installedAppInfo) { try { await checkOrSetAccessToken() @@ -196,21 +196,21 @@ const ChatWithHistoryWrapWithCheckToken: FC = ({ setAppUnavailable(true) } else { - setIsUnknwonReason(true) + setIsUnknownReason(true) setAppUnavailable(true) } } } - setInited(true) + setInitialized(true) } }, []) - if (appUnavailable) - return - - if (!inited) + if (!initialized) return null + if (appUnavailable) + return + return ( { + const { + appParams, + appPrevChatList, + currentConversationId, + currentConversationItem, + inputsForms, + newConversationInputs, + handleNewConversationCompleted, + isMobile, + isInstalledApp, + appId, + appMeta, + handleFeedback, + currentChatInstanceRef, + } = useEmbeddedChatbotContext() + const appConfig = useMemo(() => { + const config = appParams || {} + + return { + ...config, + supportFeedback: true, + opening_statement: currentConversationId ? currentConversationItem?.introduction : (config as any).opening_statement, + } as ChatConfig + }, [appParams, currentConversationItem?.introduction, currentConversationId]) + const { + chatList, + handleSend, + handleStop, + isResponding, + suggestedQuestions, + } = useChat( + appConfig, + { + inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any, + promptVariables: inputsForms, + }, + appPrevChatList, + taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId), + ) + + useEffect(() => { + if (currentChatInstanceRef.current) + currentChatInstanceRef.current.handleStop = handleStop + }, []) + + const doSend: OnSend = useCallback((message, files) => { + const data: any = { + query: message, + inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs, + conversation_id: currentConversationId, + } + + if (appConfig?.file_upload?.image.enabled && files?.length) + data.files = files + + handleSend( + getUrl('chat-messages', isInstalledApp, appId || ''), + data, + { + onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId), + onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted, + isPublicAPI: !isInstalledApp, + }, + ) + }, [ + appConfig, + currentConversationId, + currentConversationItem, + handleSend, + newConversationInputs, + handleNewConversationCompleted, + isInstalledApp, + appId, + ]) + const chatNode = useMemo(() => { + if (inputsForms.length) { + return ( + <> + {!currentConversationId && ( +
+
+ +
+
+ )} + + ) + } + + return
+ }, [currentConversationId, inputsForms, isMobile]) + + return ( + : null} + hideProcessDetail + /> + ) +} + +export default ChatWrapper diff --git a/web/app/components/base/chat/embedded-chatbot/config-panel/form-input.tsx b/web/app/components/base/chat/embedded-chatbot/config-panel/form-input.tsx new file mode 100644 index 0000000000..c163e9409c --- /dev/null +++ b/web/app/components/base/chat/embedded-chatbot/config-panel/form-input.tsx @@ -0,0 +1,46 @@ +import type { FC } from 'react' +import { useTranslation } from 'react-i18next' +import { memo } from 'react' + +type InputProps = { + form: any + value: string + onChange: (variable: string, value: string) => void +} +const FormInput: FC = ({ + form, + value, + onChange, +}) => { + const { t } = useTranslation() + const { + type, + label, + required, + max_length, + variable, + } = form + + if (type === 'paragraph') { + return ( +