summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/audio-waveform.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/components/editor/audio-waveform.tsx')
-rw-r--r--apps/web/src/components/editor/audio-waveform.tsx85
1 files changed, 65 insertions, 20 deletions
diff --git a/apps/web/src/components/editor/audio-waveform.tsx b/apps/web/src/components/editor/audio-waveform.tsx
index a673f995..372a31c5 100644
--- a/apps/web/src/components/editor/audio-waveform.tsx
+++ b/apps/web/src/components/editor/audio-waveform.tsx
@@ -19,22 +19,21 @@ const AudioWaveform: React.FC<AudioWaveformProps> = ({
useEffect(() => {
let mounted = true;
-
+ let ws = wavesurfer.current;
+
const initWaveSurfer = async () => {
if (!waveformRef.current || !audioUrl) return;
try {
- // Clean up any existing instance
- if (wavesurfer.current) {
- try {
- wavesurfer.current.destroy();
- } catch (e) {
- // Silently ignore destroy errors
- }
+ // Clear any existing instance safely
+ if (ws) {
+ // Instead of immediately destroying, just set to null
+ // We'll destroy it outside this function
wavesurfer.current = null;
}
- wavesurfer.current = WaveSurfer.create({
+ // Create a fresh instance
+ const newWaveSurfer = WaveSurfer.create({
container: waveformRef.current,
waveColor: "rgba(255, 255, 255, 0.6)",
progressColor: "rgba(255, 255, 255, 0.9)",
@@ -46,15 +45,28 @@ const AudioWaveform: React.FC<AudioWaveformProps> = ({
interact: false,
});
+ // Assign to ref only if component is still mounted
+ if (mounted) {
+ wavesurfer.current = newWaveSurfer;
+ } else {
+ // Component unmounted during initialization, clean up
+ try {
+ newWaveSurfer.destroy();
+ } catch (e) {
+ // Ignore destroy errors
+ }
+ return;
+ }
+
// Event listeners
- wavesurfer.current.on("ready", () => {
+ newWaveSurfer.on("ready", () => {
if (mounted) {
setIsLoading(false);
setError(false);
}
});
- wavesurfer.current.on("error", (err) => {
+ newWaveSurfer.on("error", (err) => {
console.error("WaveSurfer error:", err);
if (mounted) {
setError(true);
@@ -62,7 +74,7 @@ const AudioWaveform: React.FC<AudioWaveformProps> = ({
}
});
- await wavesurfer.current.load(audioUrl);
+ await newWaveSurfer.load(audioUrl);
} catch (err) {
console.error("Failed to initialize WaveSurfer:", err);
if (mounted) {
@@ -72,17 +84,50 @@ const AudioWaveform: React.FC<AudioWaveformProps> = ({
}
};
- initWaveSurfer();
-
- return () => {
- mounted = false;
- if (wavesurfer.current) {
+ // First safely destroy previous instance if it exists
+ if (ws) {
+ // Use this pattern to safely destroy the previous instance
+ const wsToDestroy = ws;
+ // Detach from ref immediately
+ wavesurfer.current = null;
+
+ // Wait a tick to destroy so any pending operations can complete
+ requestAnimationFrame(() => {
try {
- wavesurfer.current.destroy();
+ wsToDestroy.destroy();
} catch (e) {
- // Silently ignore destroy errors
+ // Ignore errors during destroy
+ }
+ // Only initialize new instance after destroying the old one
+ if (mounted) {
+ initWaveSurfer();
}
- wavesurfer.current = null;
+ });
+ } else {
+ // No previous instance to clean up, initialize directly
+ initWaveSurfer();
+ }
+
+ return () => {
+ // Mark component as unmounted
+ mounted = false;
+
+ // Store reference to current wavesurfer instance
+ const wsToDestroy = wavesurfer.current;
+
+ // Immediately clear the ref to prevent accessing it after unmount
+ wavesurfer.current = null;
+
+ // If we have an instance to clean up, do it safely
+ if (wsToDestroy) {
+ // Delay destruction to avoid race conditions
+ requestAnimationFrame(() => {
+ try {
+ wsToDestroy.destroy();
+ } catch (e) {
+ // Ignore destroy errors - they're expected
+ }
+ });
}
};
}, [audioUrl, height]);