From 9555b0179b80f50b82e43adbd60f7afee6dd29c2 Mon Sep 17 00:00:00 2001 From: Shinji-Li <1349021570@qq.com> Date: Wed, 6 Mar 2024 18:21:53 +0800 Subject: [PATCH] :pencil2: fix: slove if retrun null, still loading flag problem (#115) --- src/ProChat/components/ScrollAnchor/index.tsx | 4 +- src/ProChat/store/action.ts | 136 ++++-------------- 2 files changed, 31 insertions(+), 109 deletions(-) diff --git a/src/ProChat/components/ScrollAnchor/index.tsx b/src/ProChat/components/ScrollAnchor/index.tsx index 72665e5b..f36d8727 100644 --- a/src/ProChat/components/ScrollAnchor/index.tsx +++ b/src/ProChat/components/ScrollAnchor/index.tsx @@ -37,8 +37,8 @@ const ChatScrollAnchor = memo(({ target }: { target: React.RefObject { if (isWindowAvailable) { - // 如果是移动端,可能200太多了,认为超过 1/3 即可,PC默认200 - setScrollOffset(window.innerHeight / 3 > 200 ? 200 : window.innerHeight / 4); + // 如果是移动端,可能100太多了,认为超过 1/3 即可,PC默认100 + setScrollOffset(window.innerHeight / 3 > 100 ? 100 : window.innerHeight / 4); } }, [isWindowAvailable]); diff --git a/src/ProChat/store/action.ts b/src/ProChat/store/action.ts index b11d7954..dc7d31f9 100644 --- a/src/ProChat/store/action.ts +++ b/src/ProChat/store/action.ts @@ -8,6 +8,7 @@ import { isFunctionMessage } from '@/ProChat/utils/message'; import { setNamespace } from '@/ProChat/utils/storeDebug'; import { nanoid } from '@/ProChat/utils/uuid'; import { ChatMessage } from '@/types/message'; +import { startTransition } from 'react'; import { initialModelConfig } from '@/ProChat/store/initialState'; import { ChatStreamPayload } from '@/ProChat/types/chat'; @@ -109,16 +110,6 @@ export interface ChatAction { */ updateMessageContent: (id: string, content: string) => Promise; - /** - * 创建一条平滑输出的内容 - */ - createSmoothMessage: (id: string) => { - startAnimation: (speed?: number) => Promise; - stopAnimation: () => void; - outputQueue: string[]; - isAnimationActive: boolean; - }; - /** * 获取当前 loading 生成的消息 id * @returns 消息 id | undefined @@ -161,14 +152,7 @@ export const chatAction: StateCreator { - const { - dispatchMessage, - toggleChatLoading, - config, - defaultModelFetcher, - createSmoothMessage, - updateMessageContent, - } = get(); + const { dispatchMessage, toggleChatLoading, config, defaultModelFetcher } = get(); const abortController = toggleChatLoading( true, @@ -219,35 +203,23 @@ export const chatAction: StateCreator { dispatchMessage({ id: assistantId, key: 'error', type: 'updateMessage', value: error }); }, - onAbort: async () => { - stopAnimation(); - }, - onFinish: async (content) => { - stopAnimation(); - - if (outputQueue.length > 0 && !isFunctionCall) { - await startAnimation(15); - } - - await updateMessageContent(assistantId, content); - }, onMessageHandle: (text) => { output += text; - - if (!isAnimationActive && !isFunctionCall) startAnimation(); - - if (abortController?.signal.aborted) { - // aborted 后停止当前输出 - return; + if (!abortController.signal.aborted) { + startTransition(() => { + dispatchMessage({ + id: assistantId, + key: 'content', + type: 'updateMessage', + value: output, + }); + }); } else { - outputQueue.push(...text.split('')); + return; } // TODO: need a function call judge callback @@ -258,76 +230,13 @@ export const chatAction: StateCreator { + toggleChatLoading(false, undefined, t('generateMessage(end)') as string); + }); return { isFunctionCall }; }, - createSmoothMessage: (id) => { - const { dispatchMessage } = get(); - - let buffer = ''; - // why use queue: https://shareg.pt/GLBrjpK - let outputQueue: string[] = []; - - // eslint-disable-next-line no-undef - let animationTimeoutId: NodeJS.Timeout | null = null; - let isAnimationActive = false; - - // when you need to stop the animation, call this function - const stopAnimation = () => { - isAnimationActive = false; - if (animationTimeoutId !== null) { - clearTimeout(animationTimeoutId); - animationTimeoutId = null; - } - }; - - // define startAnimation function to display the text in buffer smooth - // when you need to start the animation, call this function - const startAnimation = (speed = 2) => - new Promise((resolve) => { - if (isAnimationActive) { - resolve(); - return; - } - - isAnimationActive = true; - - const updateText = () => { - // 如果动画已经不再激活,则停止更新文本 - if (!isAnimationActive) { - clearTimeout(animationTimeoutId!); - animationTimeoutId = null; - resolve(); - } - - // 如果还有文本没有显示 - // 检查队列中是否有字符待显示 - if (outputQueue.length > 0) { - // 从队列中获取前两个字符(如果存在) - const charsToAdd = outputQueue.splice(0, speed).join(''); - buffer += charsToAdd; - - // 更新消息内容,这里可能需要结合实际情况调整 - dispatchMessage({ id, key: 'content', type: 'updateMessage', value: buffer }); - - // 设置下一个字符的延迟 - animationTimeoutId = setTimeout(updateText, 16); // 16 毫秒的延迟模拟打字机效果 - } else { - // 当所有字符都显示完毕时,清除动画状态 - isAnimationActive = false; - animationTimeoutId = null; - resolve(); - } - }; - - updateText(); - }); - - return { startAnimation, stopAnimation, outputQueue, isAnimationActive }; - }, - realFetchAIResponse: async (messages, userMessageId) => { const { dispatchMessage, generateMessage, config, getMessageId } = get(); @@ -412,7 +321,20 @@ export const chatAction: StateCreator { - const { abortController, toggleChatLoading } = get(); + const { abortController, toggleChatLoading, chatLoadingId, chats, dispatchMessage } = get(); + // 如果当前 最后一条为 chatLoadingId 停止前需要清空 + if (chats && chats.length > 0) { + const lastChat = chats[chats.length - 1]; + if (lastChat.content === LOADING_FLAT) { + dispatchMessage({ + id: chatLoadingId, + key: 'content', + type: 'updateMessage', + value: '', + }); + } + } + if (!abortController) return; abortController.abort();