// import axe from '@axe-core/react'; import { css } from "@mui/material"; import GlobalStyles from "@mui/material/GlobalStyles"; import React from "react"; import Modal from "react-modal"; import { Provider } from "react-redux"; import { useLocation, useMatch, useNavigate } from "react-router-dom"; import { Store } from "redux"; import { characterEnvActions, ConfigUtils, Constants, generateApiAdapter, } from "@dndbeyond/character-rules-engine/es"; import config from "~/config"; import { CharacterThemeProvider } from "~/contexts/CharacterTheme"; import { SidebarProvider } from "~/contexts/Sidebar"; import useUserId from "~/hooks/useUserId"; import useUserName from "~/hooks/useUserName"; import useUserRoles from "~/hooks/useUserRoles"; import "../styles/styles.scss"; import CharacterBuilderContainer from "./CharacterBuilder/containers/CharacterBuilderContainer"; import CharacterSheetContainer from "./CharacterSheet/containers/CharacterSheetContainer"; import configureSheetStore from "./CharacterSheet/store/configureStore"; import { appEnvActions } from "./Shared/actions"; import { Managers } from "./Shared/managers"; import { DiceFeatureConfigurationState, GameLogState, } from "./Shared/stores/typings"; import { AppLoggerUtils, AppNotificationUtils, ErrorUtils, } from "./Shared/utils"; import appConfig from "./config"; const BASE_PATHNAME = config.basePathname; interface Props { readOnly: boolean; redirect?: string; characterId: number | null; } export const SheetBuilderApp: React.FC = ({ readOnly, characterId, redirect, }) => { const username = useUserName(); const userId = useUserId(); const userRoles = useUserRoles(); const navigate = useNavigate(); const matchBuilderWithOutCharacter = useMatch(`${BASE_PATHNAME}/builder/*`); const matchBuilderWithCharacter = useMatch( `${BASE_PATHNAME}/:characterId/builder/*` ); const matchSheet = useMatch(`${BASE_PATHNAME}/:characterId/`); const matchSheetWithShareId = useMatch( `${BASE_PATHNAME}/:characterId/:shareId` ); const currentPath = useLocation().pathname; if (config === undefined) { throw Error("Missing Config"); } let appContext: Constants.AppContextTypeEnum | null = null; switch (true) { case !!(matchBuilderWithOutCharacter || matchBuilderWithCharacter): appContext = Constants.AppContextTypeEnum.BUILDER; break; case !!(matchSheet || matchSheetWithShareId): appContext = Constants.AppContextTypeEnum.SHEET; break; default: throw new Error("Invalid app context"); } const diceFeatureConfiguration: DiceFeatureConfigurationState = { enabled: true, menu: true, assetBaseLocation: config.diceAssetEndpoint, apiEndpoint: config.diceApiEndpoint, trackingId: config.analyticTrackingId, }; ErrorUtils.initReporting({ debug: appConfig.debug, }); let store: Store = configureSheetStore(); store.dispatch({ type: "SET_ROUTE_CONTROLS", payload: { navigate, currentPath }, }); let parsedDiceEnabled: boolean = true; if (!diceFeatureConfiguration.enabled) { parsedDiceEnabled = false; } else { // if local storage is disabled it will just crash when trying to access, this prevents crashing. try { const diceEnabled = localStorage.getItem("dice-enabled"); // once dice are enabled set the default to on; parsedDiceEnabled = diceEnabled === "true" || diceEnabled === null; } catch (e) {} } let lastMessageTime = 0; try { const lsLastMessageTime = localStorage.getItem( `gameLog-lastMessageTime-${characterId}` ); if (lsLastMessageTime) { lastMessageTime = parseInt(lsLastMessageTime); } } catch (e) {} const gameLogSettings: GameLogState = { isOpen: false, lastMessageTime: lastMessageTime, apiEndpoint: config.gameLogApiEndpoint, ddbApiEndpoint: config.ddbApiEndpoint, }; ConfigUtils.configureRulesEngine({ apiAdapter: generateApiAdapter(config.authEndpoint, readOnly), apiInfo: config.apiInfo, store, onLogMessage: AppLoggerUtils.logMessage, onLogError: AppLoggerUtils.logError, onNotification: AppNotificationUtils.dispatchNotification, includeCustomItems: true, canUseCurrencyContainers: true, }); store.dispatch( characterEnvActions.dataSet({ context: appContext, }) ); store.dispatch( appEnvActions.dataSet({ username, userId: userId ? Number(userId) : -1, userRoles, characterId, authEndpoint: config.authEndpoint, redirect, diceEnabled: parsedDiceEnabled, diceFeatureConfiguration: diceFeatureConfiguration, gameLog: gameLogSettings, }) ); // Setup axe-core in development, not in production if (process.env.NODE_ENV !== "production") { // axe(React, ReactDOM, 10000); } Modal.setAppElement("#character-tools-target"); return ( { return css` input, button, select, textarea { font-family: Roboto, Helvetica, sans-serif; font-size: 100%; margin: 0; vertical-align: baseline; } input[type="text"], input[type="number"], input[type="search"], input[type="password"], input[type="email"], textarea, select { background-color: #fff; background: #fff none; border: 1px solid #ddd; border-radius: 4px; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); color: #000; padding: 4px; transition: 0.2s linear border, 0.2s linear box-shadow; } input[type="text"]:focus, input[type="number"]:focus, input[type="search"]:focus, input[type="password"]:focus, input[type="email"]:focus, textarea:focus { border-color: #242527; box-shadow: 0 0 4px rgba(36, 37, 39, 0.6); background-color: #f9f9f9; outline: 0; } button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; } `; }} /> {matchBuilderWithOutCharacter || matchBuilderWithCharacter ? ( ) : ( )} ); };