.refine check availibility

This commit is contained in:
afkarxyz
2026-04-13 20:43:37 +07:00
parent 967feb93e1
commit 24d640443a
5 changed files with 192 additions and 25 deletions
@@ -0,0 +1,83 @@
import type { ReactNode } from "react";
import type { TrackAvailability } from "@/types/api";
import { openExternal } from "@/lib/utils";
import { AmazonAvailabilityIcon, QobuzAvailabilityIcon, TidalAvailabilityIcon } from "./PlatformIcons";
interface AvailabilityLinkEntry {
id: string;
found: boolean;
url?: string;
icon: ReactNode;
}
function getAvailabilityLinkEntries(availability: TrackAvailability): AvailabilityLinkEntry[] {
const tidalUrl = availability.tidal_url?.trim() || "";
const qobuzUrl = availability.qobuz_url?.trim() || "";
const amazonUrl = availability.amazon_url?.trim() || "";
return [
{
id: "tidal",
found: tidalUrl !== "",
url: tidalUrl,
icon: <TidalAvailabilityIcon className={`w-4 h-4 shrink-0 ${tidalUrl ? "text-green-500" : "text-red-500"}`}/>,
},
{
id: "qobuz",
found: qobuzUrl !== "",
url: qobuzUrl,
icon: <QobuzAvailabilityIcon className={`w-4 h-4 shrink-0 ${qobuzUrl ? "text-green-500" : "text-red-500"}`}/>,
},
{
id: "amazon",
found: amazonUrl !== "",
url: amazonUrl,
icon: <AmazonAvailabilityIcon className={`w-4 h-4 shrink-0 ${amazonUrl ? "text-green-500" : "text-red-500"}`}/>,
},
];
}
export function hasAvailabilityLinks(availability?: TrackAvailability): boolean {
if (!availability) {
return false;
}
return getAvailabilityLinkEntries(availability).some((entry) => entry.found);
}
export function AvailabilityLinks({ availability }: {
availability?: TrackAvailability;
}) {
if (!availability) {
return <p>Check Availability</p>;
}
const entries = getAvailabilityLinkEntries(availability);
return (
<div className="flex flex-col gap-1.5 w-[260px] max-w-[260px] pointer-events-auto">
{entries.map((entry) => entry.found ? (
<button
key={entry.id}
type="button"
onClick={() => entry.url && openExternal(entry.url)}
className="flex items-center gap-2 text-left text-xs hover:underline min-w-0 cursor-pointer"
title={entry.url}
>
{entry.icon}
<span className="truncate whitespace-nowrap leading-5 min-w-0">
{entry.url}
</span>
</button>
) : (
<div
key={entry.id}
className="flex items-center gap-2 text-left text-xs min-w-0"
>
{entry.icon}
<span className="truncate whitespace-nowrap leading-5 min-w-0 text-red-500">
Not Found
</span>
</div>
))}
</div>
);
}
+4 -8
View File
@@ -4,8 +4,8 @@ import { Download, FolderOpen, CheckCircle, XCircle, FileText, FileCheck, Globe,
import { Spinner } from "@/components/ui/spinner";
import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip";
import type { TrackMetadata, TrackAvailability } from "@/types/api";
import { TidalAvailabilityIcon, QobuzAvailabilityIcon, AmazonAvailabilityIcon } from "./PlatformIcons";
import { usePreview } from "@/hooks/usePreview";
import { AvailabilityLinks, hasAvailabilityLinks } from "./AvailabilityLinks";
interface TrackInfoProps {
track: TrackMetadata & {
album_name: string;
@@ -135,15 +135,11 @@ export function TrackInfo({ track, isDownloading, downloadingTrack, isDownloaded
{track.spotify_id && onCheckAvailability && (<Tooltip>
<TooltipTrigger asChild>
<Button onClick={() => onCheckAvailability(track.spotify_id!)} variant="outline" size="icon" disabled={checkingAvailability}>
{checkingAvailability ? (<Spinner />) : availability ? (<CheckCircle className="h-4 w-4 text-green-500"/>) : (<Globe className="h-4 w-4"/>)}
{checkingAvailability ? (<Spinner />) : availability ? (hasAvailabilityLinks(availability) ? (<CheckCircle className="h-4 w-4 text-green-500"/>) : (<XCircle className="h-4 w-4 text-red-500"/>)) : (<Globe className="h-4 w-4"/>)}
</Button>
</TooltipTrigger>
<TooltipContent>
{availability ? (<div className="flex items-center gap-2">
<TidalAvailabilityIcon className={`w-4 h-4 ${availability.tidal ? "text-green-500" : "text-red-500"}`}/>
<QobuzAvailabilityIcon className={`w-4 h-4 ${availability.qobuz ? "text-green-500" : "text-red-500"}`}/>
<AmazonAvailabilityIcon className={`w-4 h-4 ${availability.amazon ? "text-green-500" : "text-red-500"}`}/>
</div>) : (<p>Check Availability</p>)}
<TooltipContent className="pointer-events-auto">
<AvailabilityLinks availability={availability}/>
</TooltipContent>
</Tooltip>)}
{isDownloaded && (<Tooltip>
+20 -8
View File
@@ -5,8 +5,8 @@ import { Spinner } from "@/components/ui/spinner";
import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip";
import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "@/components/ui/pagination";
import type { TrackMetadata, TrackAvailability } from "@/types/api";
import { TidalAvailabilityIcon, QobuzAvailabilityIcon, AmazonAvailabilityIcon } from "./PlatformIcons";
import { usePreview } from "@/hooks/usePreview";
import { AvailabilityLinks, hasAvailabilityLinks } from "./AvailabilityLinks";
interface TrackListProps {
tracks: TrackMetadata[];
searchQuery: string;
@@ -172,6 +172,22 @@ export function TrackList({ tracks, searchQuery, sortBy, selectedTracks, downloa
return plays;
return num.toLocaleString();
};
const getAvailabilityButtonIcon = (spotifyId?: string) => {
if (!spotifyId) {
return <Globe className="h-4 w-4"/>;
}
if (checkingAvailabilityTrack === spotifyId) {
return <Spinner />;
}
const availability = availabilityMap?.get(spotifyId);
if (!availability) {
return <Globe className="h-4 w-4"/>;
}
if (hasAvailabilityLinks(availability)) {
return <CheckCircle className="h-4 w-4 text-green-500"/>;
}
return <XCircle className="h-4 w-4 text-red-500"/>;
};
return (<div className="space-y-4">
<div className="rounded-md border">
<div className="overflow-x-auto">
@@ -323,15 +339,11 @@ export function TrackList({ tracks, searchQuery, sortBy, selectedTracks, downloa
{track.spotify_id && onCheckAvailability && (<Tooltip>
<TooltipTrigger asChild>
<Button onClick={() => onCheckAvailability(track.spotify_id!)} size="icon" variant="outline" disabled={checkingAvailabilityTrack === track.spotify_id}>
{checkingAvailabilityTrack === track.spotify_id ? (<Spinner />) : availabilityMap?.has(track.spotify_id) ? (<CheckCircle className="h-4 w-4 text-green-500"/>) : (<Globe className="h-4 w-4"/>)}
{getAvailabilityButtonIcon(track.spotify_id)}
</Button>
</TooltipTrigger>
<TooltipContent>
{availabilityMap?.has(track.spotify_id) ? (<div className="flex items-center gap-2">
<TidalAvailabilityIcon className={`w-4 h-4 ${availabilityMap.get(track.spotify_id)?.tidal ? "text-green-500" : "text-red-500"}`}/>
<QobuzAvailabilityIcon className={`w-4 h-4 ${availabilityMap.get(track.spotify_id)?.qobuz ? "text-green-500" : "text-red-500"}`}/>
<AmazonAvailabilityIcon className={`w-4 h-4 ${availabilityMap.get(track.spotify_id)?.amazon ? "text-green-500" : "text-red-500"}`}/>
</div>) : (<p>Check Availability</p>)}
<TooltipContent className="pointer-events-auto">
<AvailabilityLinks availability={track.spotify_id ? availabilityMap?.get(track.spotify_id) : undefined}/>
</TooltipContent>
</Tooltip>)}
</div>
+1 -4
View File
@@ -13,9 +13,6 @@ export function useAvailability() {
setError("No Spotify ID provided");
return null;
}
if (availabilityMap.has(spotifyId)) {
return availabilityMap.get(spotifyId)!;
}
setChecking(true);
setCheckingTrackId(spotifyId);
setError(null);
@@ -41,7 +38,7 @@ export function useAvailability() {
setChecking(false);
setCheckingTrackId(null);
}
}, [availabilityMap]);
}, []);
const getAvailability = useCallback((spotifyId: string) => {
return availabilityMap.get(spotifyId);
}, [availabilityMap]);