Initial release v1.0.0
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user