91 lines
2.5 KiB
TypeScript
91 lines
2.5 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
import { getDownloadUrl, getTracks } from 'utils/search'
|
|
|
|
import type { AlbumRow, TrackRow } from 'utils/types'
|
|
import Loader from './Loader'
|
|
|
|
export default function Album(props: { album: AlbumRow; lastReqRef: React.RefObject<number> }) {
|
|
const { album, lastReqRef } = props
|
|
const [open, setOpen] = useState(false)
|
|
|
|
return (
|
|
<div className='album'>
|
|
<div className='album-title' onClick={() => setOpen((o) => !o)}>
|
|
<h3>{album.name}</h3>
|
|
</div>
|
|
<TrackList url={album.url} open={open} lastReqRef={lastReqRef} />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function TrackList(props: { url: string; open: boolean; lastReqRef: React.RefObject<number> }) {
|
|
const { url, open, lastReqRef } = props
|
|
const [tracks, setTracks] = useState<TrackRow[] | null>(null)
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
async function fetchTracks() {
|
|
try {
|
|
setLoading(true)
|
|
const tracks = await getTracks(url, lastReqRef)
|
|
setTracks(tracks)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (!tracks && open) fetchTracks()
|
|
}, [open])
|
|
|
|
if (!open) return null
|
|
|
|
return (
|
|
<div className='album-open'>
|
|
{loading ? (
|
|
<div className='loader-container'>
|
|
<Loader show />
|
|
</div>
|
|
) : null}
|
|
{tracks && open ? (
|
|
<div className='track-list'>
|
|
{tracks.map((t, i) => (
|
|
<div className='track' key={i}>
|
|
<span className='track-name'>
|
|
<span className='track-number'>{(i + 1).toString().padStart(2, '0').slice(-2)}. </span>
|
|
<span>{t.name}</span>
|
|
</span>
|
|
<span className='format-tag'>FLAC/MP3</span>
|
|
<TrackDownload url={t.url} lastReqRef={lastReqRef} />
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function TrackDownload(props: { url: string; lastReqRef: React.RefObject<number> }) {
|
|
const { url, lastReqRef } = props
|
|
const [downloadUrl, setDownloadUrl] = useState<string>()
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
async function fetchUrl() {
|
|
try {
|
|
setLoading(true)
|
|
const dlUrl = await getDownloadUrl(url, lastReqRef)
|
|
setDownloadUrl(dlUrl)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return downloadUrl ? (
|
|
<a className='download-btn' href={downloadUrl} target='_blank' rel='noopener noreferrer' download>
|
|
Download
|
|
</a>
|
|
) : (
|
|
<button className='download-btn' disabled={loading} onClick={fetchUrl}>
|
|
{loading ? <Loader show /> : 'Download'}
|
|
</button>
|
|
)
|
|
}
|