Grabbed dndbeyond's source code

```
~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js
```
This commit is contained in:
2025-05-28 11:50:03 -07:00
commit 8df9031d27
3592 changed files with 319051 additions and 0 deletions
@@ -0,0 +1,202 @@
import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
import CloseIcon from "@mui/icons-material/Close";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import Grid from "@mui/material/Grid";
import IconButton from "@mui/material/IconButton";
import Link from "@mui/material/Link";
import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { rulesEngineSelectors } from "@dndbeyond/character-rules-engine/es";
import characterGalleryImg from "~/images/character-gallery.png";
import characterSheetImg from "~/images/character-sheet.png";
import tadaEmoji from "~/images/tada-emoji.svg?url";
type Props = {
characterId: number;
campaignId?: number | null;
};
export const ClaimConfirmationDialog: React.FC<Props> = ({
characterId,
campaignId,
}) => {
const [isOpen, setIsOpen] = useState<boolean>(true);
const theme = useSelector(rulesEngineSelectors.getCharacterTheme);
const { isDarkMode } = theme;
const close = () => {
window.location.href = `/characters/${characterId}`;
};
useEffect(() => {
characterId && setIsOpen(true);
}, [characterId]);
return (
<Dialog
onClose={close}
open={isOpen}
fullWidth={true}
sx={{
"& .MuiPaper-root": {
maxWidth: "644px",
backgroundColor: isDarkMode ? "#374045" : "white",
},
}}
>
<DialogTitle
sx={{
m: 0,
p: "24px 32px 16px",
fontSize: "18px",
fontWeight: 700,
color: isDarkMode ? "white" : "#12181C",
}}
>
<Box component="img" src={tadaEmoji} alt="Ta-da emoji" />{" "}
Congratulations!
<IconButton
aria-label="close"
onClick={close}
sx={{
position: "absolute",
right: 8,
top: 8,
color: isDarkMode
? "rgba(255, 255, 255, 0.54)"
: "rgba(0, 0, 0, 0.54)",
"&:hover": {
backgroundColor: isDarkMode
? "rgba(220, 223, 225, 0.08)"
: "#ECEDEE",
},
}}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent sx={{ m: 0, p: "0 24px 24px" }}>
<Grid container justifyContent="center">
<DialogContentText
textAlign={"center"}
sx={{ color: isDarkMode ? "white" : "#12181C" }}
>
Your character has been claimed and is now available:
</DialogContentText>
<Grid item container spacing={2}>
<Grid
item
container
xs={12}
sm={6}
direction="column"
alignItems="center"
>
<Box
component="img"
sx={{ width: "100%" }}
src={characterGalleryImg}
alt="Premade character gallery"
></Box>
<Button
variant="outlined"
href="/characters"
sx={{
height: "40px",
width: "230px",
marginTop: "-20px",
color: "#374045",
fontSize: "12px",
lineHeight: "15px",
backgroundColor: "#fff",
border: "2px solid #3298ED",
borderRadius: "8px",
boxShadow: "none",
"&:hover": {
color: "#fff",
backgroundColor: "#232B2F",
border: "2px solid #3298ED",
boxShadow: "none",
},
}}
>
Go to my characters{" "}
<ArrowForwardIosIcon
sx={{ position: "absolute", right: "8px", height: "14px" }}
/>
</Button>
</Grid>
<Grid
item
container
xs={12}
sm={6}
direction="column"
alignItems="center"
>
<Box
component="img"
sx={{
width: "100%",
}}
src={characterSheetImg}
alt="Sample character sheet"
></Box>
<Button
variant="contained"
href={`/characters/${characterId}`}
sx={{
height: "40px",
width: "230px",
marginTop: "-20px",
color: "#fff",
fontSize: "12px",
lineHeight: "15px",
backgroundColor: "#3298ED",
borderRadius: "8px",
boxShadow: "none",
"&:hover": {
color: "#fff",
backgroundColor: "#53ADF0",
boxShadow: "none",
},
}}
>
View your character{" "}
<ArrowForwardIosIcon
sx={{ position: "absolute", right: "8px", height: "14px" }}
/>
</Button>
</Grid>
</Grid>
{campaignId && (
<Link
href={`/campaigns/${campaignId}`}
variant="button"
underline="none"
sx={{ marginTop: "20px", color: "#3298ED" }}
>
or return to your campaign
</Link>
)}
</Grid>
</DialogContent>
</Dialog>
);
};
export default ClaimConfirmationDialog;
@@ -0,0 +1,222 @@
import CloseIcon from "@mui/icons-material/Close";
import VideocamIcon from "@mui/icons-material/Videocam";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import IconButton from "@mui/material/IconButton";
import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { rulesEngineSelectors } from "@dndbeyond/character-rules-engine/es";
import { MaxCharactersDialog } from "~/components/MaxCharactersDialog";
import { useAuth } from "~/contexts/Authentication";
import { useClaimCharacter } from "~/hooks/useClaimCharacter";
import { appEnvSelectors } from "~/tools/js/Shared/selectors";
import ClaimConfirmationDialog from "./ClaimConfirmationDialog";
export const WatchTourDialog: React.FC = () => {
const theme = useSelector(rulesEngineSelectors.getCharacterTheme);
const { isDarkMode } = theme;
const params = new URLSearchParams(globalThis.location.search);
const campaignJoinCode = params.get("campaignJoinCode");
const isAssigned = params.get("isAssigned") === "false" ? false : true;
const [isOpen, setIsOpen] = useState<boolean>(false);
const { characterSlotLimit, activeCharacterCount } = useSelector(
appEnvSelectors.getCharacterSlots
);
// Character slot limit is null for admin accounts
const hasOpenSlot =
characterSlotLimit === null || activeCharacterCount < characterSlotLimit;
const [isMaxCharacterMessageOpen, setIsMaxCharacterMessageOpen] =
useState(false);
const user = useAuth();
const signedIn = !!user;
const signupLink = `/create-account?returnUrl=${window.location.pathname}`;
const [
claimCharacter,
isClaimingCharacter,
isFinishedClaimingCharacter,
newCharacterId,
campaignId,
] = useClaimCharacter({
campaignJoinCode,
isAssigned,
});
const open = () => {
setIsOpen(true);
};
const close = () => {
setIsOpen(false);
};
const handleClaim = () => {
if (hasOpenSlot) {
claimCharacter();
} else {
setIsMaxCharacterMessageOpen(true);
setIsOpen(false);
}
};
useEffect(() => {
if (isFinishedClaimingCharacter) {
setIsOpen(false);
}
}, [isFinishedClaimingCharacter]);
return (
<>
<Button
variant="contained"
onClick={open}
sx={{
color: "white",
fontSize: "12px",
lineHeight: "15px",
width: "fit-content",
padding: "12px 32px",
backgroundColor: "#C50009",
borderRadius: "8px",
boxShadow: "none",
"&:hover": { backgroundColor: "#A7080B", boxShadow: "none" },
}}
>
<VideocamIcon sx={{ m: "-5px 0" }} />
&nbsp;Watch Tour
</Button>
<Dialog
onClose={close}
open={isOpen}
fullWidth={true}
maxWidth={"sm"}
sx={{
" .MuiPaper-root": {
backgroundColor: isDarkMode ? "#374045" : "white",
},
}}
>
<DialogTitle
sx={{
m: 0,
p: "24px 32px 8px",
fontSize: "18px",
fontWeight: 700,
color: isDarkMode ? "white" : "#12181C",
}}
>
D&D Beyond Character Sheet Tour
<IconButton
aria-label="close"
onClick={close}
sx={{
position: "absolute",
right: 8,
top: 8,
color: isDarkMode
? "rbga(255, 255, 255, 0.54)"
: "rgba(0, 0, 0, 0.54)",
"&:hover": "#ECEDEE",
}}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent sx={{ m: 0, p: "0 32px 24px" }}>
<Box
noValidate
component="form"
sx={{
display: "flex",
flexDirection: "column",
gap: 2,
}}
>
<div
style={{
position: "relative",
width: "100%",
height: 0,
paddingBottom: "56.25%",
}}
>
<iframe
src="https://www.youtube-nocookie.com/embed/ChYPIdCrBdE"
title="D&D Beyond Sheet Tour"
style={{
border: 0,
position: "absolute",
width: "100%",
height: "100%",
backgroundColor: "gray",
}}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
></iframe>
</div>
<DialogContentText
sx={{
color: isDarkMode ? "white" : "#12181C",
" a": {
color: "#3298ED",
textDecoration: "none",
},
" a:hover": { color: "#53ADF0" },
}}
>
For a complete guide to getting your game started, see{" "}
<a href="https://www.dndbeyond.com/posts/754-how-to-play-dungeons-dragons-using-d-d-beyond">
How to Play Dungeons & Dragons Using D&D Beyond
</a>
.
</DialogContentText>
<Button
variant="contained"
disabled={isClaimingCharacter}
{...(!signedIn && { href: signupLink })}
{...(signedIn && { onClick: handleClaim })}
sx={{
color: "white",
fontSize: "12px",
lineHeight: "15px",
width: "fit-content",
padding: "12px 32px",
m: "auto",
backgroundColor: "#3298ED",
borderRadius: "8px",
boxShadow: "none",
"&:hover": { backgroundColor: "#53ADF0", boxShadow: "none" },
}}
>
Claim Character
</Button>
</Box>
</DialogContent>
</Dialog>
<MaxCharactersDialog
open={isMaxCharacterMessageOpen}
onClose={() => setIsMaxCharacterMessageOpen(false)}
useMyCharactersLink
/>
{isFinishedClaimingCharacter && !!newCharacterId && (
<ClaimConfirmationDialog
characterId={newCharacterId}
campaignId={campaignId}
/>
)}
</>
);
};
export default WatchTourDialog;
@@ -0,0 +1,4 @@
import WatchTourDialog from "./WatchTourDialog";
export default WatchTourDialog;
export { WatchTourDialog };