From 88d4afcdfd75fa720de5b54f84cb4581924d91b3 Mon Sep 17 00:00:00 2001 From: Sergei Date: Tue, 27 Jan 2026 16:41:14 -0800 Subject: [PATCH] Display Julia's voice responses in chat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When user speaks via voice mode, both their question and Julia's response are now shown in the text chat. This provides a unified conversation history for both voice and text interactions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/(tabs)/chat.tsx | 22 ++++++++++++++++++++++ contexts/VoiceContext.tsx | 12 +++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/app/(tabs)/chat.tsx b/app/(tabs)/chat.tsx index 5944961..c5dced9 100644 --- a/app/(tabs)/chat.tsx +++ b/app/(tabs)/chat.tsx @@ -24,6 +24,7 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import { useRouter, useFocusEffect } from 'expo-router'; import { api } from '@/services/api'; import { useBeneficiary } from '@/contexts/BeneficiaryContext'; +import { useVoiceTranscript } from '@/contexts/VoiceTranscriptContext'; import { AppColors, BorderRadius, FontSizes, Spacing } from '@/constants/theme'; import type { Message, Beneficiary } from '@/types'; @@ -109,6 +110,7 @@ function normalizeQuestion(userMessage: string): string { export default function ChatScreen() { const router = useRouter(); const { currentBeneficiary, setCurrentBeneficiary } = useBeneficiary(); + const { transcript, hasNewTranscript, markTranscriptAsShown, getTranscriptAsMessages } = useVoiceTranscript(); // Helper to create initial message with beneficiary name const createInitialMessage = useCallback((beneficiaryName?: string | null): Message => ({ @@ -195,6 +197,26 @@ export default function ChatScreen() { } }, [deploymentName, createInitialMessage]); + // Add voice transcript messages to chat when new ones arrive + useEffect(() => { + if (hasNewTranscript && transcript.length > 0) { + const voiceMessages = getTranscriptAsMessages(); + if (voiceMessages.length > 0) { + setMessages(prev => { + // Filter out messages that are already in the chat (by id) + const existingIds = new Set(prev.map(m => m.id)); + const newMessages = voiceMessages.filter(m => !existingIds.has(m.id)); + if (newMessages.length > 0) { + console.log('[Chat] Adding', newMessages.length, 'voice messages to chat'); + return [...prev, ...newMessages]; + } + return prev; + }); + } + markTranscriptAsShown(); + } + }, [hasNewTranscript, transcript, getTranscriptAsMessages, markTranscriptAsShown]); + // Load beneficiaries const loadBeneficiaries = useCallback(async () => { setLoadingBeneficiaries(true); diff --git a/contexts/VoiceContext.tsx b/contexts/VoiceContext.tsx index a32bbb6..bfa8716 100644 --- a/contexts/VoiceContext.tsx +++ b/contexts/VoiceContext.tsx @@ -19,6 +19,7 @@ import React, { } from 'react'; import * as Speech from 'expo-speech'; import { api } from '@/services/api'; +import { useVoiceTranscript } from './VoiceTranscriptContext'; // WellNuo API configuration (same as chat.tsx) const API_URL = 'https://eluxnetworks.net/function/well-api/api'; @@ -146,6 +147,9 @@ export function VoiceProvider({ children }: { children: ReactNode }) { const [isListening, setIsListening] = useState(false); const [isSpeaking, setIsSpeaking] = useState(false); + // Voice transcript context for chat display + const { addTranscriptEntry } = useVoiceTranscript(); + // API token cache const apiTokenRef = useRef(null); @@ -207,6 +211,9 @@ export function VoiceProvider({ children }: { children: ReactNode }) { setStatus('processing'); setError(null); + // Add user message to transcript for chat display + addTranscriptEntry('user', trimmedText); + try { // Get API token const token = await getWellNuoToken(); @@ -246,6 +253,9 @@ export function VoiceProvider({ children }: { children: ReactNode }) { console.log('[VoiceContext] API response:', responseText.slice(0, 100) + '...'); setLastResponse(responseText); + // Add Julia's response to transcript for chat display + addTranscriptEntry('assistant', responseText); + // Speak the response await speak(responseText); @@ -266,7 +276,7 @@ export function VoiceProvider({ children }: { children: ReactNode }) { return null; } }, - [getWellNuoToken] + [getWellNuoToken, addTranscriptEntry] ); /**