source: frontend/src/components/search/SongResult.tsx@ 9c1dcc7

main
Last change on this file since 9c1dcc7 was 9c1dcc7, checked in by Filip Gavrilovski <filipgavrilovski28@…>, 5 months ago

add experimental video miniplayer

  • Property mode set to 100644
File size: 2.3 KB
Line 
1import { useNavigate } from "react-router-dom";
2import { usePlayer } from "../../context/playerContext";
3import type { Song } from "../../utils/types";
4
5interface SongResultProps {
6 song: Song;
7}
8
9const toEmbedUrl = (url: string): string => {
10 try {
11 const parsed = new URL(url);
12 if (
13 (parsed.hostname === "www.youtube.com" ||
14 parsed.hostname === "youtube.com") &&
15 parsed.searchParams.has("v")
16 ) {
17 return `https://www.youtube.com/embed/${parsed.searchParams.get("v")}`;
18 }
19 if (parsed.hostname === "youtu.be") {
20 return `https://www.youtube.com/embed${parsed.pathname}`;
21 }
22 return url;
23 } catch {
24 return url;
25 }
26};
27
28const SongResult = ({ song }: SongResultProps) => {
29 const navigate = useNavigate();
30 const { play, currentSong } = usePlayer();
31
32 return (
33 <div
34 onClick={() => navigate(`/songs/${song.id}`)}
35 className="flex items-center gap-4 p-3 rounded-lg hover:bg-white/5 cursor-pointer transition-colors group"
36 >
37 <img
38 src={song.cover || "/favicon.png"}
39 alt={song.title}
40 className="w-12 h-12 rounded object-cover"
41 onError={(e) => {
42 (e.target as HTMLImageElement).src = "/favicon.png";
43 }}
44 />
45 <div className="flex-1 min-w-0">
46 <p className="text-white font-medium truncate">{song.title}</p>
47 <p className="text-sm text-gray-400 truncate">
48 Song • {song.releasedBy}
49 </p>
50 </div>
51 <span className="text-xs text-gray-500 uppercase tracking-wider mr-2">
52 {song.genre}
53 </span>
54 {song.link && (
55 <button
56 onClick={(e) => {
57 e.stopPropagation();
58 play({
59 id: song.id,
60 title: song.title,
61 artist: song.releasedBy,
62 cover: song.cover,
63 embedUrl: toEmbedUrl(song.link!),
64 });
65 }}
66 className={`p-2 rounded-full transition-all opacity-0 group-hover:opacity-100 ${
67 currentSong?.id === song.id
68 ? "bg-white text-[#1db954]"
69 : "bg-[#1db954] text-black hover:scale-110"
70 }`}
71 aria-label={currentSong?.id === song.id ? "Now playing" : "Play song"}
72 >
73 {currentSong?.id === song.id ? (
74 <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
75 <path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" />
76 </svg>
77 ) : (
78 <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
79 <path d="M8 5v14l11-7z" />
80 </svg>
81 )}
82 </button>
83 )}
84 </div>
85 );
86};
87
88export default SongResult;
Note: See TracBrowser for help on using the repository browser.