From 764c149e2e7abbc912c68eaafe517a5c0de9d2b4 Mon Sep 17 00:00:00 2001 From: Sergei Date: Tue, 27 Jan 2026 16:30:20 -0800 Subject: [PATCH] Add toggle listening mode on VoiceFAB tap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FAB now toggles between idle and listening states - Green background (idle) → Red background (listening) - Icon switches between mic-outline and mic - Connects to VoiceContext for state management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/(tabs)/_layout.tsx | 25 ++++++++++++++----------- components/VoiceFAB.tsx | 18 +++++++++++++----- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index d7e8d14..007d12e 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -1,6 +1,6 @@ import { Tabs } from 'expo-router'; import React, { useCallback } from 'react'; -import { Platform, View, Alert } from 'react-native'; +import { Platform, View } from 'react-native'; import { Feather } from '@expo/vector-icons'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -9,6 +9,7 @@ import { VoiceFAB } from '@/components/VoiceFAB'; import { AppColors } from '@/constants/theme'; import { useColorScheme } from '@/hooks/use-color-scheme'; import { useVoiceCall } from '@/contexts/VoiceCallContext'; +import { useVoice } from '@/contexts/VoiceContext'; export default function TabLayout() { const colorScheme = useColorScheme(); @@ -17,15 +18,17 @@ export default function TabLayout() { // VoiceFAB uses VoiceCallContext internally to hide when call is active useVoiceCall(); // Ensure context is available - // Handle voice FAB press - initiate voice call + // Voice context for listening mode toggle + const { isListening, startSession, stopSession } = useVoice(); + + // Handle voice FAB press - toggle listening mode const handleVoiceFABPress = useCallback(() => { - // TODO: Integrate with voice call service when ready - Alert.alert( - 'Voice Call', - 'Voice call with Julia AI will be available soon.', - [{ text: 'OK' }] - ); - }, []); + if (isListening) { + stopSession(); + } else { + startSession(); + } + }, [isListening, startSession, stopSession]); // Calculate tab bar height based on safe area // On iOS with home indicator, insets.bottom is ~34px @@ -116,8 +119,8 @@ export default function TabLayout() { /> - {/* Voice FAB - shown when no call is active */} - + {/* Voice FAB - toggle listening mode */} + ); } diff --git a/components/VoiceFAB.tsx b/components/VoiceFAB.tsx index e90f24c..8baebe5 100644 --- a/components/VoiceFAB.tsx +++ b/components/VoiceFAB.tsx @@ -1,8 +1,8 @@ /** * Voice Floating Action Button Component * - * A floating action button for initiating voice calls with Julia AI. - * Shows on screens where voice chat is available. + * A floating action button for toggling voice listening mode. + * Tap to start/stop listening. * Hidden when a call is already active. */ @@ -23,11 +23,12 @@ interface VoiceFABProps { onPress: () => void; style?: ViewStyle; disabled?: boolean; + isListening?: boolean; } const FAB_SIZE = 56; -export function VoiceFAB({ onPress, style, disabled = false }: VoiceFABProps) { +export function VoiceFAB({ onPress, style, disabled = false, isListening = false }: VoiceFABProps) { const { isCallActive } = useVoiceCall(); const insets = useSafeAreaInsets(); @@ -103,7 +104,11 @@ export function VoiceFAB({ onPress, style, disabled = false }: VoiceFABProps) { ]} > @@ -141,6 +146,9 @@ const styles = StyleSheet.create({ shadowRadius: 8, elevation: 8, }, + fabListening: { + backgroundColor: AppColors.error, + }, fabDisabled: { backgroundColor: AppColors.surface, shadowOpacity: 0.1,