From fd7f8fd1f0da44e6a727f16cab9412561dc1e559 Mon Sep 17 00:00:00 2001 From: yyh Date: Sat, 27 Dec 2025 12:39:57 +0800 Subject: [PATCH] refactor(web): migrate share chat swr to react query --- .../base/chat/chat-with-history/hooks.tsx | 78 ++++++++---- .../base/chat/embedded-chatbot/hooks.tsx | 51 ++++++-- web/service/use-share.ts | 115 +++++++++++++++++- 3 files changed, 202 insertions(+), 42 deletions(-) diff --git a/web/app/components/base/chat/chat-with-history/hooks.tsx b/web/app/components/base/chat/chat-with-history/hooks.tsx index 3acc480518..8a3617129e 100644 --- a/web/app/components/base/chat/chat-with-history/hooks.tsx +++ b/web/app/components/base/chat/chat-with-history/hooks.tsx @@ -20,7 +20,6 @@ import { useState, } from 'react' import { useTranslation } from 'react-i18next' -import useSWR from 'swr' import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils' import { useToastContext } from '@/app/components/base/toast' import { InputVarType } from '@/app/components/workflow/types' @@ -29,14 +28,17 @@ import { useAppFavicon } from '@/hooks/use-app-favicon' import { changeLanguage } from '@/i18n-config/i18next-config' import { delConversation, - fetchChatList, - fetchConversations, - generationConversationName, pinConversation, renameConversation, unpinConversation, updateFeedback, } from '@/service/share' +import { + useInvalidateShareConversations, + useShareChatList, + useShareConversationName, + useShareConversations, +} from '@/service/use-share' import { TransferMethod } from '@/types/app' import { addFileInfos, sortAgentSorts } from '../../../tools/utils' import { CONVERSATION_ID_INFO } from '../constants' @@ -174,21 +176,42 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { return currentConversationId }, [currentConversationId, newConversationId]) - const { data: appPinnedConversationData, mutate: mutateAppPinnedConversationData } = useSWR( - appId ? ['appConversationData', isInstalledApp, appId, true] : null, - () => fetchConversations(isInstalledApp, appId, undefined, true, 100), - { revalidateOnFocus: false, revalidateOnReconnect: false }, - ) - const { data: appConversationData, isLoading: appConversationDataLoading, mutate: mutateAppConversationData } = useSWR( - appId ? ['appConversationData', isInstalledApp, appId, false] : null, - () => fetchConversations(isInstalledApp, appId, undefined, false, 100), - { revalidateOnFocus: false, revalidateOnReconnect: false }, - ) - const { data: appChatListData, isLoading: appChatListDataLoading } = useSWR( - chatShouldReloadKey ? ['appChatList', chatShouldReloadKey, isInstalledApp, appId] : null, - () => fetchChatList(chatShouldReloadKey, isInstalledApp, appId), - { revalidateOnFocus: false, revalidateOnReconnect: false }, - ) + const { data: appPinnedConversationData } = useShareConversations({ + isInstalledApp, + appId, + pinned: true, + limit: 100, + }, { + enabled: !!appId, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + }) + const { + data: appConversationData, + isLoading: appConversationDataLoading, + } = useShareConversations({ + isInstalledApp, + appId, + pinned: false, + limit: 100, + }, { + enabled: !!appId, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + }) + const { + data: appChatListData, + isLoading: appChatListDataLoading, + } = useShareChatList({ + conversationId: chatShouldReloadKey, + isInstalledApp, + appId, + }, { + enabled: !!chatShouldReloadKey, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + }) + const invalidateShareConversations = useInvalidateShareConversations() const [clearChatList, setClearChatList] = useState(false) const [isResponding, setIsResponding] = useState(false) @@ -309,7 +332,13 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { handleNewConversationInputsChange(conversationInputs) }, [handleNewConversationInputsChange, inputsForms]) - const { data: newConversation } = useSWR(newConversationId ? [isInstalledApp, appId, newConversationId] : null, () => generationConversationName(isInstalledApp, appId, newConversationId), { revalidateOnFocus: false }) + const { data: newConversation } = useShareConversationName({ + conversationId: newConversationId, + isInstalledApp, + appId, + }, { + refetchOnWindowFocus: false, + }) const [originConversationList, setOriginConversationList] = useState([]) useEffect(() => { if (appConversationData?.data && !appConversationDataLoading) @@ -429,9 +458,8 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { setClearChatList(true) }, [handleChangeConversation, setShowNewConversationItemInList, handleNewConversationInputsChange, setClearChatList, inputsForms]) const handleUpdateConversationList = useCallback(() => { - mutateAppConversationData() - mutateAppPinnedConversationData() - }, [mutateAppConversationData, mutateAppPinnedConversationData]) + invalidateShareConversations() + }, [invalidateShareConversations]) const handlePinConversation = useCallback(async (conversationId: string) => { await pinConversation(isInstalledApp, appId, conversationId) @@ -518,8 +546,8 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { setNewConversationId(newConversationId) handleConversationIdInfoChange(newConversationId) setShowNewConversationItemInList(false) - mutateAppConversationData() - }, [mutateAppConversationData, handleConversationIdInfoChange]) + invalidateShareConversations() + }, [handleConversationIdInfoChange, invalidateShareConversations]) const handleFeedback = useCallback(async (messageId: string, feedback: Feedback) => { await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating, content: feedback.content } }, isInstalledApp, appId) diff --git a/web/app/components/base/chat/embedded-chatbot/hooks.tsx b/web/app/components/base/chat/embedded-chatbot/hooks.tsx index 9e9125fc45..678590cde2 100644 --- a/web/app/components/base/chat/embedded-chatbot/hooks.tsx +++ b/web/app/components/base/chat/embedded-chatbot/hooks.tsx @@ -19,18 +19,18 @@ import { useState, } from 'react' import { useTranslation } from 'react-i18next' -import useSWR from 'swr' import { useToastContext } from '@/app/components/base/toast' import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils' import { InputVarType } from '@/app/components/workflow/types' import { useWebAppStore } from '@/context/web-app-context' import { changeLanguage } from '@/i18n-config/i18next-config' +import { updateFeedback } from '@/service/share' import { - fetchChatList, - fetchConversations, - generationConversationName, - updateFeedback, -} from '@/service/share' + useInvalidateShareConversations, + useShareChatList, + useShareConversationName, + useShareConversations, +} from '@/service/use-share' import { TransferMethod } from '@/types/app' import { getProcessedFilesFromResponse } from '../../file-uploader/utils' import { CONVERSATION_ID_INFO } from '../constants' @@ -137,9 +137,30 @@ export const useEmbeddedChatbot = () => { return currentConversationId }, [currentConversationId, newConversationId]) - const { data: appPinnedConversationData } = useSWR(['appConversationData', isInstalledApp, appId, true], () => fetchConversations(isInstalledApp, appId, undefined, true, 100)) - const { data: appConversationData, isLoading: appConversationDataLoading, mutate: mutateAppConversationData } = useSWR(['appConversationData', isInstalledApp, appId, false], () => fetchConversations(isInstalledApp, appId, undefined, false, 100)) - const { data: appChatListData, isLoading: appChatListDataLoading } = useSWR(chatShouldReloadKey ? ['appChatList', chatShouldReloadKey, isInstalledApp, appId] : null, () => fetchChatList(chatShouldReloadKey, isInstalledApp, appId)) + const { data: appPinnedConversationData } = useShareConversations({ + isInstalledApp, + appId, + pinned: true, + limit: 100, + }) + const { + data: appConversationData, + isLoading: appConversationDataLoading, + } = useShareConversations({ + isInstalledApp, + appId, + pinned: false, + limit: 100, + }) + const { + data: appChatListData, + isLoading: appChatListDataLoading, + } = useShareChatList({ + conversationId: chatShouldReloadKey, + isInstalledApp, + appId, + }) + const invalidateShareConversations = useInvalidateShareConversations() const [clearChatList, setClearChatList] = useState(false) const [isResponding, setIsResponding] = useState(false) @@ -259,7 +280,13 @@ export const useEmbeddedChatbot = () => { handleNewConversationInputsChange(conversationInputs) }, [handleNewConversationInputsChange, inputsForms]) - const { data: newConversation } = useSWR(newConversationId ? [isInstalledApp, appId, newConversationId] : null, () => generationConversationName(isInstalledApp, appId, newConversationId), { revalidateOnFocus: false }) + const { data: newConversation } = useShareConversationName({ + conversationId: newConversationId, + isInstalledApp, + appId, + }, { + refetchOnWindowFocus: false, + }) const [originConversationList, setOriginConversationList] = useState([]) useEffect(() => { if (appConversationData?.data && !appConversationDataLoading) @@ -379,8 +406,8 @@ export const useEmbeddedChatbot = () => { setNewConversationId(newConversationId) handleConversationIdInfoChange(newConversationId) setShowNewConversationItemInList(false) - mutateAppConversationData() - }, [mutateAppConversationData, handleConversationIdInfoChange]) + invalidateShareConversations() + }, [handleConversationIdInfoChange, invalidateShareConversations]) const handleFeedback = useCallback(async (messageId: string, feedback: Feedback) => { await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating, content: feedback.content } }, isInstalledApp, appId) diff --git a/web/service/use-share.ts b/web/service/use-share.ts index a5e0a11100..4dd43e06aa 100644 --- a/web/service/use-share.ts +++ b/web/service/use-share.ts @@ -1,11 +1,58 @@ +import type { AppConversationData, ConversationItem } from '@/models/share' import { useQuery } from '@tanstack/react-query' -import { fetchAppInfo, fetchAppMeta, fetchAppParams, getAppAccessModeByAppCode } from './share' +import { + fetchAppInfo, + fetchAppMeta, + fetchAppParams, + fetchChatList, + fetchConversations, + generationConversationName, + getAppAccessModeByAppCode, +} from './share' +import { useInvalid } from './use-base' const NAME_SPACE = 'webapp' +type ShareConversationsParams = { + isInstalledApp: boolean + appId?: string + lastId?: string + pinned?: boolean + limit?: number +} + +type ShareChatListParams = { + conversationId: string + isInstalledApp: boolean + appId?: string +} + +type ShareConversationNameParams = { + conversationId: string + isInstalledApp: boolean + appId?: string +} + +type ShareQueryOptions = { + enabled?: boolean + refetchOnWindowFocus?: boolean + refetchOnReconnect?: boolean +} + +export const shareQueryKeys = { + appAccessMode: (code: string | null) => [NAME_SPACE, 'appAccessMode', code] as const, + appInfo: [NAME_SPACE, 'appInfo'] as const, + appParams: [NAME_SPACE, 'appParams'] as const, + appMeta: [NAME_SPACE, 'appMeta'] as const, + conversations: [NAME_SPACE, 'conversations'] as const, + conversationList: (params: ShareConversationsParams) => [NAME_SPACE, 'conversations', params] as const, + chatList: (params: ShareChatListParams) => [NAME_SPACE, 'chatList', params] as const, + conversationName: (params: ShareConversationNameParams) => [NAME_SPACE, 'conversationName', params] as const, +} + export const useGetWebAppAccessModeByCode = (code: string | null) => { return useQuery({ - queryKey: [NAME_SPACE, 'appAccessMode', code], + queryKey: shareQueryKeys.appAccessMode(code), queryFn: () => getAppAccessModeByAppCode(code!), enabled: !!code, staleTime: 0, // backend change the access mode may cause the logic error. Because /permission API is no cached. @@ -15,7 +62,7 @@ export const useGetWebAppAccessModeByCode = (code: string | null) => { export const useGetWebAppInfo = () => { return useQuery({ - queryKey: [NAME_SPACE, 'appInfo'], + queryKey: shareQueryKeys.appInfo, queryFn: () => { return fetchAppInfo() }, @@ -24,7 +71,7 @@ export const useGetWebAppInfo = () => { export const useGetWebAppParams = () => { return useQuery({ - queryKey: [NAME_SPACE, 'appParams'], + queryKey: shareQueryKeys.appParams, queryFn: () => { return fetchAppParams(false) }, @@ -33,9 +80,67 @@ export const useGetWebAppParams = () => { export const useGetWebAppMeta = () => { return useQuery({ - queryKey: [NAME_SPACE, 'appMeta'], + queryKey: shareQueryKeys.appMeta, queryFn: () => { return fetchAppMeta(false) }, }) } + +export const useShareConversations = (params: ShareConversationsParams, options: ShareQueryOptions = {}) => { + const { + enabled = true, + refetchOnReconnect, + refetchOnWindowFocus, + } = options + const isEnabled = enabled && (!params.isInstalledApp || !!params.appId) + return useQuery({ + queryKey: shareQueryKeys.conversationList(params), + queryFn: () => fetchConversations( + params.isInstalledApp, + params.appId, + params.lastId, + params.pinned, + params.limit, + ), + enabled: isEnabled, + refetchOnReconnect, + refetchOnWindowFocus, + }) +} + +export const useShareChatList = (params: ShareChatListParams, options: ShareQueryOptions = {}) => { + const { + enabled = true, + refetchOnReconnect, + refetchOnWindowFocus, + } = options + const isEnabled = enabled && (!params.isInstalledApp || !!params.appId) && !!params.conversationId + return useQuery({ + queryKey: shareQueryKeys.chatList(params), + queryFn: () => fetchChatList(params.conversationId, params.isInstalledApp, params.appId), + enabled: isEnabled, + refetchOnReconnect, + refetchOnWindowFocus, + }) +} + +export const useShareConversationName = (params: ShareConversationNameParams, options: ShareQueryOptions = {}) => { + const { + enabled = true, + refetchOnReconnect, + refetchOnWindowFocus, + } = options + const isEnabled = enabled && (!params.isInstalledApp || !!params.appId) && !!params.conversationId + return useQuery({ + queryKey: shareQueryKeys.conversationName(params), + queryFn: () => generationConversationName(params.isInstalledApp, params.appId, params.conversationId), + enabled: isEnabled, + refetchOnReconnect, + refetchOnWindowFocus, + }) +} + +export const useInvalidateShareConversations = () => { + return useInvalid(shareQueryKeys.conversations) +}