mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
fix(ui): startup message ui (#9007)
This commit is contained in:
parent
53b5e08804
commit
058153292f
@ -4,7 +4,6 @@ import userEvent from "@testing-library/user-event";
|
||||
import { renderWithProviders } from "test-utils";
|
||||
import type { Message } from "#/message";
|
||||
import { SUGGESTIONS } from "#/utils/suggestions";
|
||||
import { WsClientProviderStatus } from "#/context/ws-client-provider";
|
||||
import { ChatInterface } from "#/components/features/chat/chat-interface";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
@ -19,7 +18,7 @@ describe("Empty state", () => {
|
||||
const { useWsClient: useWsClientMock } = vi.hoisted(() => ({
|
||||
useWsClient: vi.fn(() => ({
|
||||
send: sendMock,
|
||||
status: WsClientProviderStatus.CONNECTED,
|
||||
status: "CONNECTED",
|
||||
isLoadingMessages: false,
|
||||
})),
|
||||
}));
|
||||
@ -64,7 +63,7 @@ describe("Empty state", () => {
|
||||
// this is to test that the message is in the UI before the socket is called
|
||||
useWsClientMock.mockImplementation(() => ({
|
||||
send: sendMock,
|
||||
status: WsClientProviderStatus.CONNECTED,
|
||||
status: "CONNECTED",
|
||||
isLoadingMessages: false,
|
||||
}));
|
||||
const user = userEvent.setup();
|
||||
@ -87,7 +86,7 @@ describe("Empty state", () => {
|
||||
async () => {
|
||||
useWsClientMock.mockImplementation(() => ({
|
||||
send: sendMock,
|
||||
status: WsClientProviderStatus.CONNECTED,
|
||||
status: "CONNECTED",
|
||||
isLoadingMessages: false,
|
||||
}));
|
||||
const user = userEvent.setup();
|
||||
@ -101,7 +100,7 @@ describe("Empty state", () => {
|
||||
|
||||
useWsClientMock.mockImplementation(() => ({
|
||||
send: sendMock,
|
||||
status: WsClientProviderStatus.CONNECTED,
|
||||
status: "CONNECTED",
|
||||
isLoadingMessages: false,
|
||||
}));
|
||||
rerender(<ChatInterface />);
|
||||
|
||||
@ -9,10 +9,7 @@ import {
|
||||
AGENT_STATUS_MAP,
|
||||
IndicatorColor,
|
||||
} from "../../agent-status-map.constant";
|
||||
import {
|
||||
useWsClient,
|
||||
WsClientProviderStatus,
|
||||
} from "#/context/ws-client-provider";
|
||||
import { useWsClient } from "#/context/ws-client-provider";
|
||||
import { useNotification } from "#/hooks/useNotification";
|
||||
import { browserTab } from "#/utils/browser-tab";
|
||||
import { useActiveConversation } from "#/hooks/query/use-active-conversation";
|
||||
@ -80,10 +77,13 @@ export function AgentStatusBar() {
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (conversation?.status === "STARTING") {
|
||||
if (conversation?.status === "CONNECTING") {
|
||||
setStatusMessage(t(I18nKey.STATUS$CONNECTING_TO_RUNTIME));
|
||||
setIndicatorColor(IndicatorColor.YELLOW);
|
||||
} else if (conversation?.status === "STARTING") {
|
||||
setStatusMessage(t(I18nKey.STATUS$STARTING_RUNTIME));
|
||||
setIndicatorColor(IndicatorColor.RED);
|
||||
} else if (status === WsClientProviderStatus.DISCONNECTED) {
|
||||
} else if (status === "DISCONNECTED") {
|
||||
setStatusMessage(t(I18nKey.STATUS$WEBSOCKET_CLOSED));
|
||||
setIndicatorColor(IndicatorColor.RED);
|
||||
} else {
|
||||
|
||||
@ -2,9 +2,20 @@ import ColdIcon from "./state-indicators/cold.svg?react";
|
||||
import RunningIcon from "./state-indicators/running.svg?react";
|
||||
|
||||
type SVGIcon = React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
|
||||
export type ProjectStatus = "RUNNING" | "STOPPED" | "STARTING";
|
||||
export type ProjectStatus =
|
||||
| "RUNNING"
|
||||
| "STOPPED"
|
||||
| "STARTING"
|
||||
| "CONNECTING"
|
||||
| "CONNECTED"
|
||||
| "DISCONNECTED";
|
||||
|
||||
const INDICATORS: Record<ProjectStatus, SVGIcon> = {
|
||||
type ProjectStatusWithIcon = Exclude<
|
||||
ProjectStatus,
|
||||
"CONNECTING" | "CONNECTED" | "DISCONNECTED"
|
||||
>;
|
||||
|
||||
const INDICATORS: Record<ProjectStatusWithIcon, SVGIcon> = {
|
||||
STOPPED: ColdIcon,
|
||||
RUNNING: RunningIcon,
|
||||
STARTING: ColdIcon,
|
||||
@ -17,6 +28,7 @@ interface ConversationStateIndicatorProps {
|
||||
export function ConversationStateIndicator({
|
||||
status,
|
||||
}: ConversationStateIndicatorProps) {
|
||||
// @ts-expect-error - Type 'ProjectStatus' is not assignable to type 'ProjectStatusWithIcon'.
|
||||
const StateIcon = INDICATORS[status];
|
||||
|
||||
return (
|
||||
|
||||
@ -28,6 +28,7 @@ import {
|
||||
} from "#/types/core/guards";
|
||||
import { useOptimisticUserMessage } from "#/hooks/use-optimistic-user-message";
|
||||
import { useWSErrorMessage } from "#/hooks/use-ws-error-message";
|
||||
import { ProjectStatus } from "#/components/features/conversation-panel/conversation-state-indicator";
|
||||
|
||||
const hasValidMessageProperty = (obj: unknown): obj is { message: string } =>
|
||||
typeof obj === "object" &&
|
||||
@ -67,14 +68,8 @@ const isMessageAction = (
|
||||
): event is UserMessageAction | AssistantMessageAction =>
|
||||
isUserMessage(event) || isAssistantMessage(event);
|
||||
|
||||
export enum WsClientProviderStatus {
|
||||
CONNECTED,
|
||||
DISCONNECTED,
|
||||
CONNECTING,
|
||||
}
|
||||
|
||||
interface UseWsClient {
|
||||
status: WsClientProviderStatus;
|
||||
status: ProjectStatus;
|
||||
isLoadingMessages: boolean;
|
||||
events: Record<string, unknown>[];
|
||||
parsedEvents: (OpenHandsAction | OpenHandsObservation)[];
|
||||
@ -82,7 +77,7 @@ interface UseWsClient {
|
||||
}
|
||||
|
||||
const WsClientContext = React.createContext<UseWsClient>({
|
||||
status: WsClientProviderStatus.DISCONNECTED,
|
||||
status: "DISCONNECTED",
|
||||
isLoadingMessages: true,
|
||||
events: [],
|
||||
parsedEvents: [],
|
||||
@ -139,9 +134,7 @@ export function WsClientProvider({
|
||||
const { setErrorMessage, removeErrorMessage } = useWSErrorMessage();
|
||||
const queryClient = useQueryClient();
|
||||
const sioRef = React.useRef<Socket | null>(null);
|
||||
const [status, setStatus] = React.useState(
|
||||
WsClientProviderStatus.DISCONNECTED,
|
||||
);
|
||||
const [status, setStatus] = React.useState<ProjectStatus>("CONNECTING");
|
||||
const [events, setEvents] = React.useState<Record<string, unknown>[]>([]);
|
||||
const [parsedEvents, setParsedEvents] = React.useState<
|
||||
(OpenHandsAction | OpenHandsObservation)[]
|
||||
@ -162,7 +155,7 @@ export function WsClientProvider({
|
||||
}
|
||||
|
||||
function handleConnect() {
|
||||
setStatus(WsClientProviderStatus.CONNECTED);
|
||||
setStatus("CONNECTED");
|
||||
removeErrorMessage();
|
||||
}
|
||||
|
||||
@ -261,7 +254,7 @@ export function WsClientProvider({
|
||||
}
|
||||
|
||||
function handleDisconnect(data: unknown) {
|
||||
setStatus(WsClientProviderStatus.DISCONNECTED);
|
||||
setStatus("DISCONNECTED");
|
||||
const sio = sioRef.current;
|
||||
if (!sio) {
|
||||
return;
|
||||
@ -275,7 +268,7 @@ export function WsClientProvider({
|
||||
|
||||
function handleError(data: unknown) {
|
||||
// set status
|
||||
setStatus(WsClientProviderStatus.DISCONNECTED);
|
||||
setStatus("DISCONNECTED");
|
||||
updateStatusWhenErrorMessagePresent(data);
|
||||
|
||||
setErrorMessage(
|
||||
@ -294,7 +287,7 @@ export function WsClientProvider({
|
||||
// reset events when conversationId changes
|
||||
setEvents([]);
|
||||
setParsedEvents([]);
|
||||
setStatus(WsClientProviderStatus.DISCONNECTED);
|
||||
setStatus("CONNECTING");
|
||||
}, [conversationId]);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import React from "react";
|
||||
import {
|
||||
useWsClient,
|
||||
WsClientProviderStatus,
|
||||
} from "#/context/ws-client-provider";
|
||||
import { useWsClient } from "#/context/ws-client-provider";
|
||||
import { useConversationId } from "#/hooks/use-conversation-id";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
|
||||
@ -17,7 +14,7 @@ export const useConversationConfig = () => {
|
||||
if (!conversationId) throw new Error("No conversation ID");
|
||||
return OpenHands.getRuntimeId(conversationId);
|
||||
},
|
||||
enabled: status !== WsClientProviderStatus.DISCONNECTED && !!conversationId,
|
||||
enabled: status !== "DISCONNECTED" && !!conversationId,
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
gcTime: 1000 * 60 * 15, // 15 minutes
|
||||
});
|
||||
|
||||
@ -1,5 +1,15 @@
|
||||
// this file generate by script, don't modify it manually!!!
|
||||
export enum I18nKey {
|
||||
MICROAGENT$NO_REPOSITORY_FOUND = "MICROAGENT$NO_REPOSITORY_FOUND",
|
||||
MICROAGENT$ADD_TO_MICROAGENT = "MICROAGENT$ADD_TO_MICROAGENT",
|
||||
MICROAGENT$WHAT_TO_ADD = "MICROAGENT$WHAT_TO_ADD",
|
||||
MICROAGENT$WHERE_TO_PUT = "MICROAGENT$WHERE_TO_PUT",
|
||||
MICROAGENT$ADD_TRIGGER = "MICROAGENT$ADD_TRIGGER",
|
||||
MICROAGENT$WAIT_FOR_RUNTIME = "MICROAGENT$WAIT_FOR_RUNTIME",
|
||||
MICROAGENT$ADDING_CONTEXT = "MICROAGENT$ADDING_CONTEXT",
|
||||
MICROAGENT$VIEW_CONVERSATION = "MICROAGENT$VIEW_CONVERSATION",
|
||||
MICROAGENT$SUCCESS_PR_READY = "MICROAGENT$SUCCESS_PR_READY",
|
||||
STATUS$CONNECTING_TO_RUNTIME = "STATUS$CONNECTING_TO_RUNTIME",
|
||||
STATUS$WEBSOCKET_CLOSED = "STATUS$WEBSOCKET_CLOSED",
|
||||
HOME$LAUNCH_FROM_SCRATCH = "HOME$LAUNCH_FROM_SCRATCH",
|
||||
HOME$READ_THIS = "HOME$READ_THIS",
|
||||
|
||||
@ -5855,6 +5855,22 @@
|
||||
"tr": "Konuşmalar",
|
||||
"uk": "Розмови"
|
||||
},
|
||||
"STATUS$CONNECTING_TO_RUNTIME": {
|
||||
"en": "Connecting to runtime...",
|
||||
"zh-CN": "正在连接到运行时...",
|
||||
"zh-TW": "正在連接到執行時...",
|
||||
"de": "Verbinde mit der Laufzeitumgebung...",
|
||||
"ko-KR": "런타임에 연결 중...",
|
||||
"no": "Kobler til kjøretidsmiljø...",
|
||||
"it": "Connessione all'ambiente di esecuzione in corso...",
|
||||
"pt": "Conectando ao ambiente de execução...",
|
||||
"es": "Conectando al entorno de ejecución...",
|
||||
"ar": "جارٍ الاتصال ببيئة التشغيل...",
|
||||
"fr": "Connexion à l'environnement d'exécution en cours...",
|
||||
"tr": "Çalışma zamanı ortamına bağlanılıyor...",
|
||||
"ja": "ランタイムに接続中",
|
||||
"uk": "Підключення до середовища виконання..."
|
||||
},
|
||||
"STATUS$STARTING_RUNTIME": {
|
||||
"en": "Starting runtime...",
|
||||
"zh-CN": "启动运行时...",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user