diff --git a/README.md b/README.md index 95d69d21..64599fa5 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ npm install react-audio-voice-recorder yarn add react-audio-voice-recorder ``` +## Version 2.3.0 +- Added support to NextJs 15 + ## Migrating from v1 → v2 ### Breaking changes - In v2 the `AudioRecorder` prop `downloadFileExtension` no longer supports `mp3` and `wav` without the website using this package being [cross-origin isolated](https://web.dev/cross-origin-isolation-guide/). This change was made in order to fix [issue #54](https://github.com/samhirtarif/react-audio-recorder/issues/54) in v1.2.1 @@ -88,6 +91,7 @@ The hook returns the following: | :------------ |:---------------| | **`startRecording`** | Invoking this method starts the recording. Sets `isRecording` to `true` | | **`stopRecording`** | Invoking this method stops the recording in progress and the resulting audio is made available in `recordingBlob`. Sets `isRecording` to `false` | +| **`cancelRecording`** | Invoking this method cancels the recording in progress and discard audio. Sets `isRecording` to `false` | | **`togglePauseResume`** | Invoking this method would pause the recording if it is currently running or resume if it is paused. Toggles the value `isPaused` | | **`recordingBlob`** | This is the recording blob that is created after `stopRecording` has been called | | **`isRecording`** | A boolean value that represents whether a recording is currently in progress | diff --git a/package-lock.json b/package-lock.json index fa411c42..3be83117 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "react-audio-voice-recorder", - "version": "2.2.0", + "name": "@rafaelmarreca/react-audio-voice-recorder", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "react-audio-voice-recorder", - "version": "2.2.0", + "name": "@rafaelmarreca/react-audio-voice-recorder", + "version": "0.1.0", "license": "MIT", "dependencies": { "@ffmpeg/ffmpeg": "^0.11.6", diff --git a/package.json b/package.json index 122ad9e8..7471d436 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { - "name": "react-audio-voice-recorder", + "name": "@rafaelmarreca/react-audio-voice-recorder", "private": false, - "version": "2.2.0", + "version": "0.1.0", "type": "module", "license": "MIT", - "author": "Samhir Tarif", + "author": "Rafael Marreca", "repository": { "type": "git", - "url": "https://github.com/samhirtarif/react-audio-recorder.git" + "url": "https://github.com/rafael145a/react-audio-recorder.git" }, "keywords": [ "react", diff --git a/src/components/AudioRecordingComponent.tsx b/src/components/AudioRecordingComponent.tsx index 8b376d38..ea24a40d 100644 --- a/src/components/AudioRecordingComponent.tsx +++ b/src/components/AudioRecordingComponent.tsx @@ -8,11 +8,7 @@ import resumeSVG from "../icons/play.svg"; import saveSVG from "../icons/save.svg"; import discardSVG from "../icons/stop.svg"; import "../styles/audio-recorder.css"; - -const LiveAudioVisualizer = React.lazy(async () => { - const { LiveAudioVisualizer } = await import("react-audio-visualize"); - return { default: LiveAudioVisualizer }; -}); +import LiveAudioVisualizer from "./ReactAudioVisualizerWrapper"; /** * Usage: https://github.com/samhirtarif/react-audio-recorder#audiorecorder-component @@ -43,9 +39,11 @@ const AudioRecorder: (props: Props) => ReactElement = ({ startRecording, stopRecording, togglePauseResume, + cancelRecording, recordingBlob, isRecording, isPaused, + isCancelled, recordingTime, mediaRecorder, } = @@ -62,6 +60,9 @@ const AudioRecorder: (props: Props) => ReactElement = ({ const stopAudioRecorder: (save?: boolean) => void = ( save: boolean = true ) => { + if (!save) { + cancelRecording(); + } setShouldSave(save); stopRecording(); }; @@ -116,6 +117,7 @@ const AudioRecorder: (props: Props) => ReactElement = ({ useEffect(() => { if ( + !isCancelled && (shouldSave || recorderControls) && recordingBlob != null && onRecordingComplete != null @@ -127,6 +129,10 @@ const AudioRecorder: (props: Props) => ReactElement = ({ } }, [recordingBlob]); + useEffect(() => { + stopRecording(); + }, [isCancelled]); + return (
{ + try { + // Tenta importar de forma segura + const module = await import("react-audio-visualize"); + return module.LiveAudioVisualizer; + } catch (error) { + console.error("Error loading LiveAudioVisualizer:", error); + // Retorna um componente vazio em caso de erro + return () => null; + } +}; + +// Componente wrapper que carrega o visualizador sob demanda +const DynamicLiveAudioVisualizer = (props: any) => { + const [Component, setComponent] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + let isMounted = true; + + const load = async () => { + const VisualizerComponent = await loadVisualizer(); + if (isMounted) { + // @ts-expect-error + setComponent(() => VisualizerComponent); + setLoading(false); + } + }; + + void load(); + + return () => { + isMounted = false; + }; + }, []); + + if (loading || !Component) { + return null; + } + // @ts-expect-error + return ; +}; + +export default DynamicLiveAudioVisualizer; diff --git a/src/hooks/useAudioRecorder.ts b/src/hooks/useAudioRecorder.ts index 18db0eab..8573b47b 100644 --- a/src/hooks/useAudioRecorder.ts +++ b/src/hooks/useAudioRecorder.ts @@ -4,9 +4,11 @@ export interface recorderControls { startRecording: () => void; stopRecording: () => void; togglePauseResume: () => void; + cancelRecording: () => void; recordingBlob?: Blob; isRecording: boolean; isPaused: boolean; + isCancelled: boolean; recordingTime: number; mediaRecorder?: MediaRecorder; } @@ -49,6 +51,7 @@ const useAudioRecorder: ( ) => { const [isRecording, setIsRecording] = useState(false); const [isPaused, setIsPaused] = useState(false); + const [isCancelled, setIsCancelled] = useState(false); const [recordingTime, setRecordingTime] = useState(0); const [mediaRecorder, setMediaRecorder] = useState(); const [timerInterval, setTimerInterval] = useState(); @@ -71,7 +74,7 @@ const useAudioRecorder: ( */ const startRecording: () => void = useCallback(() => { if (timerInterval != null) return; - + setIsCancelled(false); navigator.mediaDevices .getUserMedia({ audio: audioTrackConstraints ?? true }) .then((stream) => { @@ -136,13 +139,23 @@ const useAudioRecorder: ( } }, [mediaRecorder, setIsPaused, _startTimer, _stopTimer]); + /** + * Calling this method would cancel the recording and discard + */ + const cancelRecording: () => void = useCallback(() => { + setIsCancelled(true); + setIsRecording(false); + }, []); + return { startRecording, stopRecording, togglePauseResume, + cancelRecording, recordingBlob, isRecording, isPaused, + isCancelled, recordingTime, mediaRecorder, }; diff --git a/src/main.tsx b/src/main.tsx index 35545fc2..ad2c8a06 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,6 +1,7 @@ import React from "react"; import ReactDOM from "react-dom/client"; import AudioRecorder from "./components/AudioRecordingComponent"; +import useAudioRecorder from "./hooks/useAudioRecorder"; const addAudioElement = (blob: Blob) => { const url = URL.createObjectURL(blob); @@ -10,8 +11,10 @@ const addAudioElement = (blob: Blob) => { document.body.appendChild(audio); }; -ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - +function AudioRecorderComp() { + const { isRecording, stopRecording, cancelRecording, ...recordControls } = useAudioRecorder() + + return (<> addAudioElement(blob)} // audioTrackConstraints={{ @@ -20,8 +23,16 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( // }} onNotAllowedOrFound={(err) => console.table(err)} showVisualizer={true} - downloadOnSavePress - downloadFileExtension="mp3" + recorderControls={{ isRecording, stopRecording, cancelRecording, ...recordControls}} /> + + + + ) +} + +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( + + );