import { useContext, useState, useRef, useEffect } from "react";
import { FaTimes, FaSync, FaCheckCircle, FaExclamationCircle, FaHourglassHalf, FaMusic, FaCompactDisc, FaStepForward } from "react-icons/fa";
import { QueueContext, type QueueItem, getStatus, getProgress, getCurrentTrackInfo, isActiveStatus, isTerminalStatus } from "@/contexts/queue-context";
import { useAuth } from "@/contexts/auth-context";
// Circular Progress Component
const CircularProgress = ({
progress,
isCompleted = false,
size = 60,
strokeWidth = 6
}: {
progress: number;
isCompleted?: boolean;
size?: number;
strokeWidth?: number;
}) => {
const radius = (size - strokeWidth) / 2;
const circumference = radius * 2 * Math.PI;
const strokeDashoffset = circumference - (progress / 100) * circumference;
return (
{/* Center content */}
{isCompleted ? (
) : (
{Math.round(progress)}%
)}
);
};
// Status styling configuration
const statusStyles = {
initializing: {
icon: ,
color: "text-info",
bgColor: "bg-gradient-to-r from-blue-50 to-blue-100 dark:from-blue-900/20 dark:to-blue-800/30",
borderColor: "border-info/30 dark:border-info/40",
name: "Initializing",
},
processing: {
icon: ,
color: "text-processing",
bgColor: "bg-gradient-to-r from-purple-50 to-purple-100 dark:from-purple-900/20 dark:to-purple-800/30",
borderColor: "border-processing/30 dark:border-processing/40",
name: "Processing",
},
downloading: {
icon: ,
color: "text-info",
bgColor: "bg-gradient-to-r from-blue-50 to-blue-100 dark:from-blue-900/20 dark:to-blue-800/30",
borderColor: "border-info/30 dark:border-info/40",
name: "Downloading",
},
"real-time": {
icon: ,
color: "text-info",
bgColor: "bg-gradient-to-r from-blue-50 to-blue-100 dark:from-blue-900/20 dark:to-blue-800/30",
borderColor: "border-info/30 dark:border-info/40",
name: "Downloading",
},
done: {
icon: ,
color: "text-success",
bgColor: "bg-gradient-to-r from-green-50 to-green-100 dark:from-green-900/20 dark:to-green-800/30",
borderColor: "border-success/30 dark:border-success/40",
name: "Done",
},
completed: {
icon: ,
color: "text-success",
bgColor: "bg-gradient-to-r from-green-50 to-green-100 dark:from-green-900/20 dark:to-green-800/30",
borderColor: "border-success/30 dark:border-success/40",
name: "Completed",
},
error: {
icon: ,
color: "text-error",
bgColor: "bg-gradient-to-r from-red-50 to-red-100 dark:from-red-900/20 dark:to-red-800/30",
borderColor: "border-error/30 dark:border-error/40",
name: "Error",
},
cancelled: {
icon: ,
color: "text-warning",
bgColor: "bg-gradient-to-r from-orange-50 to-orange-100 dark:from-orange-900/20 dark:to-orange-800/30",
borderColor: "border-warning/30 dark:border-warning/40",
name: "Cancelled",
},
skipped: {
icon: ,
color: "text-warning",
bgColor: "bg-gradient-to-r from-yellow-50 to-yellow-100 dark:from-yellow-900/20 dark:to-yellow-800/30",
borderColor: "border-warning/30 dark:border-warning/40",
name: "Skipped",
},
queued: {
icon: ,
color: "text-content-muted dark:text-content-muted-dark",
bgColor: "bg-gradient-to-r from-surface-muted to-surface-accent dark:from-surface-muted-dark dark:to-surface-accent-dark",
borderColor: "border-border dark:border-border-dark",
name: "Queued",
},
retrying: {
icon: ,
color: "text-warning",
bgColor: "bg-gradient-to-r from-orange-50 to-orange-100 dark:from-orange-900/20 dark:to-orange-800/30",
borderColor: "border-warning/30 dark:border-warning/40",
name: "Retrying",
},
} as const;
// Skipped Task Component
const SkippedTaskCard = ({ item }: { item: QueueItem }) => {
const { removeItem } = useContext(QueueContext) || {};
const trackInfo = getCurrentTrackInfo(item);
const TypeIcon = item.downloadType === "album" ? FaCompactDisc : FaMusic;
return (
{/* Main content */}
{item.artist}
{/* Show current track info for parent downloads */}
{(item.downloadType === "album" || item.downloadType === "playlist") && trackInfo.title && (
{trackInfo.current}/{trackInfo.total}: {trackInfo.title}
)}
{/* Status and actions */}
{/* Remove button */}
{/* Skip reason */}
{item.error && (
)}
);
};
// Cancelled Task Component
const CancelledTaskCard = ({ item }: { item: QueueItem }) => {
const { removeItem } = useContext(QueueContext) || {};
const trackInfo = getCurrentTrackInfo(item);
const TypeIcon = item.downloadType === "album" ? FaCompactDisc : FaMusic;
return (
{/* Main content */}
{item.artist}
{/* Show current track info for parent downloads */}
{(item.downloadType === "album" || item.downloadType === "playlist") && trackInfo.title && (
{trackInfo.current}/{trackInfo.total}: {trackInfo.title}
)}
{/* Status and actions */}
{/* Remove button */}
{/* Cancellation reason */}
{item.error && (
)}
);
};
const QueueItemCard = ({ item, cachedStatus }: { item: QueueItem, cachedStatus: string }) => {
const { removeItem, cancelItem } = useContext(QueueContext) || {};
const status = cachedStatus;
const progress = getProgress(item);
const trackInfo = getCurrentTrackInfo(item);
const styleInfo = statusStyles[status as keyof typeof statusStyles] || statusStyles.queued;
const isTerminal = isTerminalStatus(status);
const isActive = isActiveStatus(status);
// Get type icon
const TypeIcon = item.downloadType === "album" ? FaCompactDisc : FaMusic;
return (
{/* Main content */}
{styleInfo.icon}
{item.artist}
{/* Show current track info for parent downloads */}
{(item.downloadType === "album" || item.downloadType === "playlist") && trackInfo.title && (
{trackInfo.current}/{trackInfo.total}: {trackInfo.title}
)}
{/* Status and progress */}
{styleInfo.name}
{/* Summary info for completed downloads */}
{isTerminal && item.summary && item.downloadType !== "track" && (
{item.summary.total_successful}/{trackInfo.total || item.summary.total_successful + item.summary.total_failed + item.summary.total_skipped} tracks
)}
{/* Circular progress for active downloads */}
{isActive && progress !== undefined && (
)}
{/* Completed progress for finished downloads */}
{isTerminal && status === "done" && item.downloadType === "track" && (
)}
{/* Action buttons */}
{isTerminal ? (
) : (
)}
{/* Error message */}
{item.error && (
{status === "cancelled" ? "Cancelled: " : status === "skipped" ? "Skipped: " : "Error: "}
{item.error}
)}
{/* Summary for completed downloads with multiple tracks */}
{isTerminal && item.summary && item.downloadType !== "track" && (
Download Summary
{item.summary.total_successful + item.summary.total_failed + item.summary.total_skipped} tracks total
{item.summary.total_successful > 0 && (
{item.summary.total_successful} successful
)}
{item.summary.total_failed > 0 && (
{item.summary.total_failed} failed
)}
{item.summary.total_skipped > 0 && (
{item.summary.total_skipped} skipped
)}
)}
);
};
export const Queue = () => {
const context = useContext(QueueContext);
const { authEnabled, isAuthenticated } = useAuth();
// Check if user is authenticated (only relevant when auth is enabled)
const isUserAuthenticated = !authEnabled || isAuthenticated;
const [startY, setStartY] = useState(null);
const [isDragging, setIsDragging] = useState(false);
const [dragDistance, setDragDistance] = useState(0);
const queueRef = useRef(null);
const scrollContainerRef = useRef(null);
const headerRef = useRef(null);
const [canDrag, setCanDrag] = useState(false);
// Virtual scrolling state
const [visibleItemCount, setVisibleItemCount] = useState(7);
const [isLoadingMoreItems, setIsLoadingMoreItems] = useState(false);
// Track items that recently transitioned to terminal states
const [recentlyTerminated, setRecentlyTerminated] = useState