import { useEffect, useRef, useState } from 'react'; import { FolderOpen, X } from 'lucide-react'; import { useClient } from 'hooks/useClient'; import { Button } from '@/components/ui/button'; import { FileBrowserApp, type PickedFile } from 'officerdev'; /** * Choose a piece of media by browsing `originals/`. * * ── The browser is the platform's, not a copy of it ── * * `FileBrowserApp` in pick mode: folders navigate, opening a file hands it back. Building a picker * instead would mean reimplementing a listing, a breadcrumb, sorting, thumbnails, upload and keyboard * handling — all of it free to drift from the real browser, and none of it better. * * ── It browses ORIGINALS and the site renders DERIVED ── * * What somebody picks is the file they uploaded: a `.mov`, a 12-megapixel HEIC, whatever came off the * camera. What a browser can display is the ladder av-club built beside it. So the value stored is the * path of the ORIGINAL, and everything that renders resolves it into `derived//`. * * That indirection is the whole point of the media pipeline. Storing a derived file directly would pin * the site to one rendition — and the day the ladder changes, or a better encoder arrives, every stored * reference would name a file that is no longer the right one to serve. * * ── The path is relative to `originals/` ── * * Not absolute. The root moves between machines — it is a setting — so an absolute path stored in the * site's database would be a fact about one installation written into content that outlives it. */ export const MediaPicker = ({ open, onClose, onPick, title = 'Choose media', }: { open: boolean; onClose: () => void; /** The path relative to `originals/`, e.g. `GALLERY/CALAVERA 1/piece.jpg`. */ onPick: (relativePath: string) => void; title?: string; }) => { const client = useClient(); const clientRef = useRef(client); clientRef.current = client; const [base, setBase] = useState(null); const [error, setError] = useState(null); useEffect(() => { if (!open) return; void (async () => { try { /* The originals folder, home-relative. `/_config/root` reports `rootRelative` — the root expressed against the owner's home, which is what the file browser addresses everything by. Null means the root is outside home, which is a legitimate configuration the browser simply cannot reach. */ const settings = await clientRef.current.get<{ rootRelative: string | null }>('/fofos-admin/_config/root'); if (!settings.rootRelative) { setError('The site root is outside your home directory, so the file browser cannot reach it.'); return; } setBase(`${settings.rootRelative.replace(/\/$/, '')}/media/originals`); } catch { setError('Could not work out where the media is. Only the owner can read that setting.'); } })(); }, [open]); // Escape closes, because a modal that only closes by a button is a modal somebody gets stuck in. useEffect(() => { if (!open) return; const onKey = (event: KeyboardEvent) => { if (event.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [open, onClose]); if (!open) return null; return (
event.stopPropagation()} >

{title}

{/* Said here rather than discovered: a person picking a .mov should know the site will not serve that file itself. */} pick the original — the site serves the converted copy
{error ? (

{error}

) : base ? ( { // `file.path` is absolute-ish within the browser's root; the stored value is relative // to originals, so the base is stripped here rather than by every caller. const relative = file.path.startsWith(base) ? file.path.slice(base.length) : file.path; onPick(relative.replace(/^\/+/, '')); onClose(); }} /> ) : (

Loading…

)}
); };