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:
+202
@@ -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" }} />
|
||||
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 };
|
||||
Reference in New Issue
Block a user