import React, { Suspense, lazy, useState, useMemo, useEffect } from 'react'; import Memes from './Memes.jsx'; import Lighting from './Lighting.jsx'; import { toast } from 'react-toastify'; import { JoyUIRootIsland } from './Components.jsx'; import { PrimeReactProvider } from "primereact/api"; import { usePrimeReactThemeSwitcher } from '@/hooks/usePrimeReactThemeSwitcher.jsx'; import CustomToastContainer from '../components/ToastProvider.jsx'; import 'primereact/resources/themes/bootstrap4-light-blue/theme.css'; import 'primereact/resources/primereact.min.css'; import "primeicons/primeicons.css"; const LoginPage = lazy(() => import('./Login.jsx')); const LyricSearch = lazy(() => import('./LyricSearch')); const MediaRequestForm = lazy(() => import('./TRip/MediaRequestForm.jsx')); const RequestManagement = lazy(() => import('./TRip/RequestManagement.jsx')); // NOTE: Player is intentionally NOT imported at module initialization. // We create the lazy import inside the component at render-time only when // we are on the main site and the Player island should be rendered. This // prevents bundling the player island into pages that are explicitly // identified as subsites. const ReqForm = lazy(() => import('./req/ReqForm.jsx')); export default function Root({ child, user = undefined, ...props }) { window.toast = toast; const theme = document.documentElement.getAttribute("data-theme") const loggedIn = props.loggedIn ?? Boolean(user); usePrimeReactThemeSwitcher(theme); // Avoid adding the Player island for subsite requests. We expose a // runtime flag `window.__IS_SUBSITE` from the server layout so pages // don't need to pass guards. const isSubsite = typeof document !== 'undefined' && document.documentElement.getAttribute('data-subsite') === 'true'; // Helpful runtime debugging: only log when child changes so we don't spam // the console on every render. Use an effect so output is stable. useEffect(() => { try { if (typeof console !== 'undefined' && typeof document !== 'undefined') { console.debug(`[AppLayout] child=${String(child)}, data-subsite=${document.documentElement.getAttribute('data-subsite')}`); } } catch (e) { // no-op } }, [child]); // Only initialize the lazy player when this is NOT a subsite and the // active child is the Player island. Placing the lazy() call here // avoids creating a static dependency at module load time. // Create the lazy component only when we actually need it. Using // `useMemo` ensures we don't re-create the lazy factory on every render // which would create a new component identity and cause mount/unmount // loops and repeated log messages. const wantPlayer = !isSubsite && child === "Player"; // Use dynamic import+state on the client to avoid React.lazy identity // churn and to surface any import-time errors. Since Root is used via // client:only, this code runs in the browser and can safely import. const [PlayerComp, setPlayerComp] = useState(null); useEffect(() => { let mounted = true; if (wantPlayer) { try { console.debug('[AppLayout] dynamic-import: requesting AudioPlayer'); } catch (e) { } import('./AudioPlayer.jsx') .then((mod) => { if (!mounted) return; // set the component factory setPlayerComp(() => mod.default ?? null); try { console.debug('[AppLayout] AudioPlayer import succeeded'); } catch (e) { } }) .catch((err) => { console.error('[AppLayout] AudioPlayer import failed', err); if (mounted) setPlayerComp(() => null); }); } else { // unload if we no longer want the player setPlayerComp(() => null); } return () => { mounted = false; }; }, [wantPlayer]); return ( {/* } variant="soft" color="danger"> Work in progress... bugs are to be expected. */} {child == "LoginPage" && ()} {child == "LyricSearch" && ()} {child == "Player" && !isSubsite && PlayerComp && ( Loading player...}> )} {child == "Memes" && } {child == "qs2.MediaRequestForm" && } {child == "qs2.RequestManagement" && } {child == "ReqForm" && } {child == "Lighting" && } ); }