From 2a002697aa4bb16aab085f85f42c88f19e23cc56 Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Fri, 10 Jul 2026 14:56:34 +0800 Subject: [PATCH] Enhance MobileCallPage with chat transcript feature - Introduce a new `MobileCallTranscript` component to display chat messages during calls, improving user interaction. - Implement message timestamp formatting and automatic scrolling for better usability. - Add a toggle button to switch between camera view and chat view, enhancing the user experience during calls. - Refactor the layout of the `MobileCallPage` to accommodate the new chat functionality while maintaining existing video call features. --- .../src/components/pages/MobileCallPage.tsx | 128 ++++++++++++++++-- 1 file changed, 117 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/pages/MobileCallPage.tsx b/frontend/src/components/pages/MobileCallPage.tsx index 3585edf..1ddd137 100644 --- a/frontend/src/components/pages/MobileCallPage.tsx +++ b/frontend/src/components/pages/MobileCallPage.tsx @@ -1,7 +1,16 @@ "use client"; -import { useCallback, useEffect, useRef } from "react"; -import { Camera, Headphones, Loader2, Mic, Phone, PhoneOff, Video } from "lucide-react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { + Camera, + Headphones, + Loader2, + MessageSquareText, + Mic, + Phone, + PhoneOff, + Video, +} from "lucide-react"; import { Button } from "@/components/ui/button"; import { DropdownMenu, @@ -20,7 +29,81 @@ import { SelectValue, } from "@/components/ui/select"; import { useCameraPreview } from "@/hooks/use-camera-preview"; -import { useVoicePreview } from "@/hooks/use-voice-preview"; +import { + useVoicePreview, + type ChatMessage, +} from "@/hooks/use-voice-preview"; + +type CallView = "camera" | "chat"; + +function messageTime(timestamp: string): string { + const date = new Date(timestamp); + if (Number.isNaN(date.getTime())) return ""; + return date.toLocaleTimeString("zh-CN", { + hour: "2-digit", + minute: "2-digit", + hour12: false, + }); +} + +function MobileCallTranscript({ messages }: { messages: ChatMessage[] }) { + const scrollRef = useRef(null); + + useEffect(() => { + const container = scrollRef.current; + if (container) container.scrollTop = container.scrollHeight; + }, [messages]); + + return ( +
+
+ {messages.length === 0 ? ( +
+ +

暂无聊天记录

+

+ 通话中的语音转写和助手回复会实时显示在这里。 +

+
+ ) : ( +
+ {messages.map((message) => { + const time = messageTime(message.timestamp); + const isAssistant = message.role === "assistant"; + return ( +
+ + {isAssistant ? "助手" : "我"} + {time ? ` · ${time}` : ""} + +
+ {message.content || (message.streaming ? "…" : "")} +
+
+ ); + })} +
+ )} +
+
+ ); +} function CallAudioDeviceSelect({ micValue, @@ -163,6 +246,7 @@ export function MobileCallPage({ assistantId }: { assistantId: string }) { selectDevice, selectOutputDevice, supportsOutputSelection, + messages, connect, replaceVideoStream, disconnect, @@ -179,6 +263,7 @@ export function MobileCallPage({ assistantId }: { assistantId: string }) { selectCamera: changeCamera, } = camera; const videoRef = useRef(null); + const [view, setView] = useState("camera"); const connecting = status === "connecting"; const inCall = connecting || status === "connected"; @@ -219,22 +304,28 @@ export function MobileCallPage({ assistantId }: { assistantId: string }) { const error = previewError || cameraError; return ( -
+
); }