2023-07-29 22:40:36 +00:00
|
|
|
'use client'
|
|
|
|
|
2022-12-06 12:54:34 +00:00
|
|
|
import useSWR from 'swr'
|
|
|
|
import fetcher from '@/lib/fetcher'
|
2023-01-14 19:31:05 +00:00
|
|
|
import Image from 'next/image'
|
2022-12-06 12:54:34 +00:00
|
|
|
import clsx from 'clsx'
|
2024-09-27 20:27:23 +00:00
|
|
|
import {ElementType, ReactElement} from 'react'
|
|
|
|
import Link from 'next/link'
|
2022-12-06 12:54:34 +00:00
|
|
|
|
2023-01-14 19:31:05 +00:00
|
|
|
type Status = {
|
|
|
|
as?: ElementType
|
|
|
|
isPlaying: boolean
|
|
|
|
}
|
|
|
|
|
|
|
|
type Artist = {
|
|
|
|
as?: ElementType
|
|
|
|
artist: string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Title = {
|
|
|
|
as?: ElementType
|
|
|
|
title: string
|
|
|
|
songUrl: string
|
|
|
|
className?: string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Album = {
|
|
|
|
album: string
|
|
|
|
albumImageUrl: string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Song = {
|
|
|
|
as?: ElementType
|
|
|
|
} & Artist & Title & Album & Status
|
|
|
|
|
2023-02-22 22:40:36 +00:00
|
|
|
type PlayerStateResponse = {
|
|
|
|
data: Song
|
|
|
|
error: string
|
|
|
|
isLoading: boolean
|
|
|
|
}
|
|
|
|
|
2023-01-18 17:02:44 +00:00
|
|
|
function usePlayerState(path: string) {
|
2024-08-17 22:21:51 +00:00
|
|
|
const {data, error, isLoading} = useSWR(`/api/spotify/${path}`, fetcher) as PlayerStateResponse
|
2022-12-06 12:54:34 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
song: data,
|
|
|
|
isLoading,
|
|
|
|
isError: error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-27 20:27:23 +00:00
|
|
|
function Song({as: Component = 'div', artist, title, songUrl, album, albumImageUrl}: Song) {
|
2022-12-06 12:54:34 +00:00
|
|
|
return (
|
2023-01-06 23:16:19 +00:00
|
|
|
<Component
|
|
|
|
className="flex items-center space-x-4">
|
|
|
|
<Song.Album
|
|
|
|
album={album}
|
|
|
|
albumImageUrl={albumImageUrl}
|
2022-12-06 12:54:34 +00:00
|
|
|
/>
|
|
|
|
<div>
|
2023-01-06 23:16:19 +00:00
|
|
|
<Song.Title
|
|
|
|
title={title}
|
|
|
|
songUrl={songUrl}
|
|
|
|
/>
|
|
|
|
<Song.Artist artist={artist}/>
|
2022-12-06 12:54:34 +00:00
|
|
|
</div>
|
2023-01-06 23:16:19 +00:00
|
|
|
</Component>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-14 19:31:05 +00:00
|
|
|
Song.Album = function SongAlbum({album, albumImageUrl}: Album) {
|
2023-01-06 23:16:19 +00:00
|
|
|
return (
|
|
|
|
<Image
|
|
|
|
width="64"
|
|
|
|
height="64"
|
|
|
|
alt={album}
|
|
|
|
src={albumImageUrl}
|
|
|
|
className="aspect-square rounded-2xl object-cover"
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-14 19:31:05 +00:00
|
|
|
Song.Title = function SongTitle({as: Component = 'h2', title, songUrl, className}: Title) {
|
2023-01-06 23:16:19 +00:00
|
|
|
return (
|
2024-09-27 20:27:23 +00:00
|
|
|
<Component
|
|
|
|
className={clsx(className, 'text-sm font-semibold text-zinc-800 dark:text-zinc-100 line-clamp-1 lg:line-clamp-none')}>
|
2023-01-06 23:16:19 +00:00
|
|
|
<Link href={songUrl}>
|
|
|
|
{title}
|
|
|
|
</Link>
|
|
|
|
</Component>
|
2022-12-06 12:54:34 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-14 19:31:05 +00:00
|
|
|
Song.Artist = function SongArtist({as: Component = 'p', artist}: Artist) {
|
2022-12-06 12:54:34 +00:00
|
|
|
return (
|
2023-01-06 23:16:19 +00:00
|
|
|
<Component className="text-sm text-zinc-600 dark:text-zinc-400 line-clamp-1 lg:line-clamp-none">
|
|
|
|
{artist}
|
|
|
|
</Component>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-14 19:31:05 +00:00
|
|
|
Song.Skeleton = function SongSkeleton() {
|
2023-01-06 23:16:19 +00:00
|
|
|
return (
|
2023-01-14 19:31:05 +00:00
|
|
|
<div className="flex items-center space-x-4 animate-pulse">
|
2022-12-06 12:54:34 +00:00
|
|
|
<div
|
|
|
|
className="w-[64px] h-[64px] bg-zinc-100 rounded-2xl dark:bg-zinc-900"
|
|
|
|
/>
|
|
|
|
<div>
|
|
|
|
<p className="w-[128px] h-3 bg-zinc-100 rounded-2xl dark:bg-zinc-900"/>
|
|
|
|
<p className="mt-3 w-[128px] h-3 bg-zinc-100 rounded-2xl dark:bg-zinc-900"/>
|
|
|
|
</div>
|
2023-01-14 19:31:05 +00:00
|
|
|
</div>
|
2022-12-06 12:54:34 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-14 19:31:05 +00:00
|
|
|
export function SpotifyPlayer(): ReactElement | null {
|
2023-01-18 17:02:44 +00:00
|
|
|
const lastPlayed = usePlayerState('last-played')
|
2022-12-06 12:54:34 +00:00
|
|
|
|
2024-09-27 20:27:23 +00:00
|
|
|
if (lastPlayed.isError) return null
|
2022-12-06 12:54:34 +00:00
|
|
|
|
|
|
|
return (
|
2023-01-14 19:31:05 +00:00
|
|
|
<div className="grid">
|
2024-09-27 20:27:23 +00:00
|
|
|
{lastPlayed.isLoading
|
2023-01-06 23:16:19 +00:00
|
|
|
? <Song.Skeleton/>
|
2024-09-27 20:27:23 +00:00
|
|
|
: <Song {...lastPlayed.song}/>
|
2022-12-06 12:54:34 +00:00
|
|
|
}
|
|
|
|
</div>
|
|
|
|
)
|
2023-09-11 18:42:50 +00:00
|
|
|
}
|