Add always-visible player, play button on sidebar thumbnail, and GitHub link
- Player: Show minimal placeholder when no song is playing instead of hiding - RightSidebar: Add play button overlay on cover art thumbnail - Hover to reveal play/pause button - Click anywhere on cover to play - Shows pause icon when song is currently playing - SettingsModal: Add GitHub repo link for issues and feature requests
This commit is contained in:
@@ -959,6 +959,9 @@ export default function App() {
|
||||
onNavigateToSong={handleNavigateToSong}
|
||||
isLiked={selectedSong ? likedSongIds.has(selectedSong.id) : false}
|
||||
onToggleLike={toggleLike}
|
||||
onPlay={playSong}
|
||||
isPlaying={isPlaying}
|
||||
currentSong={currentSong}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -1086,6 +1089,9 @@ export default function App() {
|
||||
onNavigateToSong={handleNavigateToSong}
|
||||
isLiked={selectedSong ? likedSongIds.has(selectedSong.id) : false}
|
||||
onToggleLike={toggleLike}
|
||||
onPlay={playSong}
|
||||
isPlaying={isPlaying}
|
||||
currentSong={currentSong}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+13
-1
@@ -75,7 +75,19 @@ export const Player: React.FC<PlayerProps> = ({
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [isFullscreen]);
|
||||
|
||||
if (!currentSong) return null;
|
||||
// Show minimal player when no song is playing
|
||||
if (!currentSong) {
|
||||
return (
|
||||
<div className="h-20 lg:h-24 bg-white dark:bg-black/95 backdrop-blur border-t border-zinc-200 dark:border-white/10 flex items-center justify-center z-50 transition-colors duration-300 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)] dark:shadow-none">
|
||||
<div className="flex items-center gap-3 text-zinc-400 dark:text-zinc-600">
|
||||
<div className="w-10 h-10 lg:w-12 lg:h-12 rounded bg-zinc-200 dark:bg-zinc-800 flex items-center justify-center">
|
||||
<Play size={20} className="text-zinc-400 dark:text-zinc-600" />
|
||||
</div>
|
||||
<span className="text-sm font-medium">Select a song to play</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const formatTime = (time: number) => {
|
||||
if (isNaN(time)) return "0:00";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Song } from '../types';
|
||||
import { Heart, Share2, Play, MoreHorizontal, X, Copy, Wand2, MoreVertical, Download, Repeat, Video, Music, Link as LinkIcon, Sparkles, Globe, Lock, Trash2, Edit3, Layers } from 'lucide-react';
|
||||
import { Heart, Share2, Play, Pause, MoreHorizontal, X, Copy, Wand2, MoreVertical, Download, Repeat, Video, Music, Link as LinkIcon, Sparkles, Globe, Lock, Trash2, Edit3, Layers } from 'lucide-react';
|
||||
import { songsApi } from '../services/api';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import { SongDropdownMenu } from './SongDropdownMenu';
|
||||
@@ -19,9 +19,12 @@ interface RightSidebarProps {
|
||||
onToggleLike?: (song: Song) => void;
|
||||
onDelete?: (song: Song) => void;
|
||||
onAddToPlaylist?: (song: Song) => void;
|
||||
onPlay?: (song: Song) => void;
|
||||
isPlaying?: boolean;
|
||||
currentSong?: Song | null;
|
||||
}
|
||||
|
||||
export const RightSidebar: React.FC<RightSidebarProps> = ({ song, onClose, onOpenVideo, onReuse, onSongUpdate, onNavigateToProfile, onNavigateToSong, isLiked, onToggleLike, onDelete, onAddToPlaylist }) => {
|
||||
export const RightSidebar: React.FC<RightSidebarProps> = ({ song, onClose, onOpenVideo, onReuse, onSongUpdate, onNavigateToProfile, onNavigateToSong, isLiked, onToggleLike, onDelete, onAddToPlaylist, onPlay, isPlaying, currentSong }) => {
|
||||
const { token, user } = useAuth();
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
const [isOwner, setIsOwner] = useState(false);
|
||||
@@ -64,7 +67,10 @@ export const RightSidebar: React.FC<RightSidebarProps> = ({ song, onClose, onOpe
|
||||
<div className="p-5 space-y-6">
|
||||
|
||||
{/* Cover Art */}
|
||||
<div className="group relative aspect-square w-full rounded-xl overflow-hidden shadow-2xl bg-zinc-200 dark:bg-zinc-800 ring-1 ring-black/5 dark:ring-white/10">
|
||||
<div
|
||||
className="group relative aspect-square w-full rounded-xl overflow-hidden shadow-2xl bg-zinc-200 dark:bg-zinc-800 ring-1 ring-black/5 dark:ring-white/10 cursor-pointer"
|
||||
onClick={() => onPlay?.(song)}
|
||||
>
|
||||
{song.coverUrl ? (
|
||||
<img src={song.coverUrl} alt={song.title} className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105" onError={(e) => { e.currentTarget.style.display = 'none'; }} />
|
||||
) : null}
|
||||
@@ -73,6 +79,23 @@ export const RightSidebar: React.FC<RightSidebarProps> = ({ song, onClose, onOpe
|
||||
{/* Overlay Gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-60"></div>
|
||||
|
||||
{/* Play Button Overlay */}
|
||||
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onPlay?.(song);
|
||||
}}
|
||||
className="w-16 h-16 rounded-full bg-white/95 dark:bg-white text-black flex items-center justify-center shadow-2xl hover:scale-110 transition-transform"
|
||||
>
|
||||
{isPlaying && currentSong?.id === song.id ? (
|
||||
<Pause size={28} fill="currentColor" />
|
||||
) : (
|
||||
<Play size={28} fill="currentColor" className="ml-1" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-4 left-4 right-4 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2 text-white">
|
||||
<Play size={16} fill="currentColor" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { X, User as UserIcon, Palette, Info, Edit3, ExternalLink } from 'lucide-react';
|
||||
import { X, User as UserIcon, Palette, Info, Edit3, ExternalLink, Github } from 'lucide-react';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import { EditProfileModal } from './EditProfileModal';
|
||||
|
||||
@@ -144,18 +144,32 @@ export const SettingsModal: React.FC<SettingsModalProps> = ({ isOpen, onClose, t
|
||||
Powered by ACE-Step 1.5. Open source and free to use.
|
||||
</p>
|
||||
<div className="pt-3 border-t border-zinc-200 dark:border-zinc-700/50 mt-4">
|
||||
<p className="text-zinc-900 dark:text-white font-medium mb-2">Created by Ambsd</p>
|
||||
<a
|
||||
href="https://x.com/AmbsdOP"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 bg-black dark:bg-white text-white dark:text-black rounded-lg text-sm font-medium hover:bg-zinc-800 dark:hover:bg-zinc-200 transition-colors"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
Follow @AmbsdOP
|
||||
</a>
|
||||
<p className="text-zinc-900 dark:text-white font-medium mb-3">Created by Ambsd</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<a
|
||||
href="https://x.com/AmbsdOP"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 bg-black dark:bg-white text-white dark:text-black rounded-lg text-sm font-medium hover:bg-zinc-800 dark:hover:bg-zinc-200 transition-colors"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
Follow @AmbsdOP
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/fspecii/ace-step-ui"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 bg-zinc-800 dark:bg-zinc-700 text-white rounded-lg text-sm font-medium hover:bg-zinc-700 dark:hover:bg-zinc-600 transition-colors"
|
||||
>
|
||||
<Github size={16} />
|
||||
GitHub Repo
|
||||
</a>
|
||||
</div>
|
||||
<p className="text-xs text-zinc-400 dark:text-zinc-500 mt-3">
|
||||
Report issues or request features on GitHub
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user