86 lines
2.4 KiB
TypeScript
86 lines
2.4 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import Link from "next/link";
|
|
import { api, type Song } from "@/lib/api";
|
|
import { useAuth } from "@/lib/auth";
|
|
import { SongCard } from "@/components/SongCard";
|
|
|
|
export default function LibraryPage() {
|
|
const { user, loading } = useAuth();
|
|
const router = useRouter();
|
|
const [songs, setSongs] = useState<Song[] | null>(null);
|
|
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
|
|
const load = useCallback(async () => {
|
|
const { items } = await api.mySongs();
|
|
setSongs(items);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!loading && !user) router.push("/login");
|
|
}, [loading, user, router]);
|
|
|
|
useEffect(() => {
|
|
if (!user) return;
|
|
load();
|
|
}, [user, load]);
|
|
|
|
useEffect(() => {
|
|
if (!songs) return;
|
|
const hasActive = songs.some((s) => s.status === "pending" || s.status === "processing");
|
|
if (hasActive && !pollRef.current) {
|
|
pollRef.current = setInterval(load, 3000);
|
|
} else if (!hasActive && pollRef.current) {
|
|
clearInterval(pollRef.current);
|
|
pollRef.current = null;
|
|
}
|
|
return () => {
|
|
if (pollRef.current) {
|
|
clearInterval(pollRef.current);
|
|
pollRef.current = null;
|
|
}
|
|
};
|
|
}, [songs, load]);
|
|
|
|
if (loading || !user) return null;
|
|
|
|
return (
|
|
<div className="mx-auto max-w-3xl px-6 py-16">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="text-3xl font-bold">Your library</h1>
|
|
<Link
|
|
href="/create"
|
|
className="rounded-full bg-white px-4 py-2 text-sm font-semibold text-black hover:bg-white/90"
|
|
>
|
|
+ New song
|
|
</Link>
|
|
</div>
|
|
|
|
{songs === null ? (
|
|
<p className="mt-10 text-white/50">Loading…</p>
|
|
) : songs.length === 0 ? (
|
|
<p className="mt-10 text-white/50">
|
|
Nothing yet.{" "}
|
|
<Link href="/create" className="underline">
|
|
Generate your first song
|
|
</Link>
|
|
.
|
|
</p>
|
|
) : (
|
|
<div className="mt-8 flex flex-col gap-4">
|
|
{songs.map((song) => (
|
|
<SongCard
|
|
key={song.id}
|
|
song={song}
|
|
isOwner
|
|
onChange={(updated) => setSongs((prev) => prev?.map((s) => (s.id === updated.id ? updated : s)) ?? null)}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|