mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Feat add toast (#951)
* feat: add agent status bar. * fix: ws will reconnect after failed in first time. * update: delete useless codes. * feat: add toast
This commit is contained in:
parent
0b21d77880
commit
b2de79ae08
27
frontend/package-lock.json
generated
27
frontend/package-lock.json
generated
@ -25,6 +25,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-accessible-treeview": "^2.8.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-i18next": "^14.1.0",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-redux": "^9.1.0",
|
||||
@ -6099,8 +6100,7 @@
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"devOptional": true
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/damerau-levenshtein": {
|
||||
"version": "1.0.8",
|
||||
@ -7833,6 +7833,14 @@
|
||||
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
|
||||
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="
|
||||
},
|
||||
"node_modules/goober": {
|
||||
"version": "2.1.14",
|
||||
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz",
|
||||
"integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==",
|
||||
"peerDependencies": {
|
||||
"csstype": "^3.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
@ -11307,6 +11315,21 @@
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hot-toast": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz",
|
||||
"integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==",
|
||||
"dependencies": {
|
||||
"goober": "^2.1.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16",
|
||||
"react-dom": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-14.1.0.tgz",
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-accessible-treeview": "^2.8.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-i18next": "^14.1.0",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-redux": "^9.1.0",
|
||||
@ -63,11 +64,20 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^6.4.2",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^18.0.0 ",
|
||||
"@types/react": "^18.2.66",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@types/react-syntax-highlighter": "^15.5.11",
|
||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
@ -82,15 +92,6 @@
|
||||
"prettier": "^3.2.5",
|
||||
"tailwindcss": "^3.4.2",
|
||||
"ts-jest": "^29.1.2",
|
||||
"@testing-library/jest-dom": "^6.4.2",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/node": "^18.0.0 ",
|
||||
"@types/react": "^18.2.66",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@types/react-syntax-highlighter": "^15.5.11",
|
||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"typescript": "^5.4.3"
|
||||
},
|
||||
"packageManager": "npm@10.5.0",
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./App.css";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import CogTooth from "./assets/cog-tooth";
|
||||
import ChatInterface from "./components/ChatInterface";
|
||||
import Errors from "./components/Errors";
|
||||
@ -111,6 +112,7 @@ function App(): JSX.Element {
|
||||
onClose={() => setLoadMsgWarning(false)}
|
||||
/>
|
||||
<Errors />
|
||||
<Toaster />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { appendAssistantMessage } from "../state/chatSlice";
|
||||
import { setInitialized } from "../state/taskSlice";
|
||||
import store from "../store";
|
||||
import ActionType from "../types/ActionType";
|
||||
@ -6,6 +5,7 @@ import Socket from "./socket";
|
||||
import { setAllSettings, setByKey } from "../state/settingsSlice";
|
||||
import { ResConfigurations } from "../types/ResponseType";
|
||||
import { ArgConfigType } from "../types/ConfigType";
|
||||
import toast from "../utils/toast";
|
||||
|
||||
export async function fetchConfigurations(): Promise<ResConfigurations> {
|
||||
const headers = new Headers({
|
||||
@ -98,12 +98,14 @@ export function saveSettings(
|
||||
},
|
||||
);
|
||||
|
||||
let i = 0;
|
||||
for (const [key, value] of Object.entries(updatedSettings)) {
|
||||
if (DISPLAY_MAP.has(key)) {
|
||||
store.dispatch(setByKey({ key, value }));
|
||||
store.dispatch(
|
||||
appendAssistantMessage(`Set ${DISPLAY_MAP.get(key)} to "${value}"`),
|
||||
);
|
||||
setTimeout(() => {
|
||||
toast.settingsChanged(`Set ${DISPLAY_MAP.get(key)} to "${value}"`);
|
||||
}, i * 500);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import store from "../store";
|
||||
import { appendError, removeError } from "../state/errorsSlice";
|
||||
// import { toast } from "sonner";
|
||||
import { handleAssistantMessage } from "./actions";
|
||||
import { getToken } from "./auth";
|
||||
import toast from "../utils/toast";
|
||||
|
||||
class Socket {
|
||||
private static _socket: WebSocket | null = null;
|
||||
@ -27,12 +27,9 @@ class Socket {
|
||||
.then((token) => {
|
||||
Socket._initialize(token);
|
||||
})
|
||||
.catch((err) => {
|
||||
const msg = `Failed to get token: ${err}.`;
|
||||
store.dispatch(appendError(msg));
|
||||
setTimeout(() => {
|
||||
store.dispatch(removeError(msg));
|
||||
}, 2000);
|
||||
.catch(() => {
|
||||
const msg = `Connection failed. Retry...`;
|
||||
toast.stickyError("ws", msg);
|
||||
|
||||
if (this.isFirstRun) {
|
||||
setTimeout(() => {
|
||||
@ -49,6 +46,7 @@ class Socket {
|
||||
Socket._socket = new WebSocket(WS_URL);
|
||||
|
||||
Socket._socket.onopen = (e) => {
|
||||
toast.stickySuccess("ws", "Connected to server.");
|
||||
Socket.callbacks.open?.forEach((callback) => {
|
||||
callback(e);
|
||||
});
|
||||
@ -59,11 +57,8 @@ class Socket {
|
||||
};
|
||||
|
||||
Socket._socket.onerror = () => {
|
||||
const msg = "Failed connection to server";
|
||||
store.dispatch(appendError(msg));
|
||||
setTimeout(() => {
|
||||
store.dispatch(removeError(msg));
|
||||
}, 2000);
|
||||
const msg = "Connection failed. Retry...";
|
||||
toast.stickyError("ws", msg);
|
||||
};
|
||||
|
||||
Socket._socket.onclose = () => {
|
||||
@ -88,11 +83,8 @@ class Socket {
|
||||
if (Socket.isConnected()) {
|
||||
Socket._socket?.send(message);
|
||||
} else {
|
||||
const msg = "WebSocket connection is not ready.";
|
||||
store.dispatch(appendError(msg));
|
||||
setTimeout(() => {
|
||||
store.dispatch(removeError(msg));
|
||||
}, 2000);
|
||||
const msg = "Connection failed. Retry...";
|
||||
toast.stickyError("ws", msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
59
frontend/src/utils/toast.tsx
Normal file
59
frontend/src/utils/toast.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
const idMap = new Map<string, string>();
|
||||
|
||||
export default {
|
||||
stickyError: (id: string, msg: string) => {
|
||||
if (idMap.has(id)) return; // prevent duplicate toast
|
||||
const toastId = toast.loading(msg, {
|
||||
// icon: "👏",
|
||||
// style: {
|
||||
// borderRadius: "10px",
|
||||
// background: "#333",
|
||||
// color: "#fff",
|
||||
// },
|
||||
style: {
|
||||
background: "#ef4444",
|
||||
color: "#fff",
|
||||
lineBreak: "anywhere",
|
||||
},
|
||||
iconTheme: {
|
||||
primary: "#ef4444",
|
||||
secondary: "#fff",
|
||||
},
|
||||
});
|
||||
idMap.set(id, toastId);
|
||||
},
|
||||
stickySuccess: (id: string, msg: string) => {
|
||||
const toastId = idMap.get(id);
|
||||
if (toastId === undefined) return;
|
||||
if (toastId) {
|
||||
toast.success(msg, {
|
||||
id: toastId,
|
||||
style: {
|
||||
background: "#333",
|
||||
color: "#fff",
|
||||
lineBreak: "anywhere",
|
||||
},
|
||||
iconTheme: {
|
||||
primary: "#333",
|
||||
secondary: "#fff",
|
||||
},
|
||||
});
|
||||
}
|
||||
idMap.delete(id);
|
||||
},
|
||||
settingsChanged: (msg: string) => {
|
||||
toast(msg, {
|
||||
position: "bottom-right",
|
||||
className: "bg-neutral-700",
|
||||
|
||||
icon: "⚙️",
|
||||
style: {
|
||||
background: "#333",
|
||||
color: "#fff",
|
||||
lineBreak: "anywhere",
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user