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:
Leo 2024-04-11 00:10:58 +08:00 committed by GitHub
parent 0b21d77880
commit b2de79ae08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 112 additions and 33 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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>
);
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View 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",
},
});
},
};