-
-
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!