feat: Display voice messages in chat in real-time

Voice transcripts from both user and Julia now appear in the chat
immediately during the call, not just after it ends. Each voice call
shows a "Voice Call" separator, and voice messages display with a
microphone icon indicator.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Sergei 2026-01-24 20:43:24 -08:00
parent 560722e8af
commit 09fc6ce8ad

View File

@ -387,7 +387,7 @@ const voiceStyles = StyleSheet.create({
export default function ChatScreen() { export default function ChatScreen() {
const router = useRouter(); const router = useRouter();
const { currentBeneficiary, setCurrentBeneficiary } = useBeneficiary(); const { currentBeneficiary, setCurrentBeneficiary } = useBeneficiary();
const { getTranscriptAsMessages, hasNewTranscript, markTranscriptAsShown, addTranscriptEntry, clearTranscript } = useVoiceTranscript(); const { addTranscriptEntry, clearTranscript } = useVoiceTranscript();
const { user } = useAuth(); const { user } = useAuth();
const { const {
callState, callState,
@ -412,30 +412,17 @@ export default function ChatScreen() {
// Voice call state (local connecting state only) // Voice call state (local connecting state only)
const [isConnectingVoice, setIsConnectingVoice] = useState(false); const [isConnectingVoice, setIsConnectingVoice] = useState(false);
// Add voice call transcript to messages when returning from call // Track if we've shown the voice call separator for current call
const [hasShownVoiceSeparator, setHasShownVoiceSeparator] = useState(false);
// Reset separator flag when starting a new call
useEffect(() => { useEffect(() => {
if (hasNewTranscript) { if (isCallActive && !hasShownVoiceSeparator) {
const transcriptMessages = getTranscriptAsMessages(); // Will show separator on first voice message
if (transcriptMessages.length > 0) { } else if (!isCallActive) {
// Add a separator message setHasShownVoiceSeparator(false);
const separatorMessage: Message = {
id: `voice-separator-${Date.now()}`,
role: 'assistant',
content: '--- Voice Call Transcript ---',
timestamp: new Date(),
isSystem: true,
};
setMessages(prev => [...prev, separatorMessage, ...transcriptMessages]);
markTranscriptAsShown();
// Scroll to bottom
setTimeout(() => {
flatListRef.current?.scrollToEnd({ animated: true });
}, 100);
} }
} }, [isCallActive]);
}, [hasNewTranscript, getTranscriptAsMessages, markTranscriptAsShown]);
const [input, setInput] = useState(''); const [input, setInput] = useState('');
const [isSending, setIsSending] = useState(false); const [isSending, setIsSending] = useState(false);
const flatListRef = useRef<FlatList>(null); const flatListRef = useRef<FlatList>(null);
@ -559,10 +546,42 @@ export default function ChatScreen() {
endVoiceCallContext(); endVoiceCallContext();
}, [endVoiceCallContext]); }, [endVoiceCallContext]);
// Handle voice transcript entries // Handle voice transcript entries - add to chat in real-time
const handleVoiceTranscript = useCallback((role: 'user' | 'assistant', text: string) => { const handleVoiceTranscript = useCallback((role: 'user' | 'assistant', text: string) => {
if (!text.trim()) return;
// Add separator before first voice message of this call
if (!hasShownVoiceSeparator) {
const separatorMessage: Message = {
id: `voice-separator-${Date.now()}`,
role: 'assistant',
content: 'Voice Call',
timestamp: new Date(),
isSystem: true,
};
setMessages(prev => [...prev, separatorMessage]);
setHasShownVoiceSeparator(true);
}
// Create voice message and add to chat immediately
const voiceMessage: Message = {
id: `voice-${Date.now()}-${Math.random().toString(36).slice(2)}`,
role,
content: text.trim(),
timestamp: new Date(),
isVoice: true,
};
setMessages(prev => [...prev, voiceMessage]);
// Scroll to bottom
setTimeout(() => {
flatListRef.current?.scrollToEnd({ animated: true });
}, 100);
// Also store in transcript context for persistence
addTranscriptEntry(role, text); addTranscriptEntry(role, text);
}, [addTranscriptEntry]); }, [hasShownVoiceSeparator, addTranscriptEntry]);
// Cached API token for WellNuo // Cached API token for WellNuo
const apiTokenRef = useRef<string | null>(null); const apiTokenRef = useRef<string | null>(null);