diff --git a/src/components/features/charactersAndCampaigns/GameLog/GameLogEntry.tsx b/src/components/features/charactersAndCampaigns/GameLog/GameLogEntry.tsx
index e31bf592..350f332d 100644
--- a/src/components/features/charactersAndCampaigns/GameLog/GameLogEntry.tsx
+++ b/src/components/features/charactersAndCampaigns/GameLog/GameLogEntry.tsx
@@ -26,7 +26,7 @@ export function GameLogEntry(props: GameLogEntryProps) {
);
const logCreatorName = useStore(
- (store) => store.users.userMap[log.uid].doc?.displayName
+ (store) => store.users.userMap[log.uid]?.doc?.displayName
);
const isYourEntry = log.characterId
diff --git a/src/components/shared/Layout/nav/CustomTokenDialog/CustomTokenDialog.tsx b/src/components/shared/Layout/nav/CustomTokenDialog/CustomTokenDialog.tsx
new file mode 100644
index 00000000..e12cecbb
--- /dev/null
+++ b/src/components/shared/Layout/nav/CustomTokenDialog/CustomTokenDialog.tsx
@@ -0,0 +1,54 @@
+import {
+ Button,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ TextField,
+} from "@mui/material";
+import { DialogTitleWithCloseButton } from "components/shared/DialogTitleWithCloseButton";
+import { loginWithToken } from "lib/auth.lib";
+import { useState } from "react";
+
+export interface CustomTokenDialogProps {
+ open: boolean;
+ onClose: () => void;
+}
+
+export function CustomTokenDialog(props: CustomTokenDialogProps) {
+ const { open, onClose } = props;
+
+ const [token, setToken] = useState("");
+ const handleLogin = () => {
+ loginWithToken(token)
+ .then(() => {
+ location.reload();
+ })
+ .catch((error) => {
+ console.error(error);
+ });
+ };
+
+ return (
+
+ );
+}
diff --git a/src/components/shared/Layout/nav/SettingsMenu.tsx b/src/components/shared/Layout/nav/SettingsMenu.tsx
index 6c5b8a01..bdcf3012 100644
--- a/src/components/shared/Layout/nav/SettingsMenu.tsx
+++ b/src/components/shared/Layout/nav/SettingsMenu.tsx
@@ -31,6 +31,8 @@ import { useStore } from "stores/store";
import { AUTH_STATE } from "stores/auth/auth.slice.type";
import { UserNameDialog } from "components/shared/UserNameDialog";
import UsernameIcon from "@mui/icons-material/AccountCircle";
+import TokenIcon from "@mui/icons-material/Contacts";
+import { CustomTokenDialog } from "./CustomTokenDialog/CustomTokenDialog";
export function SettingsMenu() {
const [menuOpen, setMenuOpen] = useState(false);
@@ -46,6 +48,7 @@ export function SettingsMenu() {
const [betaTestsOpen, setBetaTestsOpen] = useState(false);
const [usernameDialogOpen, setUsernameDialogOpen] = useState(false);
+ const [customTokenDialogOpen, setCustomTokenDialogOpen] = useState(false);
const isMobile = useIsMobile();
@@ -178,6 +181,14 @@ export function SettingsMenu() {
Switch System
)}
+ {isLocal && (
+
+ )}
setUsernameDialogOpen(false)}
updating
/>
+ {isLocal && (
+ setCustomTokenDialogOpen(false)}
+ />
+ )}
>
);
}
diff --git a/src/lib/auth.lib.ts b/src/lib/auth.lib.ts
index 2b8cb4a6..ba4bdd21 100644
--- a/src/lib/auth.lib.ts
+++ b/src/lib/auth.lib.ts
@@ -3,6 +3,7 @@ import {
isSignInWithEmailLink,
onAuthStateChanged,
sendSignInLinkToEmail,
+ signInWithCustomToken,
signInWithEmailLink,
signInWithPopup,
signOut,
@@ -30,6 +31,19 @@ export function loginWithGoogle() {
});
}
+export function loginWithToken(token: string) {
+ return new Promise((resolve, reject) => {
+ signInWithCustomToken(firebaseAuth, token)
+ .then(() => {
+ resolve(true);
+ })
+ .catch((e) => {
+ console.error(e);
+ reject(e);
+ });
+ });
+}
+
export function sendMagicEmailLink(
email: string,
name?: string