90 lines
3.1 KiB
JavaScript
90 lines
3.1 KiB
JavaScript
import React, { useState } from 'react'
|
|
import ArtworkGallery from '../../artwork/ArtworkGallery'
|
|
|
|
function FavSkeleton() {
|
|
return (
|
|
<div className="rounded-2xl overflow-hidden bg-white/5 animate-pulse">
|
|
<div className="aspect-square bg-white/8" />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* TabFavourites
|
|
* Shows artworks the user has favourited.
|
|
*/
|
|
export default function TabFavourites({ favourites, isOwner, username }) {
|
|
const [items, setItems] = useState(favourites ?? [])
|
|
const [nextCursor, setNextCursor] = useState(null)
|
|
const [loadingMore, setLoadingMore] = useState(false)
|
|
|
|
const loadMore = async () => {
|
|
if (!nextCursor || loadingMore) return
|
|
setLoadingMore(true)
|
|
try {
|
|
const res = await fetch(
|
|
`/api/profile/${encodeURIComponent(username)}/favourites?cursor=${encodeURIComponent(nextCursor)}`,
|
|
{ headers: { Accept: 'application/json' } }
|
|
)
|
|
if (res.ok) {
|
|
const data = await res.json()
|
|
setItems((prev) => [...prev, ...(data.data ?? data)])
|
|
setNextCursor(data.next_cursor ?? null)
|
|
}
|
|
} catch (_) {}
|
|
setLoadingMore(false)
|
|
}
|
|
|
|
return (
|
|
<div
|
|
id="tabpanel-favourites"
|
|
role="tabpanel"
|
|
aria-labelledby="tab-favourites"
|
|
className="pt-6"
|
|
>
|
|
<h2 className="text-xs font-semibold uppercase tracking-widest text-slate-500 mb-4 flex items-center gap-2">
|
|
<i className="fa-solid fa-heart text-pink-400 fa-fw" />
|
|
{isOwner ? 'Your Favourites' : 'Favourites'}
|
|
</h2>
|
|
|
|
{items.length === 0 ? (
|
|
<div className="flex flex-col items-center justify-center py-20 text-center">
|
|
<div className="w-20 h-20 rounded-2xl bg-white/5 flex items-center justify-center mb-5 text-slate-500">
|
|
<i className="fa-solid fa-heart text-3xl" />
|
|
</div>
|
|
<p className="text-slate-400 font-medium">No favourites yet</p>
|
|
<p className="text-slate-600 text-sm mt-1">Artworks added to favourites will appear here.</p>
|
|
</div>
|
|
) : (
|
|
<>
|
|
<ArtworkGallery
|
|
items={items}
|
|
layout="grid"
|
|
className="grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-4"
|
|
resolveCardProps={(_, index) => ({
|
|
loading: index < 8 ? 'eager' : 'lazy',
|
|
})}
|
|
>
|
|
{loadingMore && Array.from({ length: 4 }).map((_, i) => <FavSkeleton key={`sk-${i}`} />)}
|
|
</ArtworkGallery>
|
|
|
|
{nextCursor && (
|
|
<div className="mt-8 text-center">
|
|
<button
|
|
onClick={loadMore}
|
|
disabled={loadingMore}
|
|
className="inline-flex items-center gap-2 px-6 py-2.5 rounded-xl bg-white/5 hover:bg-white/10 text-slate-300 text-sm font-medium border border-white/10 transition-all"
|
|
>
|
|
{loadingMore
|
|
? <><i className="fa-solid fa-circle-notch fa-spin fa-fw" /> Loading…</>
|
|
: <><i className="fa-solid fa-chevron-down fa-fw" /> Load more</>
|
|
}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|