-
-
Save cassidoo/dd1190c248d60c723de14fe9ee32f450 to your computer and use it in GitHub Desktop.
"use client"; | |
import { useState, useEffect, useRef } from "react"; | |
function SimpleRecordButton() { | |
const [isRecording, setIsRecording] = useState(false); | |
const [audioStream, setAudioStream] = useState(null); | |
const [mediaRecorder, setMediaRecorder] = useState(null); | |
const [audioBlob, setAudioBlob] = useState(null); | |
const [recordingTime, setRecordingTime] = useState(0); | |
const timerRef = useRef(null); | |
const RECORDING_MAX_DURATION = 240; // 4 minutes in seconds | |
useEffect(() => { | |
if (!audioStream) { | |
navigator.mediaDevices | |
.getUserMedia({ audio: true }) | |
.then((stream) => { | |
setAudioStream(stream); | |
const mediaRecorder = new MediaRecorder(stream); | |
setMediaRecorder(mediaRecorder); | |
let audio; | |
mediaRecorder.ondataavailable = (event) => { | |
if (event.data.size > 0) { | |
audio = [event.data]; | |
} | |
}; | |
mediaRecorder.onstop = (event) => { | |
const b = new Blob(audio, { type: "audio/wav" }); | |
setAudioBlob(b); | |
console.log("audioBlob", b); | |
}; | |
}) | |
.catch((error) => { | |
console.error("Error accessing microphone:", error); | |
}); | |
} | |
return () => { | |
if (timerRef.current) { | |
clearInterval(timerRef.current); | |
} | |
}; | |
}, [audioStream]); | |
const handleToggleRecording = (event) => { | |
event.preventDefault(); | |
if (isRecording) { | |
stopRecording(); | |
} else { | |
startRecording(); | |
} | |
}; | |
const startRecording = () => { | |
mediaRecorder.start(); | |
setIsRecording(true); | |
setRecordingTime(0); | |
setAudioBlob(null); | |
timerRef.current = setInterval(() => { | |
setRecordingTime((prevTime) => { | |
if (prevTime >= RECORDING_MAX_DURATION - 1) { | |
stopRecording(); | |
return RECORDING_MAX_DURATION; | |
} | |
return prevTime + 1; | |
}); | |
}, 1000); | |
}; | |
const stopRecording = () => { | |
mediaRecorder.stop(); | |
setIsRecording(false); | |
if (timerRef.current) { | |
clearInterval(timerRef.current); | |
} | |
}; | |
const formatTime = (seconds) => { | |
const minutes = Math.floor(seconds / 60); | |
const remainingSeconds = seconds % 60; | |
return `${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")}`; | |
}; | |
return ( | |
<div> | |
<button | |
onClick={handleToggleRecording} | |
className={`bg-red-400 hover:opacity-80 text-white font-bold py-2 px-4 rounded`} | |
> | |
{isRecording ? ( | |
<> | |
<span className={`mr-3 ${isRecording && "animate-pulse"}`}>●</span>{" "} | |
Stop Recording | |
</> | |
) : audioBlob ? ( | |
"Redo recording" | |
) : ( | |
"Start Recording" | |
)} | |
</button> | |
<div> | |
{isRecording && ( | |
<div> | |
<p>Recording...</p> | |
<p>Time: {formatTime(recordingTime)}</p> | |
</div> | |
)} | |
</div> | |
{audioBlob && ( | |
<> | |
<div>Preview recording before submitting:</div> | |
<audio controls> | |
<source src={URL.createObjectURL(audioBlob)} type="audio/wav" /> | |
</audio> | |
</> | |
)} | |
</div> | |
); | |
} | |
export default SimpleRecordButton; |
As a future reference it might be useful to use 2 space indentation for the gist to be more readable, otherwise nice!
As a future reference it might be useful to use 2 space indentation for the gist to be more readable, otherwise nice!
You can change how tabs are rendered in your personal GitHub settings!
https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/managing-your-tab-size-rendering-preference
I prefer using tabs instead of spaces for accessibility reasons. With a screen reader, someone going through the code will hear every individual space, which gets to be a lot when you have some deeply nested things. A screen reader reading out the tab characters is much more manageable!
@cassidoo I wasn't aware 😮! I feel a bit dumb about making the comment now... Anyway, I think I will start to do the same thing going forward to accommodate people with various disabilities 👍 Thanks for letting me on with this configuration
Thanks @arthurgubaidullin!