New source found from dndbeyond.com
This commit is contained in:
+161
-91
@@ -1,19 +1,35 @@
|
||||
import { useQuery } from "react-query";
|
||||
import { useMatch } from "react-router-dom";
|
||||
import { Routes as Router, Route, Navigate } from "react-router-dom";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { isEmpty } from "lodash";
|
||||
import { JSX } from "react";
|
||||
import { Provider, useSelector } from "react-redux";
|
||||
import {
|
||||
Navigate,
|
||||
Outlet,
|
||||
Route,
|
||||
Routes as Router,
|
||||
useMatch,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
|
||||
import config from "../config";
|
||||
import { rulesEngineSelectors } from "@dndbeyond/character-rules-engine/es";
|
||||
import { GameLogContextProvider } from "@dndbeyond/game-log-components";
|
||||
import { PddContextProvider } from "@dndbeyond/pocket-dimension-dice/components/PddContextProvider";
|
||||
import { Context as DiceWorkerContext } from "@dndbeyond/pocket-dimension-dice/context";
|
||||
|
||||
import configureSheetStore from "~/tools/js/CharacterSheet/store/configureStore";
|
||||
|
||||
import config, { authEndpoint } from "../config";
|
||||
import { useAuth } from "../contexts/Authentication";
|
||||
import { getCharacters } from "../helpers/characterServiceApi";
|
||||
import type { AppUserState } from "../hooks/useUser";
|
||||
import { getStatus } from "../state/selectors/characterUtils";
|
||||
import { MyCharacters } from "../subApps/listing";
|
||||
import { getUrlWithParams } from "../tools/js/CharacterBuilder/utils/navigationUtils";
|
||||
import { CharacterStatusEnum, QueryResponseData } from "../types";
|
||||
import { CharacterStatusEnum } from "../types";
|
||||
import MaxCharactersMessage from "./max-characters-message";
|
||||
import SheetBuilderApp from "./sheet-builder-app";
|
||||
|
||||
const BASE_PATHNAME = config.basePathname;
|
||||
const store = configureSheetStore();
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -60,13 +76,13 @@ function GetCallCharacters({
|
||||
}): JSX.Element {
|
||||
const isCharactersListing = useMatch({ path: BASE_PATHNAME, end: true });
|
||||
|
||||
const { isLoading, data, refetch, error } = useQuery<
|
||||
QueryResponseData,
|
||||
Error
|
||||
>("repoData", () => getCharacters(), {
|
||||
const { isLoading, data, refetch, error } = useQuery({
|
||||
queryKey: ["repoData"],
|
||||
queryFn: () => getCharacters(),
|
||||
refetchOnWindowFocus: false,
|
||||
refetchInterval: 24 * 60 * 60 * 1000,
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return <>{null}</>;
|
||||
}
|
||||
@@ -80,94 +96,148 @@ function GetCallCharacters({
|
||||
return <>{children({ isLoading, data, refetch, error })}</>;
|
||||
}
|
||||
|
||||
type AppRoute = {
|
||||
path: string;
|
||||
element: JSX.Element;
|
||||
};
|
||||
function CharacterSheetLayout() {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<CharacterSheetProviders />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
const getRoutes = (user: AppUserState): Array<AppRoute> => [
|
||||
{
|
||||
path: `${BASE_PATHNAME}/builder/*`,
|
||||
element: (
|
||||
<RequireAuth>
|
||||
<GetCallCharacters>
|
||||
{({ data }) => {
|
||||
const numberOfCharacters =
|
||||
data?.data?.characters?.filter(
|
||||
(character) =>
|
||||
getStatus(character) === CharacterStatusEnum.Active
|
||||
).length ?? 0;
|
||||
const maxCharacterSlotsAllowed =
|
||||
data?.data?.characterSlotLimit ?? Infinity;
|
||||
const hasMaxCharacters =
|
||||
numberOfCharacters >= maxCharacterSlotsAllowed;
|
||||
if (hasMaxCharacters) {
|
||||
return (
|
||||
<Navigate to={`${BASE_PATHNAME}/max-characters`} replace />
|
||||
);
|
||||
}
|
||||
return <SheetBuilderApp />;
|
||||
}}
|
||||
</GetCallCharacters>
|
||||
</RequireAuth>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: `${BASE_PATHNAME}/:characterId/builder/*`,
|
||||
element: (
|
||||
<RequireAuth>
|
||||
<GetCallCharacters>{() => <SheetBuilderApp />}</GetCallCharacters>
|
||||
</RequireAuth>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: `${BASE_PATHNAME}/:characterId/:shareId`,
|
||||
element: user ? (
|
||||
<GetCallCharacters>{() => <SheetBuilderApp />}</GetCallCharacters>
|
||||
) : (
|
||||
<SheetBuilderApp />
|
||||
),
|
||||
},
|
||||
{
|
||||
path: `${BASE_PATHNAME}/:characterId`,
|
||||
element: user ? (
|
||||
<GetCallCharacters>{() => <SheetBuilderApp />}</GetCallCharacters>
|
||||
) : (
|
||||
<SheetBuilderApp />
|
||||
),
|
||||
},
|
||||
{
|
||||
path: `${BASE_PATHNAME}/max-characters`,
|
||||
element: <MaxCharactersMessage />,
|
||||
},
|
||||
{
|
||||
path: BASE_PATHNAME,
|
||||
element: (
|
||||
<RequireAuth>
|
||||
<GetCallCharacters>
|
||||
{({ isLoading, data, refetch, error }) => (
|
||||
<MyCharacters
|
||||
characterQuery={{ isLoading, data, refetch, error }}
|
||||
/>
|
||||
)}
|
||||
</GetCallCharacters>
|
||||
</RequireAuth>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
element: <Navigate to={BASE_PATHNAME} replace />,
|
||||
},
|
||||
];
|
||||
function CharacterSheetProviders() {
|
||||
const user = useAuth();
|
||||
const isUserEmpty = isEmpty(user);
|
||||
const campaignId = useSelector(rulesEngineSelectors.getCampaign)?.id;
|
||||
const { characterId } = useParams();
|
||||
|
||||
return (
|
||||
<PddContextProvider
|
||||
logger={console.log}
|
||||
errorCallback={() => {}}
|
||||
nodeEnv={process.env.NODE_ENV}
|
||||
env={config.environment}
|
||||
user={isUserEmpty ? undefined : user}
|
||||
gameId={campaignId ? String(campaignId) : undefined}
|
||||
>
|
||||
<GameLogContextProvider
|
||||
baseUrl={config.gameLogApiEndpoint}
|
||||
ddbApiUrl={config.ddbApiEndpoint}
|
||||
diceServiceUrl={config.diceApiEndpoint}
|
||||
authUrl={authEndpoint}
|
||||
diceThumbnailsUrl={`${config.diceAssetEndpoint}/images/thumbnails`}
|
||||
entityId={characterId ?? ""}
|
||||
diceWorkerContext={DiceWorkerContext}
|
||||
>
|
||||
<Outlet />
|
||||
</GameLogContextProvider>
|
||||
</PddContextProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export const Routes = () => {
|
||||
const user = useAuth();
|
||||
|
||||
return (
|
||||
<Router>
|
||||
{getRoutes(user).map((props: AppRoute) => (
|
||||
<Route key={props.path} {...props} />
|
||||
))}
|
||||
{/* Routes that need the Redux store + GameLog/Pdd providers */}
|
||||
<Route element={<CharacterSheetLayout />}>
|
||||
<Route
|
||||
path={`${BASE_PATHNAME}/builder/*`}
|
||||
element={
|
||||
<RequireAuth>
|
||||
<GetCallCharacters>
|
||||
{({ data }) => {
|
||||
const numberOfCharacters =
|
||||
data?.data?.characters?.filter(
|
||||
(character) =>
|
||||
getStatus(character) === CharacterStatusEnum.Active
|
||||
).length ?? 0;
|
||||
const maxCharacterSlotsAllowed =
|
||||
data?.data?.characterSlotLimit ?? Infinity;
|
||||
const hasMaxCharacters =
|
||||
numberOfCharacters >= maxCharacterSlotsAllowed;
|
||||
if (hasMaxCharacters) {
|
||||
return (
|
||||
<Navigate
|
||||
to={`${BASE_PATHNAME}/max-characters`}
|
||||
replace
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <SheetBuilderApp />;
|
||||
}}
|
||||
</GetCallCharacters>
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={`${BASE_PATHNAME}/premade/*`}
|
||||
element={
|
||||
<RequireAuth>
|
||||
<GetCallCharacters>{() => <SheetBuilderApp />}</GetCallCharacters>
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Character routes */}
|
||||
<Route path={`${BASE_PATHNAME}/:characterId`}>
|
||||
<Route
|
||||
path="builder/*"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<GetCallCharacters>
|
||||
{() => <SheetBuilderApp />}
|
||||
</GetCallCharacters>
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path=":shareId"
|
||||
element={
|
||||
user ? (
|
||||
<GetCallCharacters>
|
||||
{() => <SheetBuilderApp />}
|
||||
</GetCallCharacters>
|
||||
) : (
|
||||
<SheetBuilderApp />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
index
|
||||
element={
|
||||
user ? (
|
||||
<GetCallCharacters>
|
||||
{() => <SheetBuilderApp />}
|
||||
</GetCallCharacters>
|
||||
) : (
|
||||
<SheetBuilderApp />
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
{/* Routes that don't need the store */}
|
||||
<Route
|
||||
path={`${BASE_PATHNAME}/max-characters`}
|
||||
element={<MaxCharactersMessage />}
|
||||
/>
|
||||
<Route
|
||||
path={BASE_PATHNAME}
|
||||
element={
|
||||
<RequireAuth>
|
||||
<GetCallCharacters>
|
||||
{({ isLoading, data, refetch, error }) => (
|
||||
<MyCharacters
|
||||
characterQuery={{ isLoading, data, refetch, error }}
|
||||
/>
|
||||
)}
|
||||
</GetCallCharacters>
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route path="/" element={<Navigate to={BASE_PATHNAME} replace />} />
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import clsx from "clsx";
|
||||
import { useLayoutEffect } from "react";
|
||||
|
||||
import { MaxCharactersMessageText } from "~/components/MaxCharactersMessageText";
|
||||
|
||||
import { DDB_MEDIA_URL } from "../constants";
|
||||
import styles from "./max-characters-message.module.css";
|
||||
|
||||
export const MaxCharactersMessage = () => {
|
||||
//This mimics the same body classes as CharacterBuilderContainer.tsx
|
||||
useLayoutEffect(() => {
|
||||
document
|
||||
.getElementsByTagName("body")[0]
|
||||
.classList.add(
|
||||
"site",
|
||||
"body-rpgcharacterbuilder",
|
||||
"site-dndbeyond",
|
||||
"body-rpgcharacter"
|
||||
);
|
||||
return () => {
|
||||
document
|
||||
.getElementsByTagName("body")[0]
|
||||
.classList.remove(
|
||||
"site",
|
||||
"body-rpgcharacterbuilder",
|
||||
"site-dndbeyond",
|
||||
"body-rpgcharacter"
|
||||
);
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "calc(100vh - 114px)",
|
||||
background: `url("${DDB_MEDIA_URL}/attachments/1/614/builder1k.jpg") no-repeat center 0px, url("${DDB_MEDIA_URL}/attachments/0/84/background_texture.png") #f9f9f9 !important`,
|
||||
}}
|
||||
justifyContent="center"
|
||||
display="flex"
|
||||
>
|
||||
<Stack
|
||||
justifyContent="center"
|
||||
sx={{
|
||||
padding: "20px",
|
||||
"& > :not(style)": { padding: "20px" },
|
||||
maxWidth: "650px",
|
||||
height: "auto",
|
||||
}}
|
||||
>
|
||||
<MaxCharactersMessageText includeTitle={true} useLinks={true} />
|
||||
</Stack>
|
||||
</Box>
|
||||
<div className={clsx([styles.page])}>
|
||||
<MaxCharactersMessageText includeTitle={true} useLinks={true} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -6,12 +6,11 @@ import { SheetBuilderApp } from "~/tools/js";
|
||||
export default function Builder() {
|
||||
const { characterId } = useParams();
|
||||
const username = useUserName();
|
||||
let parsedId: number | null = null;
|
||||
try {
|
||||
if (characterId) {
|
||||
parsedId = parseInt(characterId, 10);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
return <SheetBuilderApp characterId={parsedId} readOnly={!username} />;
|
||||
return (
|
||||
<SheetBuilderApp
|
||||
characterId={parseInt(String(characterId), 10) || null}
|
||||
readOnly={!username}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user