From 7888c98843e41ed5cf122f592cf2cfc308111636 Mon Sep 17 00:00:00 2001 From: hieptl Date: Fri, 26 Sep 2025 15:28:33 +0700 Subject: [PATCH 1/3] refactor: migration of use-ws-error-message.ts to zustand --- .../components/chat/chat-interface.test.tsx | 12 ++++--- .../features/chat/chat-interface.tsx | 6 ++-- frontend/src/context/ws-client-provider.tsx | 4 +-- frontend/src/hooks/use-ws-error-message.ts | 22 ------------- frontend/src/stores/error-message-store.ts | 33 +++++++++++++++++++ 5 files changed, 47 insertions(+), 30 deletions(-) delete mode 100644 frontend/src/hooks/use-ws-error-message.ts create mode 100644 frontend/src/stores/error-message-store.ts diff --git a/frontend/__tests__/components/chat/chat-interface.test.tsx b/frontend/__tests__/components/chat/chat-interface.test.tsx index 792a4c4fa7ba..7955761c4f88 100644 --- a/frontend/__tests__/components/chat/chat-interface.test.tsx +++ b/frontend/__tests__/components/chat/chat-interface.test.tsx @@ -18,7 +18,7 @@ import { SUGGESTIONS } from "#/utils/suggestions"; import { ChatInterface } from "#/components/features/chat/chat-interface"; import { useWsClient } from "#/context/ws-client-provider"; import { useOptimisticUserMessage } from "#/hooks/use-optimistic-user-message"; -import { useWSErrorMessage } from "#/hooks/use-ws-error-message"; +import { useErrorMessageStore } from "#/stores/error-message-store"; import { useConfig } from "#/hooks/query/use-config"; import { useGetTrajectory } from "#/hooks/mutation/use-get-trajectory"; import { useUploadFiles } from "#/hooks/mutation/use-upload-files"; @@ -27,7 +27,7 @@ import { OpenHandsAction } from "#/types/core/actions"; // Mock the hooks vi.mock("#/context/ws-client-provider"); vi.mock("#/hooks/use-optimistic-user-message"); -vi.mock("#/hooks/use-ws-error-message"); +vi.mock("#/stores/error-message-store"); vi.mock("#/hooks/query/use-config"); vi.mock("#/hooks/mutation/use-get-trajectory"); vi.mock("#/hooks/mutation/use-upload-files"); @@ -146,7 +146,9 @@ describe("ChatInterface - Chat Suggestions", () => { setOptimisticUserMessage: vi.fn(), getOptimisticUserMessage: vi.fn(() => null), }); - (useWSErrorMessage as unknown as ReturnType).mockReturnValue({ + ( + useErrorMessageStore as unknown as ReturnType + ).mockReturnValue({ getErrorMessage: vi.fn(() => null), setErrorMessage: vi.fn(), removeErrorMessage: vi.fn(), @@ -283,7 +285,9 @@ describe("ChatInterface - Empty state", () => { setOptimisticUserMessage: vi.fn(), getOptimisticUserMessage: vi.fn(() => null), }); - (useWSErrorMessage as unknown as ReturnType).mockReturnValue({ + ( + useErrorMessageStore as unknown as ReturnType + ).mockReturnValue({ getErrorMessage: vi.fn(() => null), setErrorMessage: vi.fn(), removeErrorMessage: vi.fn(), diff --git a/frontend/src/components/features/chat/chat-interface.tsx b/frontend/src/components/features/chat/chat-interface.tsx index b7217594b87d..0f9eff8bbf3e 100644 --- a/frontend/src/components/features/chat/chat-interface.tsx +++ b/frontend/src/components/features/chat/chat-interface.tsx @@ -23,7 +23,7 @@ import { ScrollToBottomButton } from "#/components/shared/buttons/scroll-to-bott import { LoadingSpinner } from "#/components/shared/loading-spinner"; import { displayErrorToast } from "#/utils/custom-toast-handlers"; import { useOptimisticUserMessage } from "#/hooks/use-optimistic-user-message"; -import { useWSErrorMessage } from "#/hooks/use-ws-error-message"; +import { useErrorMessageStore } from "#/stores/error-message-store"; import { ErrorMessageBanner } from "./error-message-banner"; import { hasUserEvent, @@ -46,7 +46,7 @@ function getEntryPoint( export function ChatInterface() { const { setMessageToSend } = useConversationStore(); - const { getErrorMessage } = useWSErrorMessage(); + const { getErrorMessage } = useErrorMessageStore(); const { send, isLoadingMessages, parsedEvents } = useWsClient(); const { setOptimisticUserMessage, getOptimisticUserMessage } = useOptimisticUserMessage(); @@ -75,6 +75,8 @@ export function ChatInterface() { const optimisticUserMessage = getOptimisticUserMessage(); const errorMessage = getErrorMessage(); + console.log("errorMessage", errorMessage); + const events = parsedEvents.filter(shouldRenderEvent); // Check if there are any substantive agent actions (not just system messages) diff --git a/frontend/src/context/ws-client-provider.tsx b/frontend/src/context/ws-client-provider.tsx index 77a02e2d3997..e76b16d1e49a 100644 --- a/frontend/src/context/ws-client-provider.tsx +++ b/frontend/src/context/ws-client-provider.tsx @@ -27,7 +27,7 @@ import { isUserMessage, } from "#/types/core/guards"; import { useOptimisticUserMessage } from "#/hooks/use-optimistic-user-message"; -import { useWSErrorMessage } from "#/hooks/use-ws-error-message"; +import { useErrorMessageStore } from "#/stores/error-message-store"; export type WebSocketStatus = "CONNECTING" | "CONNECTED" | "DISCONNECTED"; @@ -132,7 +132,7 @@ export function WsClientProvider({ children, }: React.PropsWithChildren) { const { removeOptimisticUserMessage } = useOptimisticUserMessage(); - const { setErrorMessage, removeErrorMessage } = useWSErrorMessage(); + const { setErrorMessage, removeErrorMessage } = useErrorMessageStore(); const queryClient = useQueryClient(); const sioRef = React.useRef(null); const [webSocketStatus, setWebSocketStatus] = diff --git a/frontend/src/hooks/use-ws-error-message.ts b/frontend/src/hooks/use-ws-error-message.ts deleted file mode 100644 index 370804b7b0f6..000000000000 --- a/frontend/src/hooks/use-ws-error-message.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useQueryClient } from "@tanstack/react-query"; - -export const useWSErrorMessage = () => { - const queryClient = useQueryClient(); - - const setErrorMessage = (message: string) => { - queryClient.setQueryData(["error_message"], message); - }; - - const getErrorMessage = () => - queryClient.getQueryData(["error_message"]); - - const removeErrorMessage = () => { - queryClient.removeQueries({ queryKey: ["error_message"] }); - }; - - return { - setErrorMessage, - getErrorMessage, - removeErrorMessage, - }; -}; diff --git a/frontend/src/stores/error-message-store.ts b/frontend/src/stores/error-message-store.ts new file mode 100644 index 000000000000..82c58f8bd011 --- /dev/null +++ b/frontend/src/stores/error-message-store.ts @@ -0,0 +1,33 @@ +import { create } from "zustand"; + +interface ErrorMessageState { + errorMessage: string | null; +} + +interface ErrorMessageActions { + setErrorMessage: (message: string) => void; + getErrorMessage: () => string | null; + removeErrorMessage: () => void; +} + +type ErrorMessageStore = ErrorMessageState & ErrorMessageActions; + +const initialState: ErrorMessageState = { + errorMessage: null, +}; + +export const useErrorMessageStore = create((set, get) => ({ + ...initialState, + + setErrorMessage: (message: string) => + set(() => ({ + errorMessage: message, + })), + + getErrorMessage: () => get().errorMessage, + + removeErrorMessage: () => + set(() => ({ + errorMessage: null, + })), +})); From 187d7759ebc53483e780945be630135e4a3fd75f Mon Sep 17 00:00:00 2001 From: hieptl Date: Fri, 26 Sep 2025 22:07:20 +0700 Subject: [PATCH 2/3] refactor: remove console.log --- frontend/src/components/features/chat/chat-interface.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/components/features/chat/chat-interface.tsx b/frontend/src/components/features/chat/chat-interface.tsx index 0f9eff8bbf3e..9f04b95a6992 100644 --- a/frontend/src/components/features/chat/chat-interface.tsx +++ b/frontend/src/components/features/chat/chat-interface.tsx @@ -75,8 +75,6 @@ export function ChatInterface() { const optimisticUserMessage = getOptimisticUserMessage(); const errorMessage = getErrorMessage(); - console.log("errorMessage", errorMessage); - const events = parsedEvents.filter(shouldRenderEvent); // Check if there are any substantive agent actions (not just system messages) From a4139ed7e40dbe3168e21e47bb2e7904e71d1259 Mon Sep 17 00:00:00 2001 From: hieptl Date: Tue, 30 Sep 2025 21:01:22 +0700 Subject: [PATCH 3/3] refactor: remove getErrorMessage --- frontend/__tests__/components/chat/chat-interface.test.tsx | 2 -- frontend/src/components/features/chat/chat-interface.tsx | 3 +-- frontend/src/stores/error-message-store.ts | 5 +---- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/frontend/__tests__/components/chat/chat-interface.test.tsx b/frontend/__tests__/components/chat/chat-interface.test.tsx index 9013fd83954a..e4f5c1b0f0c6 100644 --- a/frontend/__tests__/components/chat/chat-interface.test.tsx +++ b/frontend/__tests__/components/chat/chat-interface.test.tsx @@ -116,7 +116,6 @@ describe("ChatInterface - Chat Suggestions", () => { ( useErrorMessageStore as unknown as ReturnType ).mockReturnValue({ - getErrorMessage: vi.fn(() => null), setErrorMessage: vi.fn(), removeErrorMessage: vi.fn(), }); @@ -255,7 +254,6 @@ describe("ChatInterface - Empty state", () => { ( useErrorMessageStore as unknown as ReturnType ).mockReturnValue({ - getErrorMessage: vi.fn(() => null), setErrorMessage: vi.fn(), removeErrorMessage: vi.fn(), }); diff --git a/frontend/src/components/features/chat/chat-interface.tsx b/frontend/src/components/features/chat/chat-interface.tsx index 2944159d5e19..d4eb108a23e5 100644 --- a/frontend/src/components/features/chat/chat-interface.tsx +++ b/frontend/src/components/features/chat/chat-interface.tsx @@ -46,7 +46,7 @@ function getEntryPoint( export function ChatInterface() { const { setMessageToSend } = useConversationStore(); - const { getErrorMessage } = useErrorMessageStore(); + const { errorMessage } = useErrorMessageStore(); const { send, isLoadingMessages, parsedEvents } = useWsClient(); const { setOptimisticUserMessage, getOptimisticUserMessage } = useOptimisticUserMessageStore(); @@ -73,7 +73,6 @@ export function ChatInterface() { const { mutateAsync: uploadFiles } = useUploadFiles(); const optimisticUserMessage = getOptimisticUserMessage(); - const errorMessage = getErrorMessage(); const events = parsedEvents.filter(shouldRenderEvent); diff --git a/frontend/src/stores/error-message-store.ts b/frontend/src/stores/error-message-store.ts index 82c58f8bd011..4416814ed869 100644 --- a/frontend/src/stores/error-message-store.ts +++ b/frontend/src/stores/error-message-store.ts @@ -6,7 +6,6 @@ interface ErrorMessageState { interface ErrorMessageActions { setErrorMessage: (message: string) => void; - getErrorMessage: () => string | null; removeErrorMessage: () => void; } @@ -16,7 +15,7 @@ const initialState: ErrorMessageState = { errorMessage: null, }; -export const useErrorMessageStore = create((set, get) => ({ +export const useErrorMessageStore = create((set) => ({ ...initialState, setErrorMessage: (message: string) => @@ -24,8 +23,6 @@ export const useErrorMessageStore = create((set, get) => ({ errorMessage: message, })), - getErrorMessage: () => get().errorMessage, - removeErrorMessage: () => set(() => ({ errorMessage: null,