2025-11-28 09:07:55 -05:00
|
|
|
import React, { Suspense, lazy, useState, useMemo, useEffect } from 'react';
|
2025-07-24 10:06:36 -04:00
|
|
|
import Memes from './Memes.jsx';
|
2025-10-02 13:14:13 -04:00
|
|
|
import Lighting from './Lighting.jsx';
|
2025-08-09 07:10:04 -04:00
|
|
|
import { toast } from 'react-toastify';
|
2025-06-18 11:41:03 -04:00
|
|
|
import { JoyUIRootIsland } from './Components.jsx';
|
|
|
|
|
import { PrimeReactProvider } from "primereact/api";
|
2025-07-24 16:09:14 -04:00
|
|
|
import { usePrimeReactThemeSwitcher } from '@/hooks/usePrimeReactThemeSwitcher.jsx';
|
2025-06-18 07:46:59 -04:00
|
|
|
import CustomToastContainer from '../components/ToastProvider.jsx';
|
2025-07-17 06:55:01 -04:00
|
|
|
import 'primereact/resources/themes/bootstrap4-light-blue/theme.css';
|
2025-07-16 10:06:41 -04:00
|
|
|
import 'primereact/resources/primereact.min.css';
|
2025-09-12 22:39:35 -04:00
|
|
|
import "primeicons/primeicons.css";
|
2025-06-18 07:46:59 -04:00
|
|
|
|
2025-08-09 07:10:04 -04:00
|
|
|
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'));
|
2025-11-28 09:07:55 -05:00
|
|
|
// 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'));
|
2025-08-09 07:10:04 -04:00
|
|
|
|
2025-10-08 15:49:00 -04:00
|
|
|
export default function Root({ child, user = undefined, ...props }) {
|
2025-06-18 07:46:59 -04:00
|
|
|
window.toast = toast;
|
|
|
|
|
const theme = document.documentElement.getAttribute("data-theme")
|
2025-11-26 10:08:24 -05:00
|
|
|
const loggedIn = props.loggedIn ?? Boolean(user);
|
2025-07-24 16:09:14 -04:00
|
|
|
usePrimeReactThemeSwitcher(theme);
|
2025-11-28 09:07:55 -05:00
|
|
|
// 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]);
|
|
|
|
|
|
2025-06-18 07:46:59 -04:00
|
|
|
return (
|
|
|
|
|
<PrimeReactProvider>
|
2025-07-31 19:28:59 -04:00
|
|
|
<CustomToastContainer
|
|
|
|
|
theme={theme}
|
|
|
|
|
newestOnTop={true}
|
|
|
|
|
closeOnClick={true} />
|
|
|
|
|
<JoyUIRootIsland>
|
|
|
|
|
{/* <Alert
|
2025-07-17 06:55:01 -04:00
|
|
|
className="alert"
|
2025-06-18 07:46:59 -04:00
|
|
|
startDecorator={<WarningIcon />}
|
|
|
|
|
variant="soft"
|
|
|
|
|
color="danger">
|
|
|
|
|
Work in progress... bugs are to be expected.
|
2025-07-23 07:45:28 -04:00
|
|
|
</Alert> */}
|
2025-11-26 10:08:24 -05:00
|
|
|
{child == "LoginPage" && (<LoginPage {...props} loggedIn={loggedIn} />)}
|
2025-10-08 15:49:00 -04:00
|
|
|
{child == "LyricSearch" && (<LyricSearch {...props} client:only="react" />)}
|
2025-11-28 09:07:55 -05:00
|
|
|
{child == "Player" && !isSubsite && PlayerComp && (
|
|
|
|
|
<Suspense fallback={<div data-testid="player-fallback" className="p-4 text-center">Loading player...</div>}>
|
|
|
|
|
<PlayerComp client:only="react" user={user} />
|
|
|
|
|
</Suspense>
|
|
|
|
|
)}
|
2025-07-31 19:28:59 -04:00
|
|
|
{child == "Memes" && <Memes client:only="react" />}
|
|
|
|
|
{child == "qs2.MediaRequestForm" && <MediaRequestForm client:only="react" />}
|
|
|
|
|
{child == "qs2.RequestManagement" && <RequestManagement client:only="react" />}
|
2025-11-28 09:07:55 -05:00
|
|
|
{child == "ReqForm" && <ReqForm client:only="react" />}
|
2025-10-08 10:56:42 -04:00
|
|
|
{child == "Lighting" && <Lighting key={window.location.pathname + Math.random()} client:only="react" />}
|
2025-07-31 19:28:59 -04:00
|
|
|
</JoyUIRootIsland>
|
2025-06-18 07:46:59 -04:00
|
|
|
</PrimeReactProvider>
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-07-16 10:06:41 -04:00
|
|
|
|