From d459c53c3f76c274832ed52167edbce7f7366073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Fri, 4 Sep 2026 14:14:42 +0100 Subject: [PATCH] a selection that no longer exists is dropped, not reported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The collection id lives in the URL, so it outlives the row. Delete one with it open — or have somebody else delete it — and the address still asks for it, which made the media fetch 404 and put "Not found" across a panel that was otherwise working perfectly. An error about the URL, phrased as an error about the server. Now a selection naming nothing is quietly removed from the URL. There is nothing anybody could do about it and the correct state is simply "nothing selected". The error is also cleared when the selection moves. It was only cleared on the next failure, so a message from the last collection stayed up over the new one and read as this one having failed too. Co-Authored-By: Claude Opus 5 (1M context) --- web/sections/CollectionsPanel.tsx | 33 ++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/web/sections/CollectionsPanel.tsx b/web/sections/CollectionsPanel.tsx index 606ec7f..458409c 100644 --- a/web/sections/CollectionsPanel.tsx +++ b/web/sections/CollectionsPanel.tsx @@ -61,15 +61,42 @@ export const CollectionsPanel = () => { void load(); }, [load]); - // The folder's contents, re-read when the selection moves. Null while nothing is chosen, so the + /* + A selection naming something that is not there any more. + + The id lives in the URL, so it outlives the row: delete a collection with it open, reload, and the + address still asks for it. Left alone that became a 404 from the media fetch, rendered as "Not + found" over a panel that was otherwise working — an error about the URL, phrased as an error about + the server. + + Dropped rather than reported. There is nothing for anybody to do about it and the correct state is + simply "nothing selected". + */ + useEffect(() => { + if (!collections || !selectedId) return; + if (collections.some((one) => one.id === selectedId)) return; + setParams( + (current) => { + const next = new URLSearchParams(current); + next.delete('collection'); + return next; + }, + { replace: true }, + ); + }, [collections, selectedId, setParams]); + + // The gallery's photographs, re-read when the selection moves. Null while nothing is chosen, so the // empty state and "still loading" are not the same thing. useEffect(() => { - if (!selectedId) { + if (!selectedId || !selected) { setPhotos(null); return; } let alive = true; setPhotos(null); + // Cleared here rather than only on success: an error from the last selection has nothing to say + // about this one, and leaving it up reads as the new one having failed too. + setError(null); void clientRef.current .get<{ data: CollectionPhoto[] }>(`/fofos-admin/collections/${selectedId}/media`) .then((response) => alive && setPhotos(response.data)) @@ -77,7 +104,7 @@ export const CollectionsPanel = () => { return () => { alive = false; }; - }, [selectedId]); + }, [selectedId, selected]); /* The fields, in the editor column — a SIBLING panel, so they are announced rather than rendered.