Files
SpotiFLAC/frontend/src/components/AudioAnalysisDialog.tsx
T
afkarxyz 957fb83dbc v6.4
2025-11-27 06:49:33 +07:00

200 lines
6.5 KiB
TypeScript

import { useState, useCallback } from "react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Activity, Upload, X } from "lucide-react";
import { AudioAnalysis } from "@/components/AudioAnalysis";
import { SpectrumVisualization } from "@/components/SpectrumVisualization";
import { useAudioAnalysis } from "@/hooks/useAudioAnalysis";
import { SelectFile } from "../../wailsjs/go/main/App";
import { toastWithSound as toast } from "@/lib/toast-with-sound";
import { OnFileDrop, OnFileDropOff } from "../../wailsjs/runtime/runtime";
import { useEffect } from "react";
export function AudioAnalysisDialog() {
const [open, setOpen] = useState(false);
const { analyzing, result, analyzeFile, clearResult } = useAudioAnalysis();
const [selectedFilePath, setSelectedFilePath] = useState<string>("");
const [isDragging, setIsDragging] = useState(false);
const handleSelectFile = async () => {
try {
const filePath = await SelectFile();
if (filePath) {
setSelectedFilePath(filePath);
await analyzeFile(filePath);
}
} catch (err) {
toast.error("File Selection Failed", {
description: err instanceof Error ? err.message : "Failed to select file",
});
}
};
const handleFileDrop = useCallback(async (_x: number, _y: number, paths: string[]) => {
setIsDragging(false);
if (paths.length === 0) return;
const filePath = paths[0];
// Check if it's a FLAC file
if (!filePath.toLowerCase().endsWith('.flac')) {
toast.error("Invalid File Type", {
description: "Please drop a FLAC file for analysis",
});
return;
}
setSelectedFilePath(filePath);
await analyzeFile(filePath);
}, [analyzeFile]);
// Register drag and drop handlers when dialog is open
useEffect(() => {
if (open) {
OnFileDrop((x, y, paths) => {
handleFileDrop(x, y, paths);
}, true);
return () => {
OnFileDropOff();
};
}
}, [open, handleFileDrop]);
const handleClose = () => {
setOpen(false);
setTimeout(() => {
clearResult();
setSelectedFilePath("");
}, 200);
};
return (
<Dialog open={open} onOpenChange={(isOpen) => {
if (!isOpen) {
handleClose();
} else {
setOpen(true);
}
}}>
<Tooltip>
<TooltipTrigger asChild>
<DialogTrigger asChild>
<Button variant="outline" size="icon">
<Activity className="h-5 w-5" />
</Button>
</DialogTrigger>
</TooltipTrigger>
<TooltipContent side="left">
<p>Audio Quality Analyzer</p>
</TooltipContent>
</Tooltip>
<DialogContent className="sm:max-w-[900px] max-h-[90vh] overflow-y-auto flex flex-col p-6 [&>button]:hidden custom-scrollbar" aria-describedby={undefined}>
<div className="absolute right-4 top-4">
<Button
variant="ghost"
size="icon"
className="h-6 w-6 opacity-70 hover:opacity-100"
onClick={handleClose}
>
<X className="h-4 w-4" />
</Button>
</div>
<DialogTitle className="text-sm font-medium">Audio Quality Analyzer</DialogTitle>
<div className="space-y-4">
{/* File Selection */}
{!result && !analyzing && (
<div
className={`flex flex-col items-center justify-center py-12 border-2 border-dashed rounded-lg transition-colors ${
isDragging
? "border-primary bg-primary/10"
: "border-muted-foreground/30 hover:border-muted-foreground/50"
}`}
onDragOver={(e) => {
e.preventDefault();
setIsDragging(true);
}}
onDragLeave={(e) => {
e.preventDefault();
setIsDragging(false);
}}
onDrop={(e) => {
e.preventDefault();
setIsDragging(false);
}}
style={{ "--wails-drop-target": "drop" } as React.CSSProperties}
>
<Activity className={`h-16 w-16 mb-4 transition-colors ${isDragging ? "text-primary" : "text-muted-foreground/50"}`} />
<h3 className="text-lg font-medium mb-2">Analyze FLAC Audio Quality</h3>
<p className="text-sm text-muted-foreground mb-6 text-center max-w-md">
{isDragging
? "Drop your FLAC file here"
: "Drag and drop a FLAC file here, or click the button below to select"}
</p>
<Button onClick={handleSelectFile} size="lg">
<Upload className="h-5 w-5" />
Select FLAC File
</Button>
</div>
)}
{/* Analysis Results */}
{result && (
<div className="space-y-4">
{/* File Info */}
<div className="p-3 bg-muted/30 rounded-lg">
<p className="text-xs text-muted-foreground">Analyzing file:</p>
<p className="text-sm font-mono truncate">{selectedFilePath}</p>
</div>
{/* Spectrum Visualization */}
<SpectrumVisualization
sampleRate={result.sample_rate}
bitsPerSample={result.bits_per_sample}
duration={result.duration}
spectrumData={result.spectrum}
/>
{/* Detailed Analysis */}
<AudioAnalysis
result={result}
analyzing={analyzing}
showAnalyzeButton={false}
/>
{/* Actions */}
<div className="flex gap-2 justify-end pt-2">
<Button onClick={handleSelectFile} variant="outline">
<Upload className="h-4 w-4" />
Analyze Another File
</Button>
</div>
</div>
)}
{/* Loading State */}
{analyzing && !result && (
<div className="flex flex-col items-center justify-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary mb-4"></div>
<p className="text-sm text-muted-foreground">Analyzing audio file...</p>
</div>
)}
</div>
</DialogContent>
</Dialog>
);
}