Bug bash 5: the URL, the fleet loop, and an empty card

A link to a hub page no longer reloads onto the last machine; a link from a hub-only page to
a machine's page arrives at that page; a page being left no longer asks about the next page's
selection; the hub holds its machines to the rate it is itself running.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-21 23:26:36 -07:00
co-authored by Claude Opus 5
parent a7b8319974
commit 3c7e46104c
9 changed files with 84 additions and 27 deletions
+12 -6
View File
@@ -2,20 +2,26 @@
// which unit, which job, which folder. Reload keeps it and a link can carry it.
import { useCallback, useEffect, useState } from 'react'
const read = () => { try { return decodeURIComponent(location.hash.split('/').slice(3).join('/')) } catch { return '' } }
const viewOf = () => location.hash.split('/')[2] ?? ''
// The selection belongs to one page. A page reads it only while the URL is still pointing at
// that page: moving from #/grid/services/ssh.service to #/hub/docker/stack:gitea changes the
// hash in one step, and the page being left is still mounted for a moment — long enough, before
// this, for Services to go and ask grid about a "service" called stack:gitea.
const read = (view?: string) => { try { return view !== undefined && viewOf() !== view ? '' : decodeURIComponent(location.hash.split('/').slice(3).join('/')) } catch { return '' } }
export function useHashSel(): [string, (v: string) => void] {
const [sel, setSel] = useState(read)
export function useHashSel(view?: string): [string, (v: string) => void] {
const [sel, setSel] = useState(() => read(view))
useEffect(() => {
const h = () => setSel(read())
const h = () => setSel(read(view))
window.addEventListener('popstate', h); window.addEventListener('hashchange', h)
return () => { window.removeEventListener('popstate', h); window.removeEventListener('hashchange', h) }
}, [])
}, [view])
const set = useCallback((v: string) => {
if (view !== undefined && viewOf() !== view) return // the URL has already moved on
const base = location.hash.split('/').slice(0, 3).join('/')
const want = v ? `${base}/${encodeURIComponent(v)}` : base
if (location.hash !== want) history.replaceState(null, '', want)
setSel(v)
}, [])
}, [view])
return [sel, set]
}