New source found from dndbeyond.com

This commit is contained in:
2026-07-08 01:00:09 -07:00
parent 9a983a6d7b
commit dcefa0d2f2
2865 changed files with 222467 additions and 49053 deletions
@@ -1,158 +1,173 @@
import { useContext, useEffect, useRef } from "react";
import clsx from "clsx";
import { useContext, useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom";
import { DispatchProp, useDispatch, useSelector } from "react-redux";
import { useSelector } from "react-redux";
import { TypeScriptUtils } from "@dndbeyond/character-components/es";
import {
ApiAdapterUtils,
CharacterTheme,
ConfigUtils,
rulesEngineSelectors,
Constants,
ApiException,
CampaignDataContract,
CampaignUtils,
characterEnvSelectors,
} from "@dndbeyond/character-rules-engine/es";
import { Dice, DiceNotifier, IRollContext } from "@dndbeyond/dice";
import { DiceToolbar } from "@dndbeyond/dice-components";
import { Platform } from "@dndbeyond/event-pipeline-lib/shared";
import D20 from "@dndbeyond/fontawesome-cache/svgs/light/dice-d20.svg";
import { GameLogContext } from "@dndbeyond/game-log-components";
import { getMessageBroker } from "@dndbeyond/message-broker-lib";
import {
CustomDiceRoller,
rollerImport,
} from "@dndbeyond/pocket-dimension-dice/components/CustomDiceRoller";
import "@dndbeyond/pocket-dimension-dice/components/CustomDiceRoller/index.css";
import { Context as DiceWorkerContext } from "@dndbeyond/pocket-dimension-dice/context";
import { instrumentDiceClear } from "@dndbeyond/pocket-dimension-dice/helpers/bi/instrumentDiceClear";
import { instrumentDiceContainerClose } from "@dndbeyond/pocket-dimension-dice/helpers/bi/instrumentDiceContainerClose";
import { instrumentDiceContainerOpen } from "@dndbeyond/pocket-dimension-dice/helpers/bi/instrumentDiceContainerOpen";
import { instrumentDiceRoll } from "@dndbeyond/pocket-dimension-dice/helpers/bi/instrumentDiceRoll";
import { useDiceSets } from "@dndbeyond/pocket-dimension-dice/hooks/useDiceSets";
import { useUserDiceDataStore } from "@dndbeyond/pocket-dimension-dice/hooks/useUserDiceDataStore";
import { AnchoredPopover } from "@dndbeyond/ttui/components/AnchoredPopover";
import { Button } from "~/components/Button";
import { useAuth } from "~/contexts/Authentication";
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
import { useDispatch } from "~/hooks/useDispatch";
import { appEnvActions } from "../../actions/appEnv";
import {
appEnvSelectors,
characterRollContextSelectors,
} from "../../selectors";
import {
DiceFeatureConfigurationState,
SharedAppState,
} from "../../stores/typings";
import { AppLoggerUtils } from "../../utils";
import { appEnvSelectors } from "../../selectors";
import styles from "./DiceContainer.module.css";
interface Props extends DispatchProp {
campaign: CampaignDataContract | null;
canShow: boolean;
theme: CharacterTheme;
diceEnabled: boolean;
diceFeatureConfiguration: DiceFeatureConfigurationState;
characterRollContext: IRollContext;
}
const location = "characterSheet";
// <canvas id="dice-rolling-canvas" className={'dice-rolling-panel__container'} />
// this is the canvas element that we will use to draw the dice
// TODO: this is a temporary solution, we need to find a better way to do this
// waiting for adventure team to provide a better solution
const canvasElement = document.createElement("canvas");
canvasElement.id = "dice-rolling-canvas";
canvasElement.classList.add("dice-rolling-panel__container");
export default function DiceContainer({ canShow = true }) {
export default function DiceContainer({ canShow = true, isVttView = false }) {
const { nodeEnv, env } = useContext(DiceWorkerContext);
const dispatch = useDispatch();
const theme = useSelector(rulesEngineSelectors.getCharacterTheme);
const diceEnabled = useSelector(appEnvSelectors.getDiceEnabled);
const diceFeatureConfiguration = useSelector(
appEnvSelectors.getDiceFeatureConfiguration
);
const characterRollContext = useSelector(
characterRollContextSelectors.getCharacterRollContext
);
const campaign = useSelector(rulesEngineSelectors.getCampaign);
const userConfigApi = "/diceuserconfig/v1/get";
const authUser = useAuth();
let userId: string | null = null;
if (authUser?.id) {
userId = authUser.id;
}
const [{ messageTargetOptions, defaultMessageTargetOption }] =
useContext(GameLogContext);
const { campaign, characterName } = useCharacterEngine();
const user = useAuth();
const [{ entityId }] = useContext(GameLogContext);
const canvasContainer = useRef<HTMLDivElement | null>(null);
const [isCustomDiceRollerOpen, setIsCustomDiceRollerOpen] = useState(false);
const diceSets = useDiceSets(nodeEnv, env);
const setId = useUserDiceDataStore(
(state) => state.getUserDiceData(Number(user?.id))?.setId
);
const dmId = campaign
? String(CampaignUtils.getDmUserId(campaign))
: undefined;
const isSheet =
useSelector(characterEnvSelectors.getContext) ===
Constants.AppContextTypeEnum.SHEET;
useEffect(() => {
if (!canvasContainer.current?.contains(canvasElement)) {
canvasContainer.current?.appendChild(canvasElement);
}
});
useEffect(() => {
getMessageBroker().then((mb) => {
if (mb && campaign && userId) {
mb.gameId = isSheet ? CampaignUtils.getId(campaign).toString() : "0";
mb.userId = userId;
if (mb && user?.id) {
if (campaign) {
mb.gameId = CampaignUtils.getId(campaign).toString();
} else {
mb.gameId = undefined;
}
mb.userId = user.id;
}
ConfigUtils.configureRulesEngine({ messageBroker: mb });
if (diceFeatureConfiguration.enabled) {
const rulesEngineConfig = ConfigUtils.getCurrentRulesEngineConfig();
if (rulesEngineConfig.apiAdapter) {
rulesEngineConfig.apiAdapter
.get(`${diceFeatureConfiguration.apiEndpoint}${userConfigApi}`)
.then((x) => {
const diceCanvas = canvasElement;
Dice.init({
isDebug: false,
assetBaseLocation: diceFeatureConfiguration.assetBaseLocation,
trackingId: diceFeatureConfiguration.trackingId,
sessionName: "Dice",
notifier: new DiceNotifier(),
canvas: diceCanvas || undefined,
previewMode: false,
diceUserConfig: ApiAdapterUtils.getResponseData(x) as any,
})
.then(() =>
document.addEventListener("click", () => Dice.clear())
)
.catch((error: Error) => {
AppLoggerUtils.logMessage(
error.message,
Constants.LogMessageType.WARNING
);
dispatch(
appEnvActions.dataSet({
diceEnabled: false,
})
);
});
})
.catch((error: ApiException) => {
AppLoggerUtils.logMessage(
error.message,
Constants.LogMessageType.WARNING
);
dispatch(
appEnvActions.dataSet({
diceEnabled: false,
})
);
});
}
} else {
Dice.analyticsInit(diceFeatureConfiguration.trackingId, "Dice");
}
});
}, [diceFeatureConfiguration, dispatch]);
}, [
campaign,
diceFeatureConfiguration.apiEndpoint,
diceFeatureConfiguration.assetBaseLocation,
diceFeatureConfiguration.enabled,
diceFeatureConfiguration.trackingId,
dispatch,
isSheet,
user?.id,
]);
if (isVttView) return null;
return ReactDOM.createPortal(
<div className={"dice-rolling-panel"} ref={canvasContainer}>
{canShow && diceEnabled && (
<DiceToolbar
rollContext={characterRollContext}
rollTargetOptions={
messageTargetOptions && isSheet
? Object.values(messageTargetOptions?.entities).filter(
TypeScriptUtils.isNotNullOrUndefined
)
: undefined
<AnchoredPopover
offset={16}
open={isCustomDiceRollerOpen}
allowedPlacements={["top-start"]}
referenceComponent={
<Button
className={clsx([
styles.button,
isCustomDiceRollerOpen && styles.customDiceRollOpen,
])}
onClick={() => {
setIsCustomDiceRollerOpen(!isCustomDiceRollerOpen);
// isCustomDiceRollerOpen is the previous state here
if (isCustomDiceRollerOpen) {
instrumentDiceContainerClose(
user,
Platform.Web,
location,
campaign?.id,
undefined,
env
);
} else {
instrumentDiceContainerOpen(
user,
Platform.Web,
location,
campaign?.id,
undefined,
env
);
}
}}
themed
onMouseEnter={rollerImport}
>
<D20 />
</Button>
}
rollTargetDefault={defaultMessageTargetOption}
themeColor={theme.themeColor}
userId={parseInt(userId || "0", 10)}
/>
floatingArrow
floatingArrowClassName={styles.floatingArrow}
>
{diceSets && (
<CustomDiceRoller
rollCallback={() => setIsCustomDiceRollerOpen(false)}
onClose={() => setIsCustomDiceRollerOpen(false)}
diceSets={diceSets}
gameId={campaign?.id}
visible={isCustomDiceRollerOpen}
setId={setId}
entityType="character"
entityId={entityId}
name={characterName ?? undefined}
rollInstrumentationCallback={(eventPayload) =>
instrumentDiceRoll(
user,
eventPayload,
Platform.Web,
location,
campaign?.id,
undefined,
env
)
}
clearDiceCallback={() =>
instrumentDiceClear(
user,
Platform.Web,
location,
campaign?.id,
undefined,
env
)
}
dmId={dmId}
/>
)}
</AnchoredPopover>
)}
{/* We want this one so it is sticking around,
also this is where the canvas goes when we append */}