@@ -99,9 +99,42 @@ export function AppContent() {
9999 const navigate = useNavigate ( ) ;
100100 const location = useLocation ( ) ;
101101 const { appName } = useParams ( ) ;
102- const { apps, objects : allObjects , loading : metadataLoading } = useMetadata ( ) ;
102+ const { apps, objects : allObjects , loading : metadataLoading , ensureType } = useMetadata ( ) ;
103103 const { t } = useObjectTranslation ( ) ;
104104 const { objectLabel } = useObjectLabel ( ) ;
105+
106+ // Preload the metadata buckets that the routes under /apps/:appName/* assume
107+ // are fully loaded by render time. The Phase-1 MetadataProvider refactor
108+ // made these buckets lazy (only `app` is eager); without this preload some
109+ // legacy consumers (notably `ObjectView`) would mount with `objects=[]`,
110+ // hit an `if (!objectDef) return <Empty/>` early-return, then break React's
111+ // rules-of-hooks once the lazy fetch resolves and the hooks below the early
112+ // return start running. /home and /login don't go through AppContent, so
113+ // they keep the Phase-1 benefit of fetching only the `app` list at boot.
114+ //
115+ // `ensureType` is optional at the destructure site so that the many test
116+ // files which provide a partial `useMetadata()` mock continue to work
117+ // unchanged; in those environments the preload is a no-op (the mocked
118+ // metadata arrays are already populated synchronously).
119+ const [ scopeMetaReady , setScopeMetaReady ] = useState ( ! ensureType ) ;
120+ useEffect ( ( ) => {
121+ if ( ! ensureType ) {
122+ setScopeMetaReady ( true ) ;
123+ return ;
124+ }
125+ let cancelled = false ;
126+ Promise . all ( [
127+ ensureType ( 'object' ) ,
128+ ensureType ( 'dashboard' ) ,
129+ ensureType ( 'report' ) ,
130+ ensureType ( 'page' ) ,
131+ ] ) . finally ( ( ) => {
132+ if ( ! cancelled ) setScopeMetaReady ( true ) ;
133+ } ) ;
134+ return ( ) => {
135+ cancelled = true ;
136+ } ;
137+ } , [ ensureType ] ) ;
105138
106139 // Determine active app based on URL
107140 const activeApps = apps . filter ( ( a : any ) => a . active !== false ) ;
@@ -253,7 +286,7 @@ export function AppContent() {
253286 [ user , activeApp , editingRecord ]
254287 ) ;
255288
256- if ( ! dataSource || metadataLoading ) return < LoadingScreen /> ;
289+ if ( ! dataSource || metadataLoading || ! scopeMetaReady ) return < LoadingScreen /> ;
257290
258291 // Allow create-app route even when no active app exists
259292 const isCreateAppRoute = location . pathname . endsWith ( '/create-app' ) ;
0 commit comments