Fixed the "Converting circular structure to JSON" error in useMultiTrack by properly destructuring audioBuffer from track objects before serializing to localStorage. Changed from spreading the entire track object (which could have circular refs) to explicitly excluding audioBuffer using destructuring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
import { useState, useCallback, useEffect } from 'react';
|
|
import type { Track } from '@/types/track';
|
|
import { createTrack, createTrackFromBuffer } from '@/lib/audio/track-utils';
|
|
|
|
const STORAGE_KEY = 'audio-ui-multi-track';
|
|
|
|
export function useMultiTrack() {
|
|
const [tracks, setTracks] = useState<Track[]>(() => {
|
|
if (typeof window === 'undefined') return [];
|
|
|
|
try {
|
|
const saved = localStorage.getItem(STORAGE_KEY);
|
|
if (saved) {
|
|
const parsed = JSON.parse(saved);
|
|
// Note: AudioBuffers can't be serialized, so we only restore track metadata
|
|
return parsed.map((t: any) => ({
|
|
...t,
|
|
audioBuffer: null, // Will need to be reloaded
|
|
}));
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load tracks from localStorage:', error);
|
|
}
|
|
|
|
return [];
|
|
});
|
|
|
|
// Save tracks to localStorage (without audio buffers)
|
|
useEffect(() => {
|
|
if (typeof window === 'undefined') return;
|
|
|
|
try {
|
|
const trackData = tracks.map(({ audioBuffer, ...track }) => track);
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(trackData));
|
|
} catch (error) {
|
|
console.error('Failed to save tracks to localStorage:', error);
|
|
}
|
|
}, [tracks]);
|
|
|
|
const addTrack = useCallback((name?: string) => {
|
|
const track = createTrack(name);
|
|
setTracks((prev) => [...prev, track]);
|
|
return track;
|
|
}, []);
|
|
|
|
const addTrackFromBuffer = useCallback((buffer: AudioBuffer, name?: string) => {
|
|
const track = createTrackFromBuffer(buffer, name);
|
|
setTracks((prev) => [...prev, track]);
|
|
return track;
|
|
}, []);
|
|
|
|
const removeTrack = useCallback((trackId: string) => {
|
|
setTracks((prev) => prev.filter((t) => t.id !== trackId));
|
|
}, []);
|
|
|
|
const updateTrack = useCallback((trackId: string, updates: Partial<Track>) => {
|
|
setTracks((prev) =>
|
|
prev.map((track) =>
|
|
track.id === trackId ? { ...track, ...updates } : track
|
|
)
|
|
);
|
|
}, []);
|
|
|
|
const clearTracks = useCallback(() => {
|
|
setTracks([]);
|
|
}, []);
|
|
|
|
const reorderTracks = useCallback((fromIndex: number, toIndex: number) => {
|
|
setTracks((prev) => {
|
|
const newTracks = [...prev];
|
|
const [movedTrack] = newTracks.splice(fromIndex, 1);
|
|
newTracks.splice(toIndex, 0, movedTrack);
|
|
return newTracks;
|
|
});
|
|
}, []);
|
|
|
|
const setTrackBuffer = useCallback((trackId: string, buffer: AudioBuffer) => {
|
|
setTracks((prev) =>
|
|
prev.map((track) =>
|
|
track.id === trackId ? { ...track, audioBuffer: buffer } : track
|
|
)
|
|
);
|
|
}, []);
|
|
|
|
return {
|
|
tracks,
|
|
addTrack,
|
|
addTrackFromBuffer,
|
|
removeTrack,
|
|
updateTrack,
|
|
clearTracks,
|
|
reorderTracks,
|
|
setTrackBuffer,
|
|
};
|
|
}
|