v6.1
This commit is contained in:
@@ -20,6 +20,7 @@ interface AlbumInfoProps {
|
||||
sortBy: string;
|
||||
selectedTracks: string[];
|
||||
downloadedTracks: Set<string>;
|
||||
failedTracks: Set<string>;
|
||||
downloadingTrack: string | null;
|
||||
isDownloading: boolean;
|
||||
bulkDownloadType: "all" | "selected" | null;
|
||||
@@ -46,6 +47,7 @@ export function AlbumInfo({
|
||||
sortBy,
|
||||
selectedTracks,
|
||||
downloadedTracks,
|
||||
failedTracks,
|
||||
downloadingTrack,
|
||||
isDownloading,
|
||||
bulkDownloadType,
|
||||
@@ -145,6 +147,7 @@ export function AlbumInfo({
|
||||
sortBy={sortBy}
|
||||
selectedTracks={selectedTracks}
|
||||
downloadedTracks={downloadedTracks}
|
||||
failedTracks={failedTracks}
|
||||
downloadingTrack={downloadingTrack}
|
||||
isDownloading={isDownloading}
|
||||
currentPage={currentPage}
|
||||
|
||||
@@ -27,6 +27,7 @@ interface ArtistInfoProps {
|
||||
sortBy: string;
|
||||
selectedTracks: string[];
|
||||
downloadedTracks: Set<string>;
|
||||
failedTracks: Set<string>;
|
||||
downloadingTrack: string | null;
|
||||
isDownloading: boolean;
|
||||
bulkDownloadType: "all" | "selected" | null;
|
||||
@@ -55,6 +56,7 @@ export function ArtistInfo({
|
||||
sortBy,
|
||||
selectedTracks,
|
||||
downloadedTracks,
|
||||
failedTracks,
|
||||
downloadingTrack,
|
||||
isDownloading,
|
||||
bulkDownloadType,
|
||||
@@ -197,6 +199,7 @@ export function ArtistInfo({
|
||||
sortBy={sortBy}
|
||||
selectedTracks={selectedTracks}
|
||||
downloadedTracks={downloadedTracks}
|
||||
failedTracks={failedTracks}
|
||||
downloadingTrack={downloadingTrack}
|
||||
isDownloading={isDownloading}
|
||||
currentPage={currentPage}
|
||||
|
||||
@@ -26,6 +26,7 @@ interface PlaylistInfoProps {
|
||||
sortBy: string;
|
||||
selectedTracks: string[];
|
||||
downloadedTracks: Set<string>;
|
||||
failedTracks: Set<string>;
|
||||
downloadingTrack: string | null;
|
||||
isDownloading: boolean;
|
||||
bulkDownloadType: "all" | "selected" | null;
|
||||
@@ -52,6 +53,7 @@ export function PlaylistInfo({
|
||||
sortBy,
|
||||
selectedTracks,
|
||||
downloadedTracks,
|
||||
failedTracks,
|
||||
downloadingTrack,
|
||||
isDownloading,
|
||||
bulkDownloadType,
|
||||
@@ -151,6 +153,7 @@ export function PlaylistInfo({
|
||||
sortBy={sortBy}
|
||||
selectedTracks={selectedTracks}
|
||||
downloadedTracks={downloadedTracks}
|
||||
failedTracks={failedTracks}
|
||||
downloadingTrack={downloadingTrack}
|
||||
isDownloading={isDownloading}
|
||||
currentPage={currentPage}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { InputWithContext } from "@/components/ui/input-with-context";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Search, Info, XCircle } from "lucide-react";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
@@ -19,56 +18,52 @@ interface SearchBarProps {
|
||||
|
||||
export function SearchBar({ url, loading, onUrlChange, onFetch }: SearchBarProps) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="px-6 py-6 space-y-4">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label htmlFor="spotify-url">Spotify URL</Label>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right">
|
||||
<p>Supports track, album, playlist, and artist URLs</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<div className="relative flex-1">
|
||||
<InputWithContext
|
||||
id="spotify-url"
|
||||
placeholder="https://open.spotify.com/..."
|
||||
value={url}
|
||||
onChange={(e) => onUrlChange(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && onFetch()}
|
||||
className="pr-8"
|
||||
/>
|
||||
{url && (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors"
|
||||
onClick={() => onUrlChange("")}
|
||||
>
|
||||
<XCircle className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<Button onClick={onFetch} disabled={loading}>
|
||||
{loading ? (
|
||||
<>
|
||||
<Spinner />
|
||||
Fetching...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Search className="h-4 w-4" />
|
||||
Fetch
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label htmlFor="spotify-url">Spotify URL</Label>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right">
|
||||
<p>Supports track, album, playlist, and artist URLs</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<div className="relative flex-1">
|
||||
<InputWithContext
|
||||
id="spotify-url"
|
||||
placeholder="https://open.spotify.com/..."
|
||||
value={url}
|
||||
onChange={(e) => onUrlChange(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && onFetch()}
|
||||
className="pr-8"
|
||||
/>
|
||||
{url && (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors"
|
||||
onClick={() => onUrlChange("")}
|
||||
>
|
||||
<XCircle className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Button onClick={onFetch} disabled={loading}>
|
||||
{loading ? (
|
||||
<>
|
||||
<Spinner />
|
||||
Fetching...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Search className="h-4 w-4" />
|
||||
Fetch
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export function TitleBar() {
|
||||
/>
|
||||
|
||||
{/* Window control buttons */}
|
||||
<div className="absolute top-4 left-4 z-50 flex gap-2">
|
||||
<div className="fixed top-4 left-4 z-50 flex gap-2">
|
||||
<button
|
||||
onClick={handleClose}
|
||||
onMouseEnter={() => setHoveredButton("close")}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Download, FolderOpen } from "lucide-react";
|
||||
import { Download, FolderOpen, CheckCircle, XCircle } from "lucide-react";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import type { TrackMetadata } from "@/types/api";
|
||||
|
||||
@@ -9,6 +9,7 @@ interface TrackInfoProps {
|
||||
isDownloading: boolean;
|
||||
downloadingTrack: string | null;
|
||||
isDownloaded: boolean;
|
||||
isFailed: boolean;
|
||||
onDownload: (isrc: string, name: string, artists: string, albumName?: string, spotifyId?: string) => void;
|
||||
onOpenFolder: () => void;
|
||||
}
|
||||
@@ -18,6 +19,7 @@ export function TrackInfo({
|
||||
isDownloading,
|
||||
downloadingTrack,
|
||||
isDownloaded,
|
||||
isFailed,
|
||||
onDownload,
|
||||
onOpenFolder,
|
||||
}: TrackInfoProps) {
|
||||
@@ -34,7 +36,15 @@ export function TrackInfo({
|
||||
)}
|
||||
<div className="flex-1 space-y-4 min-w-0">
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-3xl font-bold wrap-break-word">{track.name}</h1>
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-3xl font-bold wrap-break-word">{track.name}</h1>
|
||||
{isDownloaded && (
|
||||
<CheckCircle className="h-6 w-6 text-green-500 shrink-0" />
|
||||
)}
|
||||
{isFailed && (
|
||||
<XCircle className="h-6 w-6 text-red-500 shrink-0" />
|
||||
)}
|
||||
</div>
|
||||
<p className="text-lg text-muted-foreground">{track.artists}</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3 text-sm">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Download, CheckCircle } from "lucide-react";
|
||||
import { Download, CheckCircle, XCircle } from "lucide-react";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import {
|
||||
Pagination,
|
||||
@@ -18,6 +18,7 @@ interface TrackListProps {
|
||||
sortBy: string;
|
||||
selectedTracks: string[];
|
||||
downloadedTracks: Set<string>;
|
||||
failedTracks: Set<string>;
|
||||
downloadingTrack: string | null;
|
||||
isDownloading: boolean;
|
||||
currentPage: number;
|
||||
@@ -36,6 +37,7 @@ export function TrackList({
|
||||
sortBy,
|
||||
selectedTracks,
|
||||
downloadedTracks,
|
||||
failedTracks,
|
||||
downloadingTrack,
|
||||
isDownloading,
|
||||
currentPage,
|
||||
@@ -165,6 +167,9 @@ export function TrackList({
|
||||
{downloadedTracks.has(track.isrc) && (
|
||||
<CheckCircle className="h-4 w-4 text-green-500 shrink-0" />
|
||||
)}
|
||||
{failedTracks.has(track.isrc) && (
|
||||
<XCircle className="h-4 w-4 text-red-500 shrink-0" />
|
||||
)}
|
||||
</div>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{track.artists}
|
||||
|
||||
Reference in New Issue
Block a user