@@ -5,25 +5,55 @@ import { Suspense, useCallback, useEffect, useRef, useState } from "react";
55import "./styles.css" ;
66
77import { Image } from "@/components/Image" ;
8- import { createWidgetStyles } from "@/lib/apple-music/artwork" ;
98import imageLoader from "@/lib/imageLoader" ;
10- import type { RecentTracks } from "@/types/apple- music" ;
9+ import type { MusicWidgetData , MusicWidgetTrack } from "@/types/music" ;
1110import { PauseIcon } from "@/components/Icons/PauseIcon" ;
1211import { PlayIcon } from "@/components/Icons/PlayIcon" ;
1312
14- export function AppleMusicWidget ( { data } : { data : RecentTracks | undefined } ) {
15- if ( ! data ) {
16- return null ;
13+ function TrackAction ( {
14+ track,
15+ isPlaying,
16+ className,
17+ onToggle,
18+ } : {
19+ track : MusicWidgetTrack ;
20+ isPlaying : boolean ;
21+ className : string ;
22+ onToggle : ( track : MusicWidgetTrack ) => void ;
23+ } ) {
24+ if ( track . previewUrl ) {
25+ return (
26+ < button
27+ type = "button"
28+ aria-label = { `${ isPlaying ? "Pause" : "Play preview of" } ${ track . name } ` }
29+ className = { className }
30+ onClick = { ( ) => onToggle ( track ) }
31+ data-playing = { isPlaying ? "true" : "false" }
32+ >
33+ { isPlaying ? < PauseIcon /> : < PlayIcon /> }
34+ </ button >
35+ ) ;
1736 }
1837
19- const tracksList = data ;
38+ if ( ! track . url ) {
39+ return null ;
40+ }
2041
21- const firstTrack = tracksList ?. length > 0 ? tracksList [ 0 ] : null ;
22- const firstTrackImage = firstTrack ?. attributes . artwork . url
23- ? firstTrack . attributes . artwork . url . replace ( "{w}" , "700" ) . replace ( "{h}" , "245" )
24- : null ;
25- const widgetStyle = createWidgetStyles ( firstTrack ?. attributes . artwork ) ;
42+ return (
43+ < a
44+ aria-label = { `Open ${ track . name } ` }
45+ className = { className }
46+ rel = "noopener noreferrer nofollow"
47+ target = "_blank"
48+ href = { track . url }
49+ data-playing = "false"
50+ >
51+ < PlayIcon />
52+ </ a >
53+ ) ;
54+ }
2655
56+ export function AppleMusicWidget ( { data } : { data : MusicWidgetData | undefined } ) {
2757 const [ currentTrackId , setCurrentTrackId ] = useState < string | null > ( null ) ;
2858 const [ isPlaying , setIsPlaying ] = useState ( false ) ;
2959 const audioRef = useRef < HTMLAudioElement | null > ( null ) ;
@@ -72,13 +102,18 @@ export function AppleMusicWidget({ data }: { data: RecentTracks | undefined }) {
72102 } ;
73103 } , [ ] ) ;
74104
105+ const tracksList = data ?. tracks ?? [ ] ;
106+ const firstTrack = tracksList . length > 0 ? tracksList [ 0 ] : null ;
107+ const firstTrackImage = firstTrack ?. artworkUrl ?? null ;
108+ const widgetStyle = data ?. style ;
109+
75110 const handleToggleTrack = useCallback (
76- async ( track : RecentTracks [ number ] ) => {
77- const previewUrl = track ?. attributes ?. previews ?. [ 0 ] ?. url ;
111+ async ( track : MusicWidgetTrack ) => {
112+ const previewUrl = track . previewUrl ;
78113
79114 if ( ! previewUrl ) {
80- if ( track ?. attributes ? .url ) {
81- window . open ( track . attributes . url , "_blank" , "noopener,noreferrer" ) ;
115+ if ( track . url ) {
116+ window . open ( track . url , "_blank" , "noopener,noreferrer" ) ;
82117 }
83118 return ;
84119 }
@@ -123,6 +158,10 @@ export function AppleMusicWidget({ data }: { data: RecentTracks | undefined }) {
123158 [ currentTrackId , isPlaying ] ,
124159 ) ;
125160
161+ if ( ! data || ! firstTrack ) {
162+ return null ;
163+ }
164+
126165 return (
127166 < div id = "applemusic-widget" style = { widgetStyle } >
128167 < Suspense fallback = { < div > Loading...</ div > } >
@@ -137,7 +176,7 @@ export function AppleMusicWidget({ data }: { data: RecentTracks | undefined }) {
137176 } }
138177 >
139178 < Image
140- alt = { firstTrack . attributes . name }
179+ alt = { firstTrack . name }
141180 src = { imageLoader ( {
142181 src : firstTrackImage ,
143182 width : 700 ,
@@ -156,84 +195,70 @@ export function AppleMusicWidget({ data }: { data: RecentTracks | undefined }) {
156195 < div className = "applemusic-widget-latest-overlay" >
157196 < div className = "applemusic-widget-latest-actions" >
158197 < div className = "applemusic-widget-latest-meta" >
159- < h3 > { firstTrack . attributes . name } </ h3 >
160- < span > { firstTrack . attributes . artistName } </ span >
161- < span > { firstTrack . attributes . albumName } </ span >
198+ < h3 > { firstTrack . name } </ h3 >
199+ < span > { firstTrack . artistName } </ span >
200+ < span > { firstTrack . albumName } </ span >
162201 </ div >
163- < button
164- type = "button"
165- aria-label = { `${
166- isTrackPlaying ( firstTrack . id ) ? "Pause" : "Play preview of"
167- } ${ firstTrack . attributes . name } `}
202+ < TrackAction
203+ track = { firstTrack }
168204 className = "trackLinkPlay"
169- onClick = { ( ) => handleToggleTrack ( firstTrack ) }
170- data-playing = { isTrackPlaying ( firstTrack . id ) ? "true" : "false" }
171- >
172- { isTrackPlaying ( firstTrack . id ) ? < PauseIcon /> : < PlayIcon /> }
173- </ button >
205+ isPlaying = { isTrackPlaying ( firstTrack . id ) }
206+ onToggle = { handleToggleTrack }
207+ />
174208 </ div >
175209 </ div >
210+ { firstTrack . isNowPlaying ? (
211+ < div className = "absolute left-0 bottom-0 z-10 bg-[#010517] text-primary-foreground px-1 py-1 text-sm" >
212+ < span > Now Playing</ span >
213+ </ div >
214+ ) : null }
176215 </ div >
177216 < div className = "applemusic-widget-tracks" >
178- { tracksList ?. map ( ( track , index ) => {
179- if ( index !== 0 ) {
180- const trackImage = track . attributes . artwork . url
181- ? track . attributes . artwork . url . replace ( "{w}" , "700" ) . replace ( "{h}" , "245" )
182- : null ;
183-
184- const isPlayingTrack = isTrackPlaying ( track . id ) ;
185-
186- return (
187- < div
188- className = "applemusic-widget-track-item"
189- key = { `${ track . id } _${ track . attributes . issrc } ` }
190- >
191- < div className = "applemusic-widget-track-item-image" >
192- < div className = "applemusic-widget-track-item-image-inner" >
193- { trackImage ? (
194- < Image
195- width = "53"
196- height = "53"
197- loading = "lazy"
198- alt = { track . attributes . albumName }
199- src = { imageLoader ( {
200- src : trackImage ,
201- width : 53 ,
202- } ) }
203- style = { {
204- objectFit : "cover" ,
205- } }
206- unoptimized
207- />
208- ) : (
209- < div
210- className = "applemusic-widget-track-item-image-placeholder"
211- aria-hidden = "true"
212- />
213- ) }
214- < button
215- type = "button"
216- className = "applemusic-widget-track-item-play"
217- onClick = { ( ) => handleToggleTrack ( track ) }
218- aria-label = { `${
219- isPlayingTrack ? "Pause" : "Play preview of"
220- } ${ track . attributes . name } `}
221- data-playing = { isPlayingTrack ? "true" : "false" }
222- >
223- { isPlayingTrack ? < PauseIcon /> : < PlayIcon /> }
224- </ button >
225- </ div >
217+ { tracksList . slice ( 1 ) . map ( ( track ) => {
218+ const isPlayingTrack = isTrackPlaying ( track . id ) ;
219+
220+ return (
221+ < div className = "applemusic-widget-track-item" key = { track . id } >
222+ < div className = "applemusic-widget-track-item-image" >
223+ < div className = "applemusic-widget-track-item-image-inner" >
224+ { track . artworkUrl ? (
225+ < Image
226+ width = "53"
227+ height = "53"
228+ loading = "lazy"
229+ alt = { track . albumName }
230+ src = { imageLoader ( {
231+ src : track . artworkUrl ,
232+ width : 53 ,
233+ } ) }
234+ style = { {
235+ objectFit : "cover" ,
236+ } }
237+ unoptimized
238+ />
239+ ) : (
240+ < div
241+ className = "applemusic-widget-track-item-image-placeholder"
242+ aria-hidden = "true"
243+ />
244+ ) }
245+ < TrackAction
246+ track = { track }
247+ className = "applemusic-widget-track-item-play"
248+ isPlaying = { isPlayingTrack }
249+ onToggle = { handleToggleTrack }
250+ />
226251 </ div >
227- < div className = "applemusic-widget-track-item-content" >
228- < div className = "applemusic-widget-track-item-text " >
229- < h3 > { track . attributes . name } </ h3 >
230- < span > { track . attributes . artistName } </ span >
231- < span > { track . attributes . albumName } </ span >
232- </ div >
252+ </ div >
253+ < div className = "applemusic-widget-track-item-content " >
254+ < div className = "applemusic-widget- track-item-text" >
255+ < h3 > { track . name } </ h3 >
256+ < span > { track . artistName } </ span >
257+ < span > { track . albumName } </ span >
233258 </ div >
234259 </ div >
235- ) ;
236- }
260+ </ div >
261+ ) ;
237262 } ) }
238263 </ div >
239264 </ >
0 commit comments