From 8312d08653fe53767b76e26367074bc8e14f5eea Mon Sep 17 00:00:00 2001 From: chenhang Date: Fri, 10 Jul 2026 11:15:59 +0800 Subject: [PATCH] fix(chat): apply unlocked voice audio url --- src/app/chat/components/message-content.tsx | 5 ++-- src/app/chat/components/voice-bubble.tsx | 9 +++---- .../__tests__/unlock_private_response.test.ts | 4 ++++ .../chat/response/unlock_private_response.ts | 1 + .../repositories/chat_local_message_store.ts | 24 ++++++++++++++++--- src/data/repositories/chat_repository.ts | 6 ++--- .../interfaces/ichat_repository.ts | 8 ++++++- .../schemas/chat/unlock_private_response.ts | 1 + .../__tests__/chat-machine.helpers.test.ts | 11 ++++----- .../chat-machine.transitions.test.ts | 16 +++++++++---- src/stores/chat/chat-unlock-flow.ts | 6 ++++- src/stores/chat/chat-unlock-helpers.ts | 10 ++++++++ 12 files changed, 76 insertions(+), 25 deletions(-) diff --git a/src/app/chat/components/message-content.tsx b/src/app/chat/components/message-content.tsx index ea0e007b..54deabef 100644 --- a/src/app/chat/components/message-content.tsx +++ b/src/app/chat/components/message-content.tsx @@ -47,6 +47,7 @@ export function MessageContent({ lockedPrivate === true || (locked === true && lockReason === "private_message"); const isLockedVoiceMessage = locked === true && lockReason === "voice_message"; + const shouldRenderVoiceMessage = hasAudio || isLockedVoiceMessage; const handleUnlockPrivateMessage = messageId && onUnlockPrivateMessage ? () => onUnlockPrivateMessage(messageId) @@ -79,7 +80,7 @@ export function MessageContent({ onOpenImage={onOpenImage} /> )} - {hasAudio && audioUrl ? ( + {shouldRenderVoiceMessage ? ( ) : null} - {hasText && !hasAudio ? ( + {hasText && !shouldRenderVoiceMessage ? ( ) : null} diff --git a/src/app/chat/components/voice-bubble.tsx b/src/app/chat/components/voice-bubble.tsx index fb0792a7..b126e9cc 100644 --- a/src/app/chat/components/voice-bubble.tsx +++ b/src/app/chat/components/voice-bubble.tsx @@ -8,7 +8,7 @@ import styles from "./voice-bubble.module.css"; export interface VoiceBubbleProps { messageId?: string; - audioUrl: string; + audioUrl?: string | null; isFromAI: boolean; locked?: boolean; hint?: string | null; @@ -28,6 +28,7 @@ export function VoiceBubble({ const audioRef = useRef(null); const [isPlaying, setIsPlaying] = useState(false); const [errorMediaUrl, setErrorMediaUrl] = useState(null); + const hasAudio = audioUrl != null && audioUrl.length > 0; const { mediaUrl, isUsingCachedMedia, reportMediaError } = useCachedChatMediaUrl({ messageId, @@ -66,7 +67,7 @@ export function VoiceBubble({ const hasError = errorMediaUrl === mediaUrl; const handleToggle = () => { - if (locked) return; + if (locked || !hasAudio) return; const audio = audioRef.current; if (!audio || hasError) return; @@ -92,7 +93,7 @@ export function VoiceBubble({ type="button" className={styles.playButton} onClick={handleToggle} - disabled={locked || hasError} + disabled={locked || hasError || !hasAudio} aria-label={isPlaying ? "Pause voice message" : "Play voice message"} > {locked ? ( @@ -132,7 +133,7 @@ export function VoiceBubble({ ) : null} - {!locked ? ( + {!locked && hasAudio ? (