mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-16 04:39:39 +02:00
Projects included: - maerchenzauber (NestJS backend + Expo mobile + SvelteKit web + Astro landing) - manacore (Expo mobile + SvelteKit web + Astro landing) - manadeck (NestJS backend + Expo mobile + SvelteKit web) - memoro (Expo mobile + SvelteKit web + Astro landing) This commit preserves the current state before monorepo restructuring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| context | ||
| core | ||
| platforms | ||
| services | ||
| store | ||
| types | ||
| utils | ||
| BACKGROUND_RECORDING_FIX.md | ||
| CODE_CHANGES.md | ||
| index.ts | ||
| IOS_PERMISSION_AUDIO_SESSION_FIX.md | ||
| KNOWLEDGE.md | ||
| README.md | ||
| TROUBLESHOOTING.md | ||
AudioRecordingV2
A modern, production-ready audio recording solution for React Native using Expo SDK 54.
Features
✅ Correct Expo SDK 54 API Usage
- Uses
AudioModule.AudioRecorderfrom internal API - Proper synchronous/asynchronous method handling
- Status polling for real-time updates
✅ Platform-Specific Optimizations
- Android: Foreground service, wake locks, Android 16 support
- iOS: Audio session management, background recording
- Web: Fallback support (basic implementation)
✅ Robust Error Handling
- Comprehensive error classification
- Automatic retry strategies
- User-friendly error messages
✅ State Management
- Zustand store integration
- Separate timer management
- Real-time status updates
Quick Start
Basic Usage
import { useAudioRecordingV2 } from '@/features/audioRecordingV2';
function RecordingComponent() {
const recording = useAudioRecordingV2();
const handleRecord = async () => {
if (!recording.permissions.microphone.granted) {
await recording.requestPermissions();
}
if (recording.status === 'idle') {
await recording.startRecording();
} else if (recording.status === 'recording') {
await recording.stopRecording();
}
};
return (
<View>
<Text>Status: {recording.status}</Text>
<Text>Duration: {recording.session?.duration || 0}s</Text>
<Button
title={recording.status === 'recording' ? 'Stop' : 'Record'}
onPress={handleRecord}
/>
</View>
);
}
Advanced Usage with Error Handling
import {
useAudioRecordingV2,
handleRecordingError,
RecordingPreset
} from '@/features/audioRecordingV2';
function AdvancedRecording() {
const recording = useAudioRecordingV2();
const startRecording = async () => {
try {
await recording.startRecording({
preset: RecordingPreset.HIGH_QUALITY,
maxDuration: 300, // 5 minutes
});
} catch (error) {
handleRecordingError(error, {
showAlert: true,
onRetry: startRecording,
});
}
};
// Rest of component...
}
Direct Service Usage (Without Store)
import { createPlatformRecordingService } from '@/features/audioRecordingV2';
// Create service instance
const recordingService = createPlatformRecordingService();
// Initialize
await recordingService.initialize();
// Start recording
await recordingService.startRecording();
// Stop and get URI
const uri = await recordingService.stopRecording();
// Cleanup when done
recordingService.cleanup();
API Reference
Store State
interface RecordingStoreState {
// State
status: RecordingStatus;
session: RecordingSession | null;
error: RecordingError | null;
permissions: PermissionState;
isInitialized: boolean;
// Actions
initialize(): Promise<void>;
startRecording(options?: RecordingOptions): Promise<void>;
stopRecording(): Promise<void>;
pauseRecording(): void;
resumeRecording(): void;
requestPermissions(): Promise<PermissionState>;
checkPermissions(): Promise<PermissionState>;
reset(): void;
setError(error: RecordingError | null): void;
}
Recording Options
interface RecordingOptions {
preset?: RecordingPreset;
format?: Partial<AudioFormat>;
maxDuration?: number; // seconds
sizeLimit?: number; // bytes
}
enum RecordingPreset {
HIGH_QUALITY = 'high_quality',
MEDIUM_QUALITY = 'medium_quality',
LOW_QUALITY = 'low_quality',
VOICE_MEMO = 'voice_memo'
}
Recording Status
enum RecordingStatus {
IDLE = 'idle',
PREPARING = 'preparing',
RECORDING = 'recording',
PAUSED = 'paused',
STOPPING = 'stopping',
STOPPED = 'stopped',
ERROR = 'error'
}
Platform-Specific Notes
Android
- Requires foreground service for background recording
- Android 16+ requires app to be in foreground to start recording
- Wake lock prevents device sleep during recording
- Notification shown during recording
iOS
- Background audio capability required in Info.plist (
UIBackgroundModes: ["audio"]) - Audio session configured with
mixWithOthersmode for background recording - Recording continues when app is backgrounded or user switches apps
- Handles real interruptions (phone calls, Siri) automatically
- Audio session verification on cold start prevents first-recording failures
Web
- Basic support using Web Audio API
- Limited functionality compared to mobile
Error Handling
The system includes comprehensive error handling:
enum RecordingErrorType {
PERMISSION_DENIED,
HARDWARE_UNAVAILABLE,
PLATFORM_RESTRICTION,
STORAGE_ERROR,
NETWORK_ERROR,
AUDIO_ENGINE_ERROR,
INITIALIZATION_ERROR,
UNKNOWN_ERROR
}
Each error includes:
- Type classification
- Error code
- User-friendly message
- Recoverable flag
- Retry strategy (if applicable)
- Platform-specific details
Troubleshooting
Android: Microphone not working
- Check permissions in app settings
- Ensure app is in foreground (Android 16+)
- Check if another app is using microphone
- Restart the app
iOS: Recording stops in background
Fixed in latest version! Background recording now works correctly.
Root Cause: Two bugs prevented background recording:
interruptionMode: 'doNotMix'- iOS revoked exclusive audio access when backgrounded- App state handler manually paused recording on
inactivestate transition
Solution:
- Changed to
interruptionMode: 'mixWithOthers'- allows background recording - Removed manual pause logic - let iOS handle audio naturally
Verification:
- Recording continues when pressing home button
- Recording continues when switching to other apps
- Recording continues when device is locked
- Full duration captured (foreground + background time)
See TROUBLESHOOTING.md for detailed technical explanation.
Zero-byte recordings
This is a known issue with Expo SDK 54 on some Android devices. The implementation includes:
- File validation after recording
- Automatic retry mechanism
- Error reporting for debugging
Migration from V1
If migrating from the old recording system:
// Old
import audioRecordingService from '@/features/audioRecording/audioRecording.service';
await audioRecordingService.startRecording();
// New
import { useAudioRecordingV2 } from '@/features/audioRecordingV2';
const recording = useAudioRecordingV2();
await recording.startRecording();
Development
Testing
# Run tests
npm test features/audioRecordingV2
# Test on Android
npx expo run:android
# Test on iOS
npx expo run:ios
Debugging
Enable debug logs:
// In AudioEngineService.ts
console.log('Debug:', status);
Contributing
When making changes:
- Test on both Android and iOS
- Verify Android 16 compatibility
- Check memory leaks with profiler
- Update types if API changes
License
Internal use only