Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion frontend/__tests__/__harness__/mock-firebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ import { vi } from "vitest";
vi.mock("../../src/ts/firebase", () => ({
app: undefined,
Auth: undefined,
isAuthenticated: () => false,
}));
2 changes: 1 addition & 1 deletion frontend/src/ts/ape/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Config } from "@monkeytype/schemas/configs";
import { isAuthenticated } from "../firebase";
import { isAuthenticated } from "../states/core";
import { showErrorNotification } from "../states/notifications";
import Ape from ".";

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import { authEvent } from "./events/auth";
import {
isAuthAvailable,
getAuthenticatedUser,
isAuthenticated,
signOut as authSignOut,
signInWithEmailAndPassword,
createUserWithEmailAndPassword,
signInWithPopup,
resetIgnoreAuthCallback,
} from "./firebase";
import * as Sentry from "./sentry";
import { isAuthenticated } from "./states/core";
Comment on lines 23 to +25
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auth.signOut() now relies on states/core.isAuthenticated() (userId signal). During Google/GitHub new-user signup flows ignoreAuthCallback prevents setUserId, so isAuthenticated() stays false even though Firebase currentUser exists; this makes signOut() a no-op and can leave the user signed in after cancelling/failing signup. Fix by basing the guard on getAuthenticatedUser() !== null (or just calling authSignOut() whenever isAuthAvailable()), rather than the reactive userId state.

Copilot uses AI. Check for mistakes.
import { showLoaderBar, hideLoaderBar } from "./states/loader-bar";
import {
showNoticeNotification,
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/ts/collections/inbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Accessor, createSignal } from "solid-js";
import Ape from "../ape";
import { queryClient } from "../queries";
import { baseKey } from "../queries/utils/keys";
import { isLoggedIn } from "../states/core";
import { isAuthenticated } from "../states/core";
import { flushDebounceStrategy } from "./utils/flushDebounceStrategy";
import { showErrorNotification } from "../states/notifications";

Expand Down Expand Up @@ -92,7 +92,7 @@ export async function flushPendingChanges({
// oxlint-disable-next-line typescript/explicit-function-return-type
export function useInboxQuery(enabled: Accessor<boolean>) {
return useLiveQuery((q) => {
if (!isLoggedIn() || !enabled()) return undefined;
if (!isAuthenticated() || !enabled()) return undefined;
return q
.from({ inbox: inboxCollection })
.where(({ inbox }) => not(eq(inbox.status, "deleted")))
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/ts/commandline/commandline-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import * as SoundController from "../controllers/sound-controller";
import * as TestLogic from "../test/test-logic";
import { getLanguageDisplayString } from "../utils/strings";
import * as ModesNotice from "../elements/modes-notice";
import { isAuthenticated } from "../firebase";

import { areUnsortedArraysEqual } from "../utils/arrays";
import { Config } from "../config/store";
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
import { getActivePage } from "../states/core";
import { getActivePage, isAuthenticated } from "../states/core";
import { Fonts } from "../constants/fonts";
import { KnownFontName } from "@monkeytype/schemas/fonts";
import * as UI from "../ui";
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/ts/commandline/lists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import * as TestStats from "../test/test-stats";
import { Command, CommandsSubgroup } from "./types";
import { buildCommandForConfigKey } from "./util";
import { CommandlineConfigMetadataObject } from "./commandline-metadata";
import { isAuthAvailable, isAuthenticated, signOut } from "../firebase";
import { isAuthAvailable, signOut } from "../firebase";
import { isAuthenticated } from "../states/core";
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commandline signOut command availability now depends on states/core.isAuthenticated(). In flows where Firebase has a currentUser but the app intentionally hasn't set userId yet (e.g. new-user OAuth signup with ignoreAuthCallback), this hides the Sign out command and prevents recovery. Use getAuthenticatedUser() !== null (or remove the isAuthenticated() check) for this command’s available() predicate.

Suggested change
import { isAuthenticated } from "../states/core";

Copilot uses AI. Check for mistakes.
import { ConfigKey } from "@monkeytype/schemas/configs";
import {
hideFpsCounter,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/commandline/lists/custom-themes-list.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { setConfig } from "../../config/setters";
import { isAuthenticated } from "../../firebase";
import { isAuthenticated } from "../../states/core";
import * as DB from "../../db";
import * as ThemeController from "../../controllers/theme-controller";
import { Command, CommandsSubgroup } from "../types";
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/commandline/lists/navigation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { navigate } from "../../controllers/route-controller";
import { isAuthenticated } from "../../firebase";
import { isAuthenticated } from "../../states/core";
import { toggleFullscreen } from "../../utils/misc";
import { Command } from "../types";

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/commandline/lists/presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as ModesNotice from "../../elements/modes-notice";
import * as Settings from "../../pages/settings";
import * as PresetController from "../../controllers/preset-controller";
import * as EditPresetPopup from "../../modals/edit-preset";
import { isAuthenticated } from "../../firebase";
import { isAuthenticated } from "../../states/core";
import { Command, CommandsSubgroup } from "../types";

const subgroup: CommandsSubgroup = {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/commandline/lists/quote-favorites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
showErrorNotification,
showSuccessNotification,
} from "../../states/notifications";
import { isAuthenticated } from "../../firebase";
import { isAuthenticated } from "../../states/core";
import { showLoaderBar, hideLoaderBar } from "../../states/loader-bar";
import * as TestWords from "../../test/test-words";
import { Command } from "../types";
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/commandline/lists/tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as ModesNotice from "../../elements/modes-notice";
import * as TagController from "../../controllers/tag-controller";
import { Config } from "../../config/store";
import * as PaceCaret from "../../test/pace-caret";
import { isAuthenticated } from "../../firebase";
import { isAuthenticated } from "../../states/core";
import { Command, CommandsSubgroup } from "../types";

const subgroup: CommandsSubgroup = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { AnimePresence } from "./AnimePresence";
* @example
* ```tsx
* <AnimeConditional
* if={isLoggedIn()}
* if={isAuthenticated()}
* then={<Dashboard />}
* else={<LoginForm />}
* exitBeforeEnter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { JSXElement, Show } from "solid-js";

import { setConfig } from "../../../config/setters";
import { Config } from "../../../config/store";
import { isAuthenticated } from "../../../firebase";
import {
getThemeIndicator,
isAuthenticated,
setCommandlineSubgroup,
} from "../../../states/core";
import { showModal } from "../../../states/modals";
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/ts/components/modals/MobileTestConfigModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { For, JSXElement, Show } from "solid-js";
import { setConfig, setQuoteLengthAll } from "../../config/setters";
import { getConfig } from "../../config/store";
import { restartTestEvent } from "../../events/test";
import { isLoggedIn } from "../../states/core";
import { isAuthenticated } from "../../states/core";
import { showModal } from "../../states/modals";
import { areUnsortedArraysEqual } from "../../utils/arrays";
import { AnimatedModal } from "../common/AnimatedModal";
Expand Down Expand Up @@ -186,7 +186,7 @@ export function MobileTestConfigModal(): JSXElement {
<Show when={getConfig.mode === "quote"}>
<For each={quoteLengths}>
{(ql) => (
<Show when={!("loginRequired" in ql) || isLoggedIn()}>
<Show when={!("loginRequired" in ql) || isAuthenticated()}>
<MCButton
text={ql.label}
active={isQuoteLengthActive(ql.value)}
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/ts/components/modals/QuoteSearchModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Config } from "../../config/store";
import { isCaptchaAvailable } from "../../controllers/captcha-controller";
import QuotesController, { Quote } from "../../controllers/quotes-controller";
import * as DB from "../../db";
import { isLoggedIn } from "../../states/core";
import { isAuthenticated } from "../../states/core";
import { hideLoaderBar, showLoaderBar } from "../../states/loader-bar";
import {
hideModalAndClearChain,
Expand Down Expand Up @@ -105,7 +105,7 @@ function Item(props: {
onReport: () => void;
onToggleFavorite: () => Promise<boolean>;
}): JSXElement {
const loggedOut = (): boolean => !isLoggedIn();
const loggedOut = (): boolean => !isAuthenticated();
const [isFav, setIsFav] = createSignal(
// oxlint-disable-next-line solid/reactivity -- intentionally reading once as initial value
!loggedOut() && QuotesController.isQuoteFavorite(props.quote),
Expand Down Expand Up @@ -455,7 +455,7 @@ export function QuoteSearchModal(): JSXElement {
<div class="flex flex-col justify-between gap-2 sm:flex-row">
<div class="text-2xl text-sub">Quote search</div>
<div class="grid gap-2">
<Show when={isLoggedIn()}>
<Show when={isAuthenticated()}>
<Button
fa={{ icon: "fa-plus" }}
text="Submit a quote"
Expand Down Expand Up @@ -509,7 +509,7 @@ export function QuoteSearchModal(): JSXElement {
}}
/>
</div>
<Show when={isLoggedIn()}>
<Show when={isAuthenticated()}>
<Button
variant="button"
fa={{ icon: "fa-heart", fixedWidth: true }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
getRankQueryOptions,
} from "../../../queries/leaderboards";
import { getServerConfigurationQueryOptions } from "../../../queries/server-configuration";
import { getActivePage, isLoggedIn } from "../../../states/core";
import { getActivePage, isAuthenticated } from "../../../states/core";
import {
getGoToUserPage,
getPage,
Expand Down Expand Up @@ -100,7 +100,7 @@ export function LeaderboardPage(): JSXElement {

const rankQuery = useQuery(() => ({
...getRankQueryOptions(getSelection()),
enabled: isLoggedIn() && isOpen(),
enabled: isAuthenticated() && isOpen(),
}));

const serverConfigurationQuery = useQuery(() => ({
Expand Down Expand Up @@ -191,7 +191,7 @@ export function LeaderboardPage(): JSXElement {
/>

<Show
when={isLoggedIn() && !dataQuery.isLoading}
when={isAuthenticated() && !dataQuery.isLoading}
fallback={<Separator />}
>
<AsyncContent
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/ts/components/pages/leaderboard/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Language } from "@monkeytype/schemas/languages";
import { Mode } from "@monkeytype/schemas/shared";
import { Accessor, For, JSXElement, Show } from "solid-js";

import { isLoggedIn } from "../../../states/core";
import { isAuthenticated } from "../../../states/core";
import { Selection } from "../../../states/leaderboard-selection";
import { FaSolidIcon } from "../../../types/font-awesome";
import { Button } from "../../common/Button";
Expand Down Expand Up @@ -65,7 +65,7 @@ export function Sidebar(props: {
{ id: "daily", text: "daily", icon: "fa-sun" },
]}
/>
<Show when={isLoggedIn() && props.connectionsEnabled}>
<Show when={isAuthenticated() && props.connectionsEnabled}>
<Group
selected={props.selection().friendsOnly}
onSelect={selectFriendsOnly}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/ts/components/pages/profile/UserDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { addFriend, isFriend } from "../../../db";
import * as EditProfileModal from "../../../modals/edit-profile";
import * as UserReportModal from "../../../modals/user-report";
import { bp } from "../../../states/breakpoints";
import { getUserId, isLoggedIn } from "../../../states/core";
import { getUserId, isAuthenticated } from "../../../states/core";
import {
showNoticeNotification,
showErrorNotification,
Expand Down Expand Up @@ -113,7 +113,7 @@ function ActionButtons(props: {

const [hasFriendRequest, setHasFriendRequest] = createSignal(false);
const showFriendsButton = () =>
isLoggedIn() && !isUsersProfile() && !hasFriendRequest();
isAuthenticated() && !isUsersProfile() && !hasFriendRequest();

createEffect(() => {
setHasFriendRequest(
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/ts/components/pages/test/TestConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getConfig } from "../../../config/store";
import { restartTestEvent } from "../../../events/test";
import { createEffectOn } from "../../../hooks/effects";
import { useRefWithUtils } from "../../../hooks/useRefWithUtils";
import { isLoggedIn } from "../../../states/core";
import { isAuthenticated } from "../../../states/core";
import { showModal } from "../../../states/modals";
import { getResultVisible, getFocus } from "../../../states/test";
import { FaObject } from "../../../types/font-awesome";
Expand Down Expand Up @@ -345,7 +345,7 @@ function Mode2Quote(props: ComponentProps<"div">): JSXElement {
/>
)}
</For>
<Show when={isLoggedIn()}>
<Show when={isAuthenticated()}>
<Button
class={buttonClass}
fa={{
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/config/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { checkCompatibility } from "@monkeytype/funbox";
import * as DB from "../db";
import { showNoticeNotification } from "../states/notifications";
import { isAuthenticated } from "../firebase";
import { isAuthenticated } from "../states/core";
import { canSetFunboxWithConfig } from "./funbox-validation";
import { reloadAfter } from "../utils/misc";
import { isDevEnvironment } from "../utils/env";
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/ts/controllers/route-controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as PageController from "./page-controller";
import * as TestUI from "../test/test-ui";
import * as PageTransition from "../legacy-states/page-transition";
import { isAuthAvailable, isAuthenticated } from "../firebase";
import { isAuthAvailable } from "../firebase";
import { isAuthenticated } from "../states/core";
import { isFunboxActive } from "../test/funbox/list";
import * as TestState from "../test/test-state";
import { showNoticeNotification } from "../states/notifications";
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/ts/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
showNoticeNotification,
showErrorNotification,
} from "./states/notifications";
import { isAuthenticated, getAuthenticatedUser } from "./firebase";
import { getAuthenticatedUser } from "./firebase";
import { isAuthenticated } from "./states/core";
import { lastElementFromArray } from "./utils/arrays";
import * as Dates from "date-fns";
import {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/elements/account-settings/ape-key-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from "@monkeytype/schemas/ape-keys";
import { format } from "date-fns/format";
import { SimpleModal, TextArea } from "../simple-modal";
import { isAuthenticated } from "../../firebase";
import { isAuthenticated } from "../../states/core";
import { qs, qsr } from "../../utils/dom";

const editApeKey = new SimpleModal({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { showErrorNotification } from "../../states/notifications";
import { Connection } from "@monkeytype/schemas/connections";
import Ape from "../../ape";
import { format } from "date-fns/format";
import { isAuthenticated } from "../../firebase";
import { isAuthenticated } from "../../states/core";
import * as DB from "../../db";
import { getReceiverUid } from "../../db";
import { qsr } from "../../utils/dom";
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/elements/modes-notice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as Last10Average from "../elements/last-10-average";
import { Config } from "../config/store";
import * as TestWords from "../test/test-words";
import { configEvent, type ConfigEventKey } from "../events/config";
import { isAuthenticated } from "../firebase";
import { isAuthenticated } from "../states/core";
import * as CustomTextState from "../legacy-states/custom-text-name";
import { getLanguageDisplayString } from "../utils/strings";
import Format from "../singletons/format";
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/ts/elements/settings/theme-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import {
import { showLoaderBar, hideLoaderBar } from "../../states/loader-bar";
import * as DB from "../../db";
import { configEvent } from "../../events/config";
import { isAuthenticated } from "../../firebase";
import { getActivePage } from "../../states/core";
import { getActivePage, isAuthenticated } from "../../states/core";
import { ThemeName } from "@monkeytype/schemas/configs";
import { captureException } from "../../sentry";
import { ColorName, ThemesList, ThemeWithName } from "../../constants/themes";
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/event-handlers/account.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as PbTablesModal from "../modals/pb-tables";
import * as EditProfileModal from "../modals/edit-profile";
import { getSnapshot } from "../db";
import { isAuthenticated } from "../firebase";
import { isAuthenticated } from "../states/core";
import {
showNoticeNotification,
showErrorNotification,
Expand Down
4 changes: 0 additions & 4 deletions frontend/src/ts/firebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,6 @@ export async function init(callback: ReadyCallback): Promise<void> {
}
}

export function isAuthenticated(): boolean {
return Auth?.currentUser !== undefined && Auth?.currentUser !== null;
}

/**
*
* @returns the current user if authenticated, else `null`
Expand Down
7 changes: 2 additions & 5 deletions frontend/src/ts/modals/simple-modals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ import { showNoticeNotification } from "../states/notifications";
import * as Settings from "../pages/settings";
import * as ThemePicker from "../elements/settings/theme-picker";
import { FirebaseError } from "firebase/app";
import {
isAuthenticated,
getAuthenticatedUser,
isAuthAvailable,
} from "../firebase";
import { getAuthenticatedUser, isAuthAvailable } from "../firebase";
import { isAuthenticated } from "../states/core";
import {
EmailAuthProvider,
User,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/modals/user-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import * as CaptchaController from "../controllers/captcha-controller";
import SlimSelect from "slim-select";
import AnimatedModal from "../utils/animated-modal";
import { isAuthenticated } from "../firebase";
import { isAuthenticated } from "../states/core";
import { CharacterCounter } from "../elements/character-counter";
import { ReportUserReason } from "@monkeytype/schemas/users";
import { qsr } from "../utils/dom";
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/ts/pages/account-settings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PageWithUrlParams } from "./page";
import * as Skeleton from "../utils/skeleton";
import { getAuthenticatedUser, isAuthenticated } from "../firebase";
import { getActivePage } from "../states/core";
import { getAuthenticatedUser } from "../firebase";
import { getActivePage, isAuthenticated } from "../states/core";
import { swapElements } from "../utils/misc";
import { getSnapshot } from "../db";
import Ape from "../ape";
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/ts/pages/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ import {
} from "../states/notifications";
import * as ImportExportSettingsModal from "../modals/import-export-settings";
import { configEvent, type ConfigEventKey } from "../events/config";
import { getActivePage } from "../states/core";
import { getActivePage, isAuthenticated } from "../states/core";
import { PageWithUrlParams } from "./page";
import { isAuthenticated } from "../firebase";
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
import SlimSelect from "slim-select";
import * as Skeleton from "../utils/skeleton";
Expand Down
Loading
Loading