Created a waveform component to display the recording's waveform

This commit is contained in:
Tiago Ribeiro
2023-06-14 16:22:48 +01:00
parent 31f2eb510e
commit f5fc85e1a7
5 changed files with 133 additions and 5 deletions

32
.pnp.cjs generated
View File

@@ -37,6 +37,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/react", "npm:18.0.27"],\
["@types/react-dom", "npm:18.0.10"],\
["@types/uuid", "npm:9.0.1"],\
["@types/wavesurfer.js", "npm:6.0.6"],\
["@wixc3/react-board", "virtual:bf6aaa3c042cc1a42ee2910afd2615e1727d392da84fae76c4ff4b04654d9bc15e6952fab427843d598f4a94d8eaa3198f30dab7b7c8d523ed6cd30ef92cfb66#npm:2.2.0"],\
["autoprefixer", "virtual:bf6aaa3c042cc1a42ee2910afd2615e1727d392da84fae76c4ff4b04654d9bc15e6952fab427843d598f4a94d8eaa3198f30dab7b7c8d523ed6cd30ef92cfb66#npm:10.4.14"],\
["axios", "npm:1.3.5"],\
@@ -68,6 +69,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["tailwindcss", "virtual:bf6aaa3c042cc1a42ee2910afd2615e1727d392da84fae76c4ff4b04654d9bc15e6952fab427843d598f4a94d8eaa3198f30dab7b7c8d523ed6cd30ef92cfb66#npm:3.3.1"],\
["typescript", "patch:typescript@npm%3A4.9.5#~builtin<compat/typescript>::version=4.9.5&hash=289587"],\
["uuid", "npm:9.0.0"],\
["wavesurfer.js", "npm:6.6.4"],\
["zustand", "virtual:bf6aaa3c042cc1a42ee2910afd2615e1727d392da84fae76c4ff4b04654d9bc15e6952fab427843d598f4a94d8eaa3198f30dab7b7c8d523ed6cd30ef92cfb66#npm:4.3.7"]\
],\
"linkType": "SOFT"\
@@ -1770,6 +1772,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["@types/debounce", [\
["npm:3.0.0", {\
"packageLocation": "./.yarn/cache/@types-debounce-npm-3.0.0-b7858a5be9-af99f44f8c.zip/node_modules/@types/debounce/",\
"packageDependencies": [\
["@types/debounce", "npm:3.0.0"]\
],\
"linkType": "HARD"\
}]\
]],\
["@types/express", [\
["npm:4.17.17", {\
"packageLocation": "./.yarn/cache/@types-express-npm-4.17.17-46fe8173db-0196dacc27.zip/node_modules/@types/express/",\
@@ -1989,6 +2000,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["@types/wavesurfer.js", [\
["npm:6.0.6", {\
"packageLocation": "./.yarn/cache/@types-wavesurfer.js-npm-6.0.6-465818fff7-732e998507.zip/node_modules/@types/wavesurfer.js/",\
"packageDependencies": [\
["@types/wavesurfer.js", "npm:6.0.6"],\
["@types/debounce", "npm:3.0.0"]\
],\
"linkType": "HARD"\
}]\
]],\
["@typescript-eslint/parser", [\
["npm:5.51.0", {\
"packageLocation": "./.yarn/cache/@typescript-eslint-parser-npm-5.51.0-884b6bb8aa-096ec81913.zip/node_modules/@typescript-eslint/parser/",\
@@ -5343,6 +5364,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/react", "npm:18.0.27"],\
["@types/react-dom", "npm:18.0.10"],\
["@types/uuid", "npm:9.0.1"],\
["@types/wavesurfer.js", "npm:6.0.6"],\
["@wixc3/react-board", "virtual:bf6aaa3c042cc1a42ee2910afd2615e1727d392da84fae76c4ff4b04654d9bc15e6952fab427843d598f4a94d8eaa3198f30dab7b7c8d523ed6cd30ef92cfb66#npm:2.2.0"],\
["autoprefixer", "virtual:bf6aaa3c042cc1a42ee2910afd2615e1727d392da84fae76c4ff4b04654d9bc15e6952fab427843d598f4a94d8eaa3198f30dab7b7c8d523ed6cd30ef92cfb66#npm:10.4.14"],\
["axios", "npm:1.3.5"],\
@@ -5374,6 +5396,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["tailwindcss", "virtual:bf6aaa3c042cc1a42ee2910afd2615e1727d392da84fae76c4ff4b04654d9bc15e6952fab427843d598f4a94d8eaa3198f30dab7b7c8d523ed6cd30ef92cfb66#npm:3.3.1"],\
["typescript", "patch:typescript@npm%3A4.9.5#~builtin<compat/typescript>::version=4.9.5&hash=289587"],\
["uuid", "npm:9.0.0"],\
["wavesurfer.js", "npm:6.6.4"],\
["zustand", "virtual:bf6aaa3c042cc1a42ee2910afd2615e1727d392da84fae76c4ff4b04654d9bc15e6952fab427843d598f4a94d8eaa3198f30dab7b7c8d523ed6cd30ef92cfb66#npm:4.3.7"]\
],\
"linkType": "SOFT"\
@@ -7209,6 +7232,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["wavesurfer.js", [\
["npm:6.6.4", {\
"packageLocation": "./.yarn/cache/wavesurfer.js-npm-6.6.4-ee0823a013-cee2a98695.zip/node_modules/wavesurfer.js/",\
"packageDependencies": [\
["wavesurfer.js", "npm:6.6.4"]\
],\
"linkType": "HARD"\
}]\
]],\
["webcrypto-core", [\
["npm:1.7.7", {\
"packageLocation": "./.yarn/cache/webcrypto-core-npm-1.7.7-bb22025843-1dc5aedb25.zip/node_modules/webcrypto-core/",\

View File

@@ -43,11 +43,13 @@
"swr": "^2.1.3",
"typescript": "4.9.5",
"uuid": "^9.0.0",
"wavesurfer.js": "^6.6.4",
"zustand": "^4.3.6"
},
"devDependencies": {
"@types/lodash": "^4.14.191",
"@types/uuid": "^9.0.1",
"@types/wavesurfer.js": "^6.0.6",
"@wixc3/react-board": "^2.2.0",
"autoprefixer": "^10.4.13",
"postcss": "^8.4.21",

View File

@@ -0,0 +1,56 @@
import React, {useEffect, useRef} from "react";
import WaveSurfer from "wavesurfer.js";
interface Props {
audio: string;
isPlaying: boolean;
onEnd: () => void;
}
const Waveform = ({audio, isPlaying, onEnd}: Props) => {
const containerRef = useRef(null);
const waveSurferRef = useRef<WaveSurfer | null>(null);
useEffect(() => {
const waveSurfer = WaveSurfer.create({
container: containerRef?.current || "",
responsive: true,
cursorWidth: 0,
height: 24,
waveColor: "#FCDDEC",
progressColor: "#EF5DA8",
barGap: 4,
barWidth: 4,
barRadius: 2,
fillParent: true,
hideScrollbar: true,
normalize: true,
});
waveSurfer.load(audio);
waveSurfer.on("ready", () => {
waveSurferRef.current = waveSurfer;
});
waveSurfer.on("finish", onEnd);
return () => {
waveSurfer.destroy();
};
}, [audio, onEnd]);
useEffect(() => {
if (isPlaying) waveSurferRef.current?.play();
if (!isPlaying) waveSurferRef.current?.pause();
}, [isPlaying]);
return (
<>
<button onClick={() => waveSurferRef.current?.playPause()} type="button">
{" "}
play/pause{" "}
</button>
<div className="w-full h-fit" ref={containerRef} />
</>
);
};
export default Waveform;

View File

@@ -8,9 +8,10 @@ import {sessionOptions} from "@/lib/session";
import useUser from "@/hooks/useUser";
import Sidebar from "@/components/Sidebar";
import dynamic from "next/dynamic";
import {BsCheckCircleFill, BsMicFill, BsPauseCircle, BsPlayCircle, BsPlayFill, BsTrashFill} from "react-icons/bs";
import {BsCheckCircleFill, BsMicFill, BsPauseCircle, BsPauseFill, BsPlayCircle, BsPlayFill, BsTrashFill} from "react-icons/bs";
import {useEffect, useState} from "react";
const Waveform = dynamic(() => import("../components/Waveform"), {ssr: false});
const ReactMediaRecorder = dynamic(() => import("react-media-recorder").then((mod) => mod.ReactMediaRecorder), {
ssr: false,
});
@@ -38,6 +39,7 @@ export default function Page() {
const {user} = useUser({redirectTo: "/login"});
const [recordingDuration, setRecordingDuration] = useState(0);
const [isRecording, setIsRecording] = useState(false);
const [isPlaying, setIsPlaying] = useState(false);
useEffect(() => {
let recordingInterval: NodeJS.Timer | undefined = undefined;
@@ -78,7 +80,7 @@ export default function Page() {
<div className="flex gap-8 items-center justify-center py-8">
{status === "idle" && (
<>
{!mediaBlobUrl && <div className="w-full h-2 max-w-4xl bg-mti-gray-smoke rounded-full" />}
<div className="w-full h-2 max-w-4xl bg-mti-gray-smoke rounded-full" />
{status === "idle" && (
<BsMicFill
onClick={() => {
@@ -155,10 +157,21 @@ export default function Page() {
</div>
</>
)}
{status === "stopped" && (
{status === "stopped" && mediaBlobUrl && (
<>
<BsPlayFill className="text-mti-gray-cool cursor-pointer w-5 h-5" />
<div className="w-full h-2 max-w-4xl bg-mti-gray-smoke rounded-full" />
{isPlaying && (
<BsPauseFill
className="text-mti-gray-cool cursor-pointer w-5 h-5"
onClick={() => setIsPlaying(false)}
/>
)}
{!isPlaying && (
<BsPlayFill
className="text-mti-gray-cool cursor-pointer w-5 h-5"
onClick={() => setIsPlaying(true)}
/>
)}
<Waveform audio={mediaBlobUrl} isPlaying={isPlaying} onEnd={() => setIsPlaying(false)} />
<div className="flex gap-4 items-center">
<BsTrashFill
className="text-mti-gray-cool cursor-pointer w-5 h-5"

View File

@@ -1071,6 +1071,13 @@ __metadata:
languageName: node
linkType: hard
"@types/debounce@npm:*":
version: 3.0.0
resolution: "@types/debounce@npm:3.0.0"
checksum: af99f44f8ce90388aa5a909066c64b4ddc5efdff72bccf7dcef1fe8a04f03d03a7a42cc1f42ff14620c66923f92600ccfeb768bd77ad250f12a6ebca116ca719
languageName: node
linkType: hard
"@types/express-serve-static-core@npm:^4.17.33":
version: 4.17.33
resolution: "@types/express-serve-static-core@npm:4.17.33"
@@ -1256,6 +1263,15 @@ __metadata:
languageName: node
linkType: hard
"@types/wavesurfer.js@npm:^6.0.6":
version: 6.0.6
resolution: "@types/wavesurfer.js@npm:6.0.6"
dependencies:
"@types/debounce": "*"
checksum: 732e9985075dfd4a9fa9cfa0dd959a3d4243fa16a7a11abe953a98a9a391a76878e0451f9bf1b8f2356d6822fbf34c8e5b278af60b6818c1c47e1fbe2cc1a5eb
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:^5.42.0":
version: 5.51.0
resolution: "@typescript-eslint/parser@npm:5.51.0"
@@ -3998,6 +4014,7 @@ __metadata:
"@types/react": 18.0.27
"@types/react-dom": 18.0.10
"@types/uuid": ^9.0.1
"@types/wavesurfer.js": ^6.0.6
"@wixc3/react-board": ^2.2.0
autoprefixer: ^10.4.13
axios: ^1.3.5
@@ -4029,6 +4046,7 @@ __metadata:
tailwindcss: ^3.2.4
typescript: 4.9.5
uuid: ^9.0.0
wavesurfer.js: ^6.6.4
zustand: ^4.3.6
languageName: unknown
linkType: soft
@@ -5593,6 +5611,13 @@ __metadata:
languageName: node
linkType: hard
"wavesurfer.js@npm:^6.6.4":
version: 6.6.4
resolution: "wavesurfer.js@npm:6.6.4"
checksum: cee2a98695241ab08edaab8d7b2a5ac59a428b8c8d79fa9c2692b4f95b15da9a61a7f81cd18114da2b37d59dabf7dc4854d0862bedfa98b2650620f05bea567b
languageName: node
linkType: hard
"webcrypto-core@npm:^1.7.7":
version: 1.7.7
resolution: "webcrypto-core@npm:1.7.7"