source: frontend/src/components/MiniPlayer.tsx@ c684a6d

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

add experimental video miniplayer

  • Property mode set to 100644
File size: 5.9 KB
RevLine 
[9c1dcc7]1import { usePlayer } from "../context/playerContext";
2
3const MiniPlayer = () => {
4 const { currentSong, isMinimized, stop, toggleMinimize } = usePlayer();
5
6 if (!currentSong) return null;
7
8 return (
9 <>
10 {/* Fullscreen overlay - only visible when not minimized */}
11 <div
12 className={`fixed inset-0 z-100 bg-[#0a0a0f] flex flex-col transition-opacity duration-300 ${
13 isMinimized
14 ? "opacity-0 pointer-events-none"
15 : "opacity-100 pointer-events-auto"
16 }`}
17 >
18 {/* Header bar */}
19 <div className="flex items-center justify-between px-6 py-4 bg-[#1a1a2e]/80 backdrop-blur-sm">
20 <div className="flex items-center gap-4">
21 <img
22 src={currentSong.cover || "/favicon.png"}
23 alt={currentSong.title}
24 className="w-12 h-12 rounded-lg object-cover shadow-lg"
25 onError={(e) => {
26 (e.target as HTMLImageElement).src = "/favicon.png";
27 }}
28 />
29 <div>
30 <p className="text-white font-semibold text-lg">
31 {currentSong.title}
32 </p>
33 <p className="text-gray-400 text-sm">{currentSong.artist}</p>
34 </div>
35 </div>
36
37 <div className="flex items-center gap-2">
38 {/* Minimize */}
39 <button
40 onClick={toggleMinimize}
41 className="text-gray-400 hover:text-white p-2.5 rounded-full hover:bg-white/10 transition-all"
42 aria-label="Minimize player"
43 >
44 <svg
45 className="w-6 h-6"
46 fill="none"
47 stroke="currentColor"
48 viewBox="0 0 24 24"
49 >
50 <path
51 strokeLinecap="round"
52 strokeLinejoin="round"
53 strokeWidth={2}
54 d="M19 14l-7 7m0 0l-7-7m7 7V3"
55 />
56 </svg>
57 </button>
58
59 {/* Close */}
60 <button
61 onClick={stop}
62 className="text-gray-400 hover:text-red-400 p-2.5 rounded-full hover:bg-white/10 transition-all"
63 aria-label="Close player"
64 >
65 <svg
66 className="w-6 h-6"
67 fill="none"
68 stroke="currentColor"
69 viewBox="0 0 24 24"
70 >
71 <path
72 strokeLinecap="round"
73 strokeLinejoin="round"
74 strokeWidth={2}
75 d="M6 18L18 6M6 6l12 12"
76 />
77 </svg>
78 </button>
79 </div>
80 </div>
81
82 {/* Video container - visible only in fullscreen */}
83 <div className="flex-1 flex items-center justify-center p-4">
84 <div
85 className={`w-full max-w-5xl aspect-video rounded-xl overflow-hidden shadow-2xl ${
86 isMinimized ? "invisible" : "visible"
87 }`}
88 >
89 {/* Single iframe - always mounted to preserve playback state */}
90 <iframe
91 key={currentSong.embedUrl}
92 src={`${currentSong.embedUrl}?autoplay=1`}
93 title={currentSong.title}
94 className="w-full h-full"
95 allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
96 allowFullScreen
97 />
98 </div>
99 </div>
100 </div>
101
102 {/* Minimized bar - always visible at bottom */}
103 <div
104 className={`fixed bottom-0 left-0 right-0 z-100 h-16 bg-[#1a1a2e] border-t border-white/10 shadow-lg transition-transform duration-300 ${
105 isMinimized ? "translate-y-0" : "translate-y-full"
106 }`}
107 >
108 <div className="flex items-center justify-between px-4 h-full max-w-7xl mx-auto">
109 {/* Song info */}
110 <div className="flex items-center gap-3 min-w-0 flex-1">
111 <img
112 src={currentSong.cover || "/favicon.png"}
113 alt={currentSong.title}
114 className="w-10 h-10 rounded object-cover shrink-0"
115 onError={(e) => {
116 (e.target as HTMLImageElement).src = "/favicon.png";
117 }}
118 />
119 <div className="min-w-0">
120 <p className="text-white text-sm font-medium truncate">
121 {currentSong.title}
122 </p>
123 <p className="text-gray-400 text-xs truncate">
124 {currentSong.artist}
125 </p>
126 </div>
127 </div>
128
129 {/* Now playing indicator */}
130 <div className="hidden sm:flex items-center gap-1.5 px-4">
131 <div className="flex items-end gap-0.5 h-4">
132 <div
133 className="w-1 bg-[#1db954] rounded-full animate-[bounce_0.6s_ease-in-out_infinite]"
134 style={{ height: "60%" }}
135 />
136 <div
137 className="w-1 bg-[#1db954] rounded-full animate-[bounce_0.6s_ease-in-out_infinite_0.1s]"
138 style={{ height: "100%" }}
139 />
140 <div
141 className="w-1 bg-[#1db954] rounded-full animate-[bounce_0.6s_ease-in-out_infinite_0.2s]"
142 style={{ height: "40%" }}
143 />
144 <div
145 className="w-1 bg-[#1db954] rounded-full animate-[bounce_0.6s_ease-in-out_infinite_0.3s]"
146 style={{ height: "80%" }}
147 />
148 </div>
149 <span className="text-[#1db954] text-xs font-medium ml-1">
150 Playing
151 </span>
152 </div>
153
154 {/* Controls */}
155 <div className="flex items-center gap-1">
156 {/* Expand - shows fullscreen to allow seeking */}
157 <button
158 onClick={toggleMinimize}
159 className="text-gray-400 hover:text-white p-2 rounded-full hover:bg-white/10 transition-all"
160 aria-label="Expand player to seek"
161 title="Expand to seek in video"
162 >
163 <svg
164 className="w-5 h-5"
165 fill="none"
166 stroke="currentColor"
167 viewBox="0 0 24 24"
168 >
169 <path
170 strokeLinecap="round"
171 strokeLinejoin="round"
172 strokeWidth={2}
173 d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"
174 />
175 </svg>
176 </button>
177
178 {/* Close */}
179 <button
180 onClick={stop}
181 className="text-gray-400 hover:text-red-400 p-2 rounded-full hover:bg-white/10 transition-all"
182 aria-label="Close player"
183 >
184 <svg
185 className="w-5 h-5"
186 fill="none"
187 stroke="currentColor"
188 viewBox="0 0 24 24"
189 >
190 <path
191 strokeLinecap="round"
192 strokeLinejoin="round"
193 strokeWidth={2}
194 d="M6 18L18 6M6 6l12 12"
195 />
196 </svg>
197 </button>
198 </div>
199 </div>
200 </div>
201 </>
202 );
203};
204
205export default MiniPlayer;
Note: See TracBrowser for help on using the repository browser.