import { Plural, Trans, useLingui } from "@lingui/react/macro"; import type { MediaLibrarySource } from "@rakazo/contracts"; import { Button, Input } from "@rakazo/ui-web"; import { RefreshCw, X } from "lucide-react"; import { useEffect, useId, useState } from "react"; import { rpc } from "../lib/rpc"; import { Shimmer } from "./ai/primitives"; const POLL_MS = 2000; /** Folders on the server whose photos and videos the owner's bots can search and use. */ export function MediaLibraryFolders() { const { t } = useLingui(); const id = useId(); const [sources, setSources] = useState(null); const [folder, setFolder] = useState(""); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; rpc.media.library .list() .then((next) => { if (!cancelled) setSources(next); }) .catch((err: unknown) => { if (!cancelled) setError(err instanceof Error ? err.message : t`Couldn't load`); }); return () => { cancelled = true; }; }, [t]); const syncing = sources?.some((source) => source.status === "syncing") ?? false; useEffect(() => { if (!syncing) return; const timer = setInterval(() => { rpc.media.library .list() .then(setSources) .catch(() => undefined); }, POLL_MS); return () => clearInterval(timer); }, [syncing]); async function act(action: () => Promise) { if (busy) return; setBusy(true); setError(null); try { await action(); } catch (err) { setError(err instanceof Error ? err.message : t`Couldn't save`); } finally { setBusy(false); } } function replace(next: MediaLibrarySource) { setSources((current) => [...(current ?? []).filter((source) => source.id !== next.id), next]); } const disabled = busy || sources === null; return (

Library

{sources?.length ? (
    {sources.map((source) => (
  • {source.path}

    {source.status === "syncing" ? (

    Indexing…

    ) : source.status === "failed" ? (

    {source.error}

    ) : (

    )}
  • ))}
) : null}
{ event.preventDefault(); const path = folder.trim(); if (!path) return; void act(async () => { replace(await rpc.media.library.add({ path })); setFolder(""); }); }} > setFolder(event.target.value)} />
{error ? (

{error}

) : null}
); }