New source found from dndbeyond.com
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import clsx from "clsx";
|
||||
import { FC, HTMLAttributes } from "react";
|
||||
import { useMatch } from "react-router-dom";
|
||||
import { createPortal } from "react-dom";
|
||||
import { useMatch, useSearchParams } from "react-router-dom";
|
||||
|
||||
import { NavigationMenu } from "@dndbeyond/navigation-menu";
|
||||
import { Footer } from "@dndbeyond/ttui/components/Footer";
|
||||
import { MegaMenu } from "@dndbeyond/ttui/components/MegaMenu";
|
||||
import { Sitebar } from "@dndbeyond/ttui/components/Sitebar";
|
||||
|
||||
import config from "~/config";
|
||||
import useUser from "~/hooks/useUser";
|
||||
@@ -13,9 +13,40 @@ import styles from "./styles.module.css";
|
||||
|
||||
const BASE_PATHNAME = config.basePathname;
|
||||
|
||||
const NOTIFICATIONS_CACHE_KEY = "dndbNotificationsCache";
|
||||
|
||||
const seedNotificationsCache = () => {
|
||||
try {
|
||||
const existing = localStorage.getItem(NOTIFICATIONS_CACHE_KEY);
|
||||
const parsed = existing ? JSON.parse(existing) : null;
|
||||
const isValid =
|
||||
parsed &&
|
||||
typeof parsed.expiresAtMs === "number" &&
|
||||
Date.now() < parsed.expiresAtMs;
|
||||
if (!isValid) {
|
||||
localStorage.setItem(
|
||||
NOTIFICATIONS_CACHE_KEY,
|
||||
JSON.stringify({
|
||||
expiresAtMs: Date.now() + 60 * 60 * 1000,
|
||||
counts: {
|
||||
unreadNotifications: null,
|
||||
unreadMessages: null,
|
||||
unreadReports: null,
|
||||
unreadAppeals: null,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
// localStorage unavailable
|
||||
}
|
||||
};
|
||||
|
||||
interface LayoutProps extends HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
export const Layout: FC<LayoutProps> = ({ children, ...props }) => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const isVttView = searchParams.get("view") === "vtt";
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
const user = useUser();
|
||||
const matchSheet = useMatch(`${BASE_PATHNAME}/:characterId/`);
|
||||
@@ -23,20 +54,33 @@ export const Layout: FC<LayoutProps> = ({ children, ...props }) => {
|
||||
// Don't show the navigation in production
|
||||
if (!isDev) return <>{children}</>;
|
||||
|
||||
// Seed the notifications cache so NavigationMenu skips the JWT fetch in local
|
||||
// dev, where the auth service is not proxied.
|
||||
seedNotificationsCache();
|
||||
|
||||
const navigationUser =
|
||||
user && user.avatarUrl
|
||||
? {
|
||||
displayName: user.displayName,
|
||||
avatarUrl: user.avatarUrl,
|
||||
subscriptionTier: user.subscriptionTier as "Master",
|
||||
roles: user.roles,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<div {...props}>
|
||||
{/* TODO: fetch navItems */}
|
||||
<div className={styles.siteStyles}>
|
||||
<Sitebar user={user as any} navItems={[]} />
|
||||
{/* TODO: fetch sources */}
|
||||
<MegaMenu enablePartyWizard={true} sources={[]} />
|
||||
</div>
|
||||
{!isVttView &&
|
||||
createPortal(
|
||||
<NavigationMenu user={navigationUser} />,
|
||||
document.getElementById("header-wrapper")!
|
||||
)}
|
||||
<div className={clsx(["container", styles.devContainer])}>{children}</div>
|
||||
{!matchSheet && (
|
||||
<div className={styles.siteStyles}>
|
||||
<Footer />
|
||||
</div>
|
||||
)}
|
||||
{!isVttView &&
|
||||
(!matchSheet ||
|
||||
matchSheet.pathname === "/characters/builder" ||
|
||||
matchSheet.pathname === "/characters/premade") &&
|
||||
createPortal(<Footer />, document.getElementById("footer-wrapper")!)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user