fix(ui): replace bare crypto.randomUUID with uniqueId fallback and show WS disconnect error

- use-chat-sessions, skill-upload-dialog: replace crypto.randomUUID()
  with uniqueId() which has a UUID v4 fallback for non-secure contexts
  (HTTP without localhost) where crypto.randomUUID is unavailable
- use-chat-send: show localized error message when WebSocket is not
  connected instead of silently dropping the send
- Add error.notConnected i18n key to en/vi/zh chat locale files

Closes #352
This commit is contained in:
viettranx committed 2026-03-23 16:12:07 +07:00
1 parent 072d98a856
commit 59a52f1e2f
6 files changed
+23 -6

No files matched your search

+4 -1
View File
@@ -18,5 +18,8 @@
"attached": "attached"
},
"forwardedFrom": "Forwarded from",
"replyingTo": "Replying to"
"replyingTo": "Replying to",
"error": {
"notConnected": "Not connected. Please wait for the connection to be established."
}
}
+4 -1
View File
@@ -18,5 +18,8 @@
"newChat": "Trò chuyện mới",
"openSessions": "Mở phiên",
"readOnly": "Chỉ đọc — phiên này thuộc về người dùng khác",
"replyingTo": "Đang trả lời"
"replyingTo": "Đang trả lời",
"error": {
"notConnected": "Chưa kết nối. Vui lòng chờ kết nối được thiết lập."
}
}
+4 -1
View File
@@ -18,5 +18,8 @@
"newChat": "新建对话",
"openSessions": "打开Session",
"readOnly": "只读 — 此Session属于其他用户",
"replyingTo": "回复"
"replyingTo": "回复",
"error": {
"notConnected": "未连接。请等待连接建立。"
}
}
+7 -1
View File
@@ -1,4 +1,5 @@
import { useState, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { useWs, useHttp } from "@/hooks/use-ws";
import { Methods } from "@/api/protocol";
import type { ChatMessage } from "@/types/chat";
@@ -25,6 +26,7 @@ export function useChatSend({
onMessageAdded,
onExpectRun,
}: UseChatSendOptions) {
const { t } = useTranslation("chat");
const ws = useWs();
const http = useHttp();
const [sending, setSending] = useState(false);
@@ -34,7 +36,11 @@ export function useChatSend({
async (message: string, sessionKey: string, files?: AttachedFile[]) => {
const hasMessage = message.trim().length > 0;
const hasFiles = files && files.length > 0;
if (!ws.isConnected || (!hasMessage && !hasFiles) || !sessionKey) return;
if (!ws.isConnected) {
setError(t("error.notConnected"));
return;
}
if ((!hasMessage && !hasFiles) || !sessionKey) return;
const trimmed = message.trim();
setError(null);
@@ -7,6 +7,7 @@ import { useAuthStore } from "@/stores/use-auth-store";
import { toast } from "@/stores/use-toast-store";
import i18next from "i18next";
import { userFriendlyError } from "@/lib/error-utils";
import { uniqueId } from "@/lib/utils";
/**
* Manages the session list for the chat sidebar.
@@ -45,7 +46,7 @@ export function useChatSessions(agentId: string) {
}, [loadSessions]);
const buildNewSessionKey = useCallback(() => {
const convId = crypto.randomUUID();
const convId = uniqueId();
return `agent:${agentId}:ws:direct:${convId}`;
}, [agentId]);
@@ -11,6 +11,7 @@ import {
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { validateSkillZip } from "./lib/validate-skill-zip";
import { uniqueId } from "@/lib/utils";
type FileStatus = "validating" | "valid" | "invalid" | "uploading" | "success" | "error";
@@ -46,7 +47,7 @@ export function SkillUploadDialog({ open, onOpenChange, onUpload }: SkillUploadD
if (fresh.length === 0) return;
const pending: FileEntry[] = fresh.map((f) => ({
id: crypto.randomUUID(),
id: uniqueId(),
file: f,
status: "validating" as const,
}));