feat: integrate ImportDialog into file upload flow

Now when users upload an audio file, they see the ImportDialog with options:

**User Flow:**
1. User selects audio file (click or drag-drop)
2. File is pre-decoded to extract metadata (sample rate, channels)
3. ImportDialog shows with file info and import options:
   - Convert to Mono (if stereo/multi-channel)
   - Resample Audio (44.1kHz - 192kHz)
   - Normalize on Import
4. User clicks Import (or Cancel)
5. File is imported with selected transformations applied
6. Track name auto-updates to filename

**Track.tsx Changes:**
- Added import dialog state (showImportDialog, pendingFile, fileMetadata)
- Updated handleFileChange to show dialog instead of direct import
- Added handleImport to process file with user-selected options
- Added handleImportCancel to dismiss dialog
- Renders ImportDialog when showImportDialog is true
- Logs imported audio metadata to console

Now users can see and control all import transformations!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-19 08:38:15 +01:00
parent 37f910acb7
commit 500a466bae

View File

@@ -15,6 +15,8 @@ import { createAutomationPoint } from '@/lib/audio/automation/utils';
import { createAutomationLane } from '@/lib/audio/automation-utils';
import { EffectDevice } from '@/components/effects/EffectDevice';
import { EffectBrowser } from '@/components/effects/EffectBrowser';
import { ImportDialog } from '@/components/dialogs/ImportDialog';
import { importAudioFile, type ImportOptions } from '@/lib/audio/decoder';
export interface TrackProps {
track: TrackType;
@@ -86,6 +88,11 @@ export function Track({
const resizeStartRef = React.useRef({ y: 0, height: 0 });
const [effectBrowserOpen, setEffectBrowserOpen] = React.useState(false);
// Import dialog state
const [showImportDialog, setShowImportDialog] = React.useState(false);
const [pendingFile, setPendingFile] = React.useState<File | null>(null);
const [fileMetadata, setFileMetadata] = React.useState<{ sampleRate?: number; channels?: number }>({});
// Selection state
const [isSelecting, setIsSelecting] = React.useState(false);
const [selectionStart, setSelectionStart] = React.useState<number | null>(null);
@@ -452,24 +459,55 @@ export function Track({
if (!file || !onLoadAudio) return;
try {
// Decode to get basic metadata before showing dialog
const arrayBuffer = await file.arrayBuffer();
const audioContext = new AudioContext();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
onLoadAudio(audioBuffer);
const tempBuffer = await audioContext.decodeAudioData(arrayBuffer);
// Update track name to filename if it's still default
if (track.name === 'New Track' || track.name === 'Untitled Track') {
const fileName = file.name.replace(/\.[^/.]+$/, '');
onNameChange(fileName);
}
// Set metadata and show import dialog
setFileMetadata({
sampleRate: tempBuffer.sampleRate,
channels: tempBuffer.numberOfChannels,
});
setPendingFile(file);
setShowImportDialog(true);
} catch (error) {
console.error('Failed to load audio file:', error);
console.error('Failed to read audio file metadata:', error);
}
// Reset input
e.target.value = '';
};
const handleImport = async (options: ImportOptions) => {
if (!pendingFile || !onLoadAudio) return;
try {
setShowImportDialog(false);
const { buffer, metadata } = await importAudioFile(pendingFile, options);
onLoadAudio(buffer);
// Update track name to filename if it's still default
if (track.name === 'New Track' || track.name === 'Untitled Track') {
const fileName = metadata.fileName.replace(/\.[^/.]+$/, '');
onNameChange(fileName);
}
console.log('Audio imported:', metadata);
} catch (error) {
console.error('Failed to import audio file:', error);
} finally {
setPendingFile(null);
setFileMetadata({});
}
};
const handleImportCancel = () => {
setShowImportDialog(false);
setPendingFile(null);
setFileMetadata({});
};
const handleLoadAudioClick = () => {
fileInputRef.current?.click();
};
@@ -1005,6 +1043,17 @@ export function Track({
<div className="absolute inset-x-0 bottom-0 h-px bg-border group-hover:bg-primary transition-colors duration-200" />
</div>
)}
{/* Import Dialog */}
{showImportDialog && pendingFile && (
<ImportDialog
fileName={pendingFile.name}
originalSampleRate={fileMetadata.sampleRate}
originalChannels={fileMetadata.channels}
onImport={handleImport}
onCancel={handleImportCancel}
/>
)}
</div>
);
}