From 51ccf55a3cfd5329652322c41fbd0e8ebb881415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 29 Aug 2026 13:35:02 +0100 Subject: [PATCH] stop the Database block flickering, properly this time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It switched to the live answer as soon as `status` arrived — while `backupsReady` was still its initial `false`, which is a REPORT of not-ready rather than the absence of one. So it decided the section was unfinished, painted open, and shut again a moment later when the real answer came. `backupsReady` is now null until BackupsRow has actually answered, and the live value is used only once every local part has. Until then the summary decides, which is what it was added for. And nothing but the header renders until the summary lands. The title and hint are constants and can paint immediately; only the check and the body wait on data — the same shape Root location settled on. Co-Authored-By: Claude Opus 5 --- web/DatabaseBlock.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/web/DatabaseBlock.tsx b/web/DatabaseBlock.tsx index 1d9f116..f2989fd 100644 --- a/web/DatabaseBlock.tsx +++ b/web/DatabaseBlock.tsx @@ -88,7 +88,9 @@ export const DatabaseBlock = () => { `BackupsRow` actually know. See RootLocationBlock for why both sources are needed. */ const [summaryReady, setSummaryReady] = useState(null); - const [backupsReady, setBackupsReady] = useState(false); + /* null until BackupsRow has actually answered. `false` would be a REPORT of not-ready, and using + it before the answer exists is what made this block paint open and then shut. */ + const [backupsReady, setBackupsReady] = useState(null); const [collapsed, setCollapsed] = useState(null); useEffect(() => { @@ -276,7 +278,16 @@ export const DatabaseBlock = () => { (status.development.tables ?? 0) > 0 && backupsReady, ); - const settledNow = status ? live : summaryReady === true; + + /* + The summary until EVERY local part has answered, then the live value. + + Both conditions, not either. Switching as soon as `status` arrived meant using `backupsReady` while + it was still its initial value — an answer nobody had given — so the block decided it was unfinished + and opened, and shut again a moment later when the real answer came. + */ + const answered = status !== null && backupsReady !== null; + const settledNow = answered ? live : summaryReady === true; useEffect(() => { setCollapsed((chosen) => { @@ -308,7 +319,7 @@ export const DatabaseBlock = () => { ) : undefined } > - {!status ? null : settled ? ( + {summaryReady === null || !status ? null : settledNow ? ( <> {row('Production', 'production', status.production)} {row('Development', 'development', status.development)}