From 78e41b9221674cf956b41c8395b2d37e70b5a040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20S=C4=99k?= Date: Thu, 15 Jan 2026 12:00:14 +0100 Subject: [PATCH] fix: bring back demo recorder playback ui --- apps/common-app/src/demos/Record/Record.tsx | 68 +++++++++++++++++-- .../demos/Record/RecordingVisualization.tsx | 2 +- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/apps/common-app/src/demos/Record/Record.tsx b/apps/common-app/src/demos/Record/Record.tsx index 9f3f1b6b3..5e52038c3 100644 --- a/apps/common-app/src/demos/Record/Record.tsx +++ b/apps/common-app/src/demos/Record/Record.tsx @@ -1,5 +1,6 @@ import React, { FC, useCallback, useEffect, useState } from 'react'; import { + AudioBuffer, AudioManager, RecordingNotificationManager, } from 'react-native-audio-api'; @@ -7,8 +8,10 @@ import { import { Alert, StyleSheet, View } from 'react-native'; import { Container } from '../../components'; -import { audioRecorder as Recorder } from '../../singletons'; +import { Easing, useSharedValue, withTiming } from 'react-native-reanimated'; +import { audioRecorder as Recorder, audioContext } from '../../singletons'; import ControlPanel from './ControlPanel'; +import PlaybackVisualization from './PlaybackVisualization'; import RecordingTime from './RecordingTime'; import RecordingVisualization from './RecordingVisualization'; import Status from './Status'; @@ -23,6 +26,10 @@ AudioManager.setAudioSessionOptions({ const Record: FC = () => { const [state, setState] = useState(RecordingState.Idle); const [hasPermissions, setHasPermissions] = useState(false); + const [recordedBuffer, setRecordedBuffer] = useState( + null + ); + const currentPositionSV = useSharedValue(0); const setNotification = (paused: boolean) => { RecordingNotificationManager.show({ @@ -87,10 +94,19 @@ const Record: FC = () => { setState(RecordingState.Recording); }, []); - const onStopRecording = useCallback(() => { - Recorder.stop(); + const onStopRecording = useCallback(async () => { + const info = Recorder.stop(); RecordingNotificationManager.hide(); setState(RecordingState.ReadyToPlay); + + if (info.status !== 'success') { + Alert.alert('Error', `Failed to stop recording: ${info.message}`); + setRecordedBuffer(null); + return; + } + + const audioBuffer = await audioContext.decodeAudioData(info.path); + setRecordedBuffer(audioBuffer); }, []); const onPlayRecording = useCallback(() => { @@ -98,8 +114,31 @@ const Record: FC = () => { return; } + if (!recordedBuffer) { + Alert.alert('Error', 'No recorded audio to play.'); + return; + } + + const source = audioContext.createBufferSource(); + source.buffer = recordedBuffer; + source.connect(audioContext.destination); + source.start(audioContext.currentTime + 0.1); + + source.onEnded = () => { + setState(RecordingState.Idle); + }; + + setTimeout(() => { + currentPositionSV.value = 0; + + withTiming(recordedBuffer.duration, { + duration: recordedBuffer.duration * 1000, + easing: Easing.linear, + }); + }, 100); + setState(RecordingState.Playing); - }, [state]); + }, [state, recordedBuffer, currentPositionSV]); const onToggleState = useCallback( (action: RecordingState) => { @@ -187,6 +226,9 @@ const Record: FC = () => { return () => { Recorder.disableFileOutput(); + Recorder.stop(); + AudioManager.setAudioSessionActivity(false); + RecordingNotificationManager.hide(); }; }, []); @@ -194,9 +236,21 @@ const Record: FC = () => { - - - + {[RecordingState.Playing, RecordingState.ReadyToPlay].includes(state) ? ( + <> + + + ) : ( + <> + + + + + )} diff --git a/apps/common-app/src/demos/Record/RecordingVisualization.tsx b/apps/common-app/src/demos/Record/RecordingVisualization.tsx index 0bc3211da..88a2c00d0 100644 --- a/apps/common-app/src/demos/Record/RecordingVisualization.tsx +++ b/apps/common-app/src/demos/Record/RecordingVisualization.tsx @@ -257,7 +257,7 @@ const RecordingVisualization: React.FC = ({ if (barHeights.value.length !== numBars) { barHeights.value = new Array(numBars).fill(-1); } - }, [numBars]); + }, [numBars, barHeights]); useEffect(() => { if (numBars <= 0) {