134 lines
6.2 KiB
TypeScript
134 lines
6.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { RefreshCw, Music2 } from "lucide-react";
|
|
import { cn } from "@/utils/cn";
|
|
import { GradientSpinner } from "@/components/ui/GradientSpinner";
|
|
import { useAudio } from "@/lib/audio-context";
|
|
import { useDiscoverData } from "@/features/discover/hooks/useDiscoverData";
|
|
import { useDiscoverActions } from "@/features/discover/hooks/useDiscoverActions";
|
|
import { usePreviewPlayer } from "@/features/discover/hooks/usePreviewPlayer";
|
|
import { DiscoverHero } from "@/features/discover/components/DiscoverHero";
|
|
import { DiscoverActionBar } from "@/features/discover/components/DiscoverActionBar";
|
|
import { DiscoverSettings } from "@/features/discover/components/DiscoverSettings";
|
|
import { TrackList } from "@/features/discover/components/TrackList";
|
|
import { UnavailableAlbums } from "@/features/discover/components/UnavailableAlbums";
|
|
import { HowItWorks } from "@/features/discover/components/HowItWorks";
|
|
|
|
export default function DiscoverWeeklyPage() {
|
|
const { currentTrack, isPlaying } = useAudio();
|
|
const [showSettings, setShowSettings] = useState(false);
|
|
|
|
// Custom hooks - single source of truth for batch status from useDiscoverData
|
|
const { playlist, config, setConfig, loading, reloadData, batchStatus, refreshBatchStatus, setPendingGeneration, updateTrackLiked, isGenerating } = useDiscoverData();
|
|
const {
|
|
handleGenerate,
|
|
handleLike,
|
|
handlePlayPlaylist,
|
|
handlePlayTrack,
|
|
handleTogglePlay,
|
|
} = useDiscoverActions(playlist, reloadData, isGenerating, refreshBatchStatus, setPendingGeneration, updateTrackLiked);
|
|
const { currentPreview, handleTogglePreview } = usePreviewPlayer();
|
|
|
|
// Check if we're playing from this playlist
|
|
const isPlaylistPlaying = playlist?.tracks.some(
|
|
(t) => t.id === currentTrack?.id
|
|
);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center min-h-screen">
|
|
<GradientSpinner size="md" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen">
|
|
<DiscoverHero playlist={playlist} config={config} />
|
|
|
|
<DiscoverActionBar
|
|
playlist={playlist}
|
|
config={config}
|
|
isPlaylistPlaying={isPlaylistPlaying || false}
|
|
isPlaying={isPlaying}
|
|
onPlayToggle={isPlaylistPlaying && isPlaying ? handleTogglePlay : handlePlayPlaylist}
|
|
onGenerate={handleGenerate}
|
|
onToggleSettings={() => setShowSettings(!showSettings)}
|
|
isGenerating={isGenerating}
|
|
batchStatus={batchStatus}
|
|
/>
|
|
|
|
{showSettings && (
|
|
<DiscoverSettings
|
|
config={config}
|
|
onUpdateConfig={setConfig}
|
|
onPlaylistCleared={reloadData}
|
|
/>
|
|
)}
|
|
|
|
{/* Track Listing */}
|
|
<div className="px-4 md:px-8 pb-32">
|
|
{playlist && playlist.tracks.length > 0 ? (
|
|
<div className="space-y-6">
|
|
<TrackList
|
|
tracks={playlist.tracks}
|
|
currentTrack={currentTrack}
|
|
isPlaying={isPlaying}
|
|
onPlayTrack={handlePlayTrack}
|
|
onTogglePlay={handleTogglePlay}
|
|
onLike={handleLike}
|
|
/>
|
|
|
|
<UnavailableAlbums
|
|
unavailable={playlist.unavailable}
|
|
currentPreview={currentPreview}
|
|
onTogglePreview={handleTogglePreview}
|
|
/>
|
|
|
|
<HowItWorks />
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-col items-center justify-center py-24 text-center">
|
|
<div className="w-20 h-20 bg-gradient-to-br from-purple-600/20 to-yellow-600/20 rounded-full flex items-center justify-center mb-4 shadow-xl border border-white/10">
|
|
<Music2 className="w-10 h-10 text-purple-400" />
|
|
</div>
|
|
<h3 className="text-lg font-medium text-white mb-1">
|
|
No Discover Weekly Yet
|
|
</h3>
|
|
<p className="text-sm text-gray-500 mb-6 max-w-md">
|
|
Generate your first playlist based on your
|
|
listening history!
|
|
</p>
|
|
<button
|
|
onClick={handleGenerate}
|
|
disabled={isGenerating}
|
|
className={cn(
|
|
"flex items-center gap-2 px-6 py-3 rounded-full text-white font-semibold transition-all",
|
|
isGenerating
|
|
? "bg-white/5 cursor-not-allowed opacity-50"
|
|
: "bg-purple-600/20 hover:bg-purple-600/30 border border-purple-500/30 hover:scale-105"
|
|
)}
|
|
>
|
|
{isGenerating ? (
|
|
<>
|
|
<GradientSpinner size="sm" />
|
|
{batchStatus?.status === "scanning"
|
|
? "Importing tracks..."
|
|
: `Downloading... ${batchStatus?.completed || 0}/${batchStatus?.total || 0}`
|
|
}
|
|
</>
|
|
) : (
|
|
<>
|
|
<RefreshCw className="w-5 h-5" />
|
|
Generate Now
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|