From a08c002c53fc4d0b1b31a6614e91f4043b2d97b9 Mon Sep 17 00:00:00 2001 From: duckietm Date: Wed, 20 May 2026 14:56:35 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=86=99=20Fix=20backgrounds=20after=20the?= =?UTF-8?q?=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backgrounds/BackgroundsView.tsx | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/components/backgrounds/BackgroundsView.tsx b/src/components/backgrounds/BackgroundsView.tsx index 1c51398..a6a6f65 100644 --- a/src/components/backgrounds/BackgroundsView.tsx +++ b/src/components/backgrounds/BackgroundsView.tsx @@ -1,4 +1,4 @@ -import { Dispatch, FC, SetStateAction, use, useCallback, useMemo, useState } from 'react'; +import { Dispatch, FC, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react'; import { Base, Grid, Flex, NitroCardView, NitroCardHeaderView, NitroCardTabsView, NitroCardTabsItemView, NitroCardContentView, Text } from '../../common'; import { useRoom } from '../../hooks'; import { GetOptionalConfigurationValue } from '../../api'; @@ -27,18 +27,32 @@ type TabType = typeof TABS[number]; type RemoteData = Partial>; -let backgroundsDataPromise: Promise | null = null; +// Module-scoped cache so repeated mounts don't refetch the same JSON. +// Not a Promise — we deliberately don't expose anything that could be +// passed to React's `use()` hook. Suspending here unmounts the parent +// room tree (no boundary upstream), which orphans the Pixi +// canvas and leaves the room rendered as a black square until another +// state change forces a re-render. +let cachedBackgroundsData: RemoteData | null = null; +let inflightBackgroundsFetch: Promise | null = null; -const fetchBackgroundsData = (): Promise => +const loadBackgroundsData = (): Promise => { - if(backgroundsDataPromise) return backgroundsDataPromise; + if(cachedBackgroundsData) return Promise.resolve(cachedBackgroundsData); + if(inflightBackgroundsFetch) return inflightBackgroundsFetch; - backgroundsDataPromise = fetch(configFileUrl('infostand_backgrounds.json'), { credentials: 'omit' }) + inflightBackgroundsFetch = fetch(configFileUrl('infostand_backgrounds.json'), { credentials: 'omit' }) .then(r => r.ok ? r.json() : null) - .then(json => (json && typeof json === 'object') ? json as RemoteData : null) - .catch(() => null); + .then(json => + { + const result = (json && typeof json === 'object') ? json as RemoteData : null; + cachedBackgroundsData = result; + return result; + }) + .catch(() => null) + .finally(() => { inflightBackgroundsFetch = null; }); - return backgroundsDataPromise; + return inflightBackgroundsFetch; }; export const BackgroundsView: FC = ({ @@ -56,9 +70,20 @@ export const BackgroundsView: FC = ({ }) => { const [activeTab, setActiveTab] = useState('backgrounds'); - const remoteData = use(fetchBackgroundsData()); + const [remoteData, setRemoteData] = useState(cachedBackgroundsData); const { roomSession } = useRoom(); + useEffect(() => + { + if(remoteData) return; + let cancelled = false; + loadBackgroundsData().then(data => + { + if(!cancelled && data) setRemoteData(data); + }); + return () => { cancelled = true; }; + }, [remoteData]); + const processData = useCallback((configData: any[], idField: string): ItemData[] => { if (!configData?.length) return [];