Home stays first, and a reorder shows in the preview
Home has no grip, and dropping onto it is refused — hiding the handle alone would still let something be dragged above the site's front door. Every preview now reloads on the change channel, not just the page builder's own writes. The Layout preview draws the top menu and had no reload token at all, so the one screen where a reorder is visible was the one that never showed it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import { SECTIONS, sectionOpen } from './sections';
|
||||
import { href, useNav } from './routes';
|
||||
import { useReady } from './useReady';
|
||||
import { pageParam, usePages, type Page } from './usePages';
|
||||
import { useFofosChanged } from './channel';
|
||||
|
||||
/**
|
||||
* The left column: which part of the fofos site the detail panel is showing.
|
||||
@@ -91,11 +92,15 @@ export const NavPanel = () => {
|
||||
The write happens once, at the end: `PUT /pages/order` takes the whole set, because an order is a
|
||||
property of the SET and sending one row's new index leaves the server guessing about everyone else.
|
||||
*/
|
||||
const [, bump] = useFofosChanged();
|
||||
const [dragged, setDragged] = useState<number | null>(null);
|
||||
const orderedRef = useRef<Page[] | null>(null);
|
||||
|
||||
const moveOver = (event: React.DragEvent, overId: number) => {
|
||||
if (dragged === null || !pages) return;
|
||||
// Home is first and stays first — see the note on its missing grip. Refusing the drop here is what
|
||||
// stops anything being dragged ABOVE it, which no amount of hiding the handle would prevent.
|
||||
if (pages.find((page) => page.id === overId)?.is_home) return;
|
||||
// Without this the drop is refused and the browser animates the row back to where it started.
|
||||
event.preventDefault();
|
||||
if (dragged === overId) return;
|
||||
@@ -119,6 +124,8 @@ export const NavPanel = () => {
|
||||
|
||||
void clientRef.current
|
||||
.put('/fofos-admin/pages/order', { ids: ordered.map((page) => page.id) })
|
||||
// The menu is the site's, so every preview on the screen is now showing the old one.
|
||||
.then(() => bump())
|
||||
// The list is already showing the new order; a failure has to put the old one back, and the
|
||||
// server is the only thing that knows what that was.
|
||||
.catch(() => void clientRef.current.get<{ data: Page[] }>('/fofos-admin/pages').then((body) => announce(body.data)));
|
||||
@@ -210,6 +217,16 @@ export const NavPanel = () => {
|
||||
current ? 'bg-muted' : 'hover:bg-muted/50'
|
||||
} ${dragged === page.id ? 'opacity-40' : ''}`}
|
||||
>
|
||||
{/*
|
||||
Home has no grip.
|
||||
|
||||
It is the site's front door and its address is `/`; a menu that opens somewhere else
|
||||
is not a menu with a different order, it is a different site. The space is kept so the
|
||||
titles still line up — an unindented Home would read as a heading rather than a row.
|
||||
*/}
|
||||
{page.is_home ? (
|
||||
<span className="w-6 shrink-0" aria-hidden />
|
||||
) : (
|
||||
<span
|
||||
draggable
|
||||
onDragStart={(event) => {
|
||||
@@ -224,6 +241,7 @@ export const NavPanel = () => {
|
||||
>
|
||||
<GripVertical className="size-3.5" />
|
||||
</span>
|
||||
)}
|
||||
|
||||
<Link
|
||||
to={href('pages', pageParam(page))}
|
||||
|
||||
+15
-2
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useClient } from 'hooks/useClient';
|
||||
import { readableError } from './errors';
|
||||
import { DEVICES, usePreviewable, useViewMode } from './ViewMode';
|
||||
import { useFofosChanged } from './channel';
|
||||
|
||||
/**
|
||||
* A page of the real site, embedded.
|
||||
@@ -209,9 +210,21 @@ export const SitePreview = ({
|
||||
The lesson is the same one as `useClient` two files along: a value rebuilt every render must not be
|
||||
the thing something else keys on.
|
||||
*/
|
||||
/*
|
||||
A change anywhere on the site reloads every preview of it.
|
||||
|
||||
`reloadToken` is the panel's own — the page builder bumps it after its own writes. This is the
|
||||
other half: something ELSE changed the site, and a frame showing the old one is a frame lying. The
|
||||
nav reordering the menu is the case that made it necessary; the Layout preview draws that menu and
|
||||
had no token at all.
|
||||
|
||||
Its purpose is exactly this — "anyone reading it should read it again" — and a preview is a reader.
|
||||
*/
|
||||
const [changed] = useFofosChanged();
|
||||
|
||||
useEffect(() => {
|
||||
setLoaded(0);
|
||||
}, [reloadToken]);
|
||||
}, [reloadToken, changed]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loaded) return;
|
||||
@@ -334,7 +347,7 @@ export const SitePreview = ({
|
||||
<iframe
|
||||
// The key is what makes `reloadToken` reload: a new key is a new element, so the browser
|
||||
// fetches the document again and `onLoad` runs, which re-injects the builder's chrome.
|
||||
key={reloadToken ?? 0}
|
||||
key={`${reloadToken ?? 0}:${changed}`}
|
||||
ref={frame}
|
||||
// Applied on load as well as on change: the document is replaced on a reload, and the
|
||||
// properties go with it.
|
||||
|
||||
Reference in New Issue
Block a user