refactor: optimize audio initialization and fix agent interactions

- Lazy initialize audio (on-demand instead of mount) in both player and Speechmatics hooks
- Add audio session teardown on stop for proper cleanup
- Implement agent audio recording with sendAgentAudio in AgentInputArea
- Fix Android navigation race condition in CreateAgentModal (navigate before closing)
- Add animations to global footer and agent screen (FadeIn/FadeOut)
- Auto-cancel stuck agents when sending new messages
- Fix permission request ID propagation from AgentManager to Session
- Apply theme background color via AppContainer wrapper

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Mohamed Boudra
2025-10-23 13:15:18 +02:00
co-authored by Claude
parent 10189514dd
commit 74fffcd8a3
11 changed files with 139 additions and 76 deletions
+6 -16
View File
@@ -1,4 +1,4 @@
import { useState, useRef, useEffect } from 'react';
import { useState, useRef } from 'react';
import { initialize, playPCMData } from '@speechmatics/expo-two-way-audio';
interface QueuedAudio {
@@ -45,20 +45,6 @@ export function useAudioPlayer() {
const queueRef = useRef<QueuedAudio[]>([]);
const isProcessingQueueRef = useRef(false);
// Initialize Speechmatics audio on mount
useEffect(() => {
const initializeAudio = async () => {
try {
await initialize();
setAudioInitialized(true);
console.log('[AudioPlayer] ✅ Initialized (Speechmatics two-way audio)');
} catch (error) {
console.error('[AudioPlayer] Failed to initialize audio:', error);
}
};
initializeAudio();
}, []);
async function play(audioData: Blob): Promise<number> {
return new Promise((resolve, reject) => {
// Add to queue with its promise handlers
@@ -112,8 +98,12 @@ export function useAudioPlayer() {
`[AudioPlayer] Playing audio (${audioData.size} bytes, type: ${audioData.type})`
);
// Initialize audio if not already initialized
if (!audioInitialized) {
throw new Error('Audio not initialized');
console.log('[AudioPlayer] Initializing audio...');
await initialize();
setAudioInitialized(true);
console.log('[AudioPlayer] ✅ Initialized (Speechmatics two-way audio)');
}
// Get PCM data from blob (server now sends PCM format)
@@ -149,26 +149,6 @@ export function useSpeechmaticsAudio(
const SPEECH_CONFIRMATION_MS = config.speechConfirmationDuration;
const DETECTION_GRACE_PERIOD_MS = config.detectionGracePeriod;
// Initialize audio on mount
useEffect(() => {
const initializeAudio = async () => {
try {
await initialize();
setAudioInitialized(true);
console.log("[SpeechmaticsAudio] Audio initialized");
} catch (error) {
console.error("[SpeechmaticsAudio] Initialization error:", error);
const err = error instanceof Error ? error : new Error(String(error));
config.onError?.(err);
}
};
initializeAudio();
return () => {
tearDown();
};
}, []);
// Update segment duration timer
useEffect(() => {
if (!isDetecting && !isSpeaking) {
@@ -314,16 +294,20 @@ export function useSpeechmaticsAudio(
);
async function start(): Promise<void> {
if (!audioInitialized) {
throw new Error("Audio not initialized");
}
if (isActive) {
console.log("[SpeechmaticsAudio] Already active");
return;
}
try {
// Initialize audio if not already initialized
if (!audioInitialized) {
console.log("[SpeechmaticsAudio] Initializing audio...");
await initialize();
setAudioInitialized(true);
console.log("[SpeechmaticsAudio] Audio initialized");
}
console.log("[SpeechmaticsAudio] Starting audio capture...");
// Start recording
@@ -344,7 +328,16 @@ export function useSpeechmaticsAudio(
console.log("[SpeechmaticsAudio] Stopping audio capture...");
// Stop recording
toggleRecording(false);
if (isActive) {
toggleRecording(false);
}
// Tear down audio session
if (audioInitialized) {
tearDown();
setAudioInitialized(false);
console.log("[SpeechmaticsAudio] Audio torn down");
}
// Reset state
audioBufferRef.current = [];
@@ -383,15 +376,6 @@ export function useSpeechmaticsAudio(
});
}
// Cleanup on unmount
useEffect(() => {
return () => {
if (isActive) {
stop();
}
};
}, [isActive]);
return {
start,
stop,