62 lines
2.2 KiB
TypeScript
62 lines
2.2 KiB
TypeScript
|
|
import { useEffect, useState } from 'react'
|
||
|
|
import { Link } from 'react-router-dom'
|
||
|
|
import { api } from '../api.js'
|
||
|
|
import type { Item } from '../types.js'
|
||
|
|
import Cover from '../components/Cover.js'
|
||
|
|
|
||
|
|
export default function QueuePage() {
|
||
|
|
const [items, setItems] = useState<Item[] | null>(null)
|
||
|
|
const [error, setError] = useState(false)
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
void api
|
||
|
|
.listCollection({ ripped: 'not_ripped' })
|
||
|
|
.then((res) => setItems([...res.items].reverse()))
|
||
|
|
.catch(() => setError(true))
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
function markRipped(id: number) {
|
||
|
|
void api
|
||
|
|
.setRip(id, true)
|
||
|
|
.then(() => setItems((list) => (list ?? []).filter((i) => i.id !== id)))
|
||
|
|
.catch(() => setError(true))
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="space-y-3">
|
||
|
|
<p className="text-sm text-neutral-400">Rip queue — oldest additions first.</p>
|
||
|
|
{error && <p className="text-sm text-red-400">Could not load the queue.</p>}
|
||
|
|
{items && items.length === 0 && (
|
||
|
|
<div className="py-12 text-center">
|
||
|
|
<p className="text-neutral-400">Nothing waiting to be ripped.</p>
|
||
|
|
<Link to="/scan" className="mt-2 inline-block text-sm text-emerald-400">
|
||
|
|
Scan something →
|
||
|
|
</Link>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
{items &&
|
||
|
|
items.map((item) => (
|
||
|
|
<div key={item.id} className="flex items-center gap-3 rounded-xl border border-neutral-800 bg-neutral-900 p-3">
|
||
|
|
<Cover src={item.artworkUrl} alt="" className="size-12 shrink-0" />
|
||
|
|
<div className="min-w-0 flex-1">
|
||
|
|
<p className="truncate text-sm font-medium">{item.title}</p>
|
||
|
|
<p className="truncate text-xs text-neutral-400">
|
||
|
|
{item.artist} · added {new Date(item.dateAdded + 'Z').toLocaleDateString()}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={() => markRipped(item.id)}
|
||
|
|
className="shrink-0 rounded-lg bg-emerald-500 px-3 py-1.5 text-sm font-medium text-neutral-950"
|
||
|
|
>
|
||
|
|
Mark ripped
|
||
|
|
</button>
|
||
|
|
<Link to={`/item/${item.id}`} className="shrink-0 text-xs text-neutral-400">
|
||
|
|
details
|
||
|
|
</Link>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|