diff --git a/frontend/src/html/popups.html b/frontend/src/html/popups.html index 8bab25716116..e5dff36d5de4 100644 --- a/frontend/src/html/popups.html +++ b/frontend/src/html/popups.html @@ -57,21 +57,6 @@ - - - Dev options - generate data - quick login - toggle media query debug - show test notifications - show real words input - xp bar test - toggle fake chart data - toggle caret debug - disable slow timer fail - - - diff --git a/frontend/src/styles/core.scss b/frontend/src/styles/core.scss index 0789286106ea..2a15e4d16991 100644 --- a/frontend/src/styles/core.scss +++ b/frontend/src/styles/core.scss @@ -269,24 +269,6 @@ kbd { } } -#devButtons { - position: fixed; - left: 0; - top: 10rem; - display: grid; - grid-auto-flow: row; - gap: 0.5rem; - text-decoration: none; - z-index: 999999999; - border-radius: 0 1rem 1rem 0; - - .button { - padding: 1rem; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } -} - .avatar { --size: 1em; diff --git a/frontend/src/styles/popups.scss b/frontend/src/styles/popups.scss index 5418f2f68af1..bb8f7eeaffb7 100644 --- a/frontend/src/styles/popups.scss +++ b/frontend/src/styles/popups.scss @@ -777,12 +777,6 @@ body.darkMode { } } -#devOptionsModal { - .modal { - max-width: 400px; - } -} - #shareTestSettingsModal { .modal { max-width: 600px; diff --git a/frontend/src/ts/components/layout/overlays/Overlays.tsx b/frontend/src/ts/components/layout/overlays/Overlays.tsx index 80df879f0a7e..4dde727e09d1 100644 --- a/frontend/src/ts/components/layout/overlays/Overlays.tsx +++ b/frontend/src/ts/components/layout/overlays/Overlays.tsx @@ -1,8 +1,11 @@ -import { JSXElement } from "solid-js"; +import { JSXElement, Show } from "solid-js"; +import { envConfig } from "virtual:env-config"; import { getIsScreenshotting } from "../../../signals/core"; import { showModal } from "../../../stores/modals"; import { cn } from "../../../utils/cn"; +import { isDevEnvironment } from "../../../utils/misc"; +import { Button } from "../../common/Button"; import { Fa } from "../../common/Fa"; import { ScrollToTop } from "../footer/ScrollToTop"; import { Banners } from "./Banners"; @@ -34,6 +37,38 @@ export function Overlays(): JSXElement { + + + > ); } + +function DevButtons(): JSXElement { + return ( + + + showModal("DevOptions")} + fa={{ + icon: "fa-flask", + }} + class="rounded-tl-none rounded-bl-none p-2" + /> + + ); +} diff --git a/frontend/src/ts/components/modals/DevOptionsModal.tsx b/frontend/src/ts/components/modals/DevOptionsModal.tsx new file mode 100644 index 000000000000..a2e72abac929 --- /dev/null +++ b/frontend/src/ts/components/modals/DevOptionsModal.tsx @@ -0,0 +1,158 @@ +import { createSignal, For, JSXElement } from "solid-js"; +import { envConfig } from "virtual:env-config"; + +import { signIn } from "../../auth"; +import * as Notifications from "../../elements/notifications"; +import { update } from "../../elements/xp-bar"; +import { getInputElement } from "../../input/input-element"; +import { showPopup } from "../../modals/simple-modals"; +import { showLoaderBar, hideLoaderBar } from "../../signals/loader-bar"; +import { hideModal } from "../../stores/modals"; +import { toggleUserFakeChartData } from "../../test/result"; +import { disableSlowTimerFail } from "../../test/test-timer"; +import { FaSolidIcon } from "../../types/font-awesome"; +import { setMediaQueryDebugLevel } from "../../ui"; +import { toggleCaretDebug } from "../../utils/caret"; +import { createErrorMessage } from "../../utils/misc"; +import { AnimatedModal } from "../common/AnimatedModal"; +import { Button } from "../common/Button"; + +const [mediaQueryDebugLevel, setLocalMediaQueryDebugLevel] = createSignal(0); + +type DevButton = { + icon: FaSolidIcon; + label: () => string; + onClick: () => void; +}; + +export function DevOptionsModal(): JSXElement { + const buttons: DevButton[] = [ + { + icon: "fa-database", + label: () => "Generate Data", + onClick: () => showPopup("devGenerateData"), + }, + { + icon: "fa-bell", + label: () => "Test Notifications", + onClick: () => { + Notifications.add("This is a test", 1, { duration: 0 }); + Notifications.add("This is a test", 0, { duration: 0 }); + Notifications.add("This is a test", -1, { + duration: 0, + details: { test: true, error: "Example error message" }, + }); + hideModal("DevOptions"); + }, + }, + { + icon: "fa-ruler", + label: () => `Media Query Debug (${mediaQueryDebugLevel()})`, + onClick: () => { + const next = + mediaQueryDebugLevel() >= 2 ? 0 : mediaQueryDebugLevel() + 1; + setLocalMediaQueryDebugLevel(next); + Notifications.add(`Setting media query debug level to ${next}`, 5); + setMediaQueryDebugLevel(next); + }, + }, + { + icon: "fa-eye", + label: () => "Show Real Words Input", + onClick: () => { + const el = getInputElement(); + el.style.opacity = "1"; + el.style.marginTop = "1.5em"; + el.style.caretColor = "red"; + hideModal("DevOptions"); + }, + }, + { + icon: "fa-sign-in-alt", + label: () => "Quick Login", + onClick: () => { + if ( + envConfig.quickLoginEmail === undefined || + envConfig.quickLoginPassword === undefined + ) { + Notifications.add( + "Quick login credentials not set. Add QUICK_LOGIN_EMAIL and QUICK_LOGIN_PASSWORD to your frontend .env file.", + -1, + ); + return; + } + showLoaderBar(); + void signIn( + envConfig.quickLoginEmail, + envConfig.quickLoginPassword, + true, + ) + .then((result) => { + if (!result.success) { + Notifications.add(result.message, -1); + } + }) + .catch((error: unknown) => { + Notifications.add( + createErrorMessage(error, "Quick login failed"), + -1, + ); + }) + .finally(() => { + hideLoaderBar(); + }); + hideModal("DevOptions"); + }, + }, + { + icon: "fa-star", + label: () => "XP Bar Test", + onClick: () => { + setTimeout(() => { + void update(1000000, 20800, { + base: 100, + fullAccuracy: 200, + accPenalty: 300, + quote: 400, + punctuation: 500, + streak: 10_000, + configMultiplier: 2, + }); + }, 500); + hideModal("DevOptions"); + }, + }, + { + icon: "fa-chart-bar", + label: () => "Toggle Fake Chart Data", + onClick: toggleUserFakeChartData, + }, + { + icon: "fa-i-cursor", + label: () => "Toggle Caret Debug", + onClick: toggleCaretDebug, + }, + { + icon: "fa-clock", + label: () => "Disable Slow Timer Fail", + onClick: disableSlowTimerFail, + }, + ]; + + return ( + + + + {(btn) => ( + + )} + + + + ); +} diff --git a/frontend/src/ts/components/modals/Modals.tsx b/frontend/src/ts/components/modals/Modals.tsx index ec512d92bf58..8b67d9fe88ec 100644 --- a/frontend/src/ts/components/modals/Modals.tsx +++ b/frontend/src/ts/components/modals/Modals.tsx @@ -1,15 +1,25 @@ -import { JSXElement } from "solid-js"; +import { JSXElement, Show, Suspense, lazy } from "solid-js"; +import { isDevEnvironment } from "../../utils/misc"; import { ContactModal } from "./ContactModal"; import { SupportModal } from "./SupportModal"; import { VersionHistoryModal } from "./VersionHistoryModal"; +const DevOptionsModal = lazy(async () => + import("./DevOptionsModal").then((m) => ({ default: m.DevOptionsModal })), +); + export function Modals(): JSXElement { return ( <> + + + + + > ); } diff --git a/frontend/src/ts/index.ts b/frontend/src/ts/index.ts index f3a8f9e5f307..10f8dbc13943 100644 --- a/frontend/src/ts/index.ts +++ b/frontend/src/ts/index.ts @@ -32,10 +32,9 @@ import "./elements/no-css"; import { egVideoListener } from "./popups/video-ad-popup"; import "./states/connection"; import "./test/tts"; -import { isDevEnvironment, addToGlobal } from "./utils/misc"; +import { addToGlobal } from "./utils/misc"; import * as Focus from "./test/focus"; import { fetchLatestVersion } from "./utils/version"; -import { getDevOptionsModal } from "./utils/async-modules"; import * as Sentry from "./sentry"; import * as Cookies from "./cookies"; import "./elements/psa"; @@ -97,10 +96,4 @@ addToGlobal({ qsr: qsr, }); -if (isDevEnvironment()) { - void getDevOptionsModal().then((module) => { - module.appendButton(); - }); -} - mountComponents(); diff --git a/frontend/src/ts/modals/dev-options.ts b/frontend/src/ts/modals/dev-options.ts deleted file mode 100644 index b8726f2e7d0e..000000000000 --- a/frontend/src/ts/modals/dev-options.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { envConfig } from "virtual:env-config"; -import AnimatedModal from "../utils/animated-modal"; -import { showPopup } from "./simple-modals"; -import * as Notifications from "../elements/notifications"; -import { setMediaQueryDebugLevel } from "../ui"; -import { signIn } from "../auth"; - -import { showLoaderBar, hideLoaderBar } from "../signals/loader-bar"; -import { update } from "../elements/xp-bar"; -import { toggleUserFakeChartData } from "../test/result"; -import { toggleCaretDebug } from "../utils/caret"; -import { getInputElement } from "../input/input-element"; -import { disableSlowTimerFail } from "../test/test-timer"; -import { ElementWithUtils, qsr } from "../utils/dom"; - -let mediaQueryDebugLevel = 0; - -export function show(): void { - void modal.show(); -} - -async function setup(modalEl: ElementWithUtils): Promise { - modalEl.qs(".generateData")?.on("click", () => { - showPopup("devGenerateData"); - }); - modalEl.qs(".showTestNotifications")?.on("click", () => { - Notifications.add("This is a test", 1, { - duration: 0, - }); - Notifications.add("This is a test", 0, { - duration: 0, - }); - Notifications.add("This is a test", -1, { - duration: 0, - details: { test: true, error: "Example error message" }, - }); - void modal.hide(); - }); - modalEl.qs(".toggleMediaQueryDebug")?.on("click", () => { - mediaQueryDebugLevel++; - if (mediaQueryDebugLevel > 2) { - mediaQueryDebugLevel = 0; - } - Notifications.add( - `Setting media query debug level to ${mediaQueryDebugLevel}`, - 5, - ); - setMediaQueryDebugLevel(mediaQueryDebugLevel); - }); - modalEl.qs(".showRealWordsInput")?.on("click", () => { - getInputElement().style.opacity = "1"; - getInputElement().style.marginTop = "1.5em"; - getInputElement().style.caretColor = "red"; - void modal.hide(); - }); - modalEl.qs(".quickLogin")?.on("click", () => { - if ( - envConfig.quickLoginEmail === undefined || - envConfig.quickLoginPassword === undefined - ) { - Notifications.add( - "Quick login credentials not set. Add QUICK_LOGIN_EMAIL and QUICK_LOGIN_PASSWORD to your frontend .env file.", - -1, - ); - return; - } - showLoaderBar(); - void signIn( - envConfig.quickLoginEmail, - envConfig.quickLoginPassword, - true, - ).then(() => { - hideLoaderBar(); - }); - void modal.hide(); - }); - modalEl.qs(".xpBarTest")?.on("click", () => { - setTimeout(() => { - void update(1000000, 20800, { - base: 100, - fullAccuracy: 200, - accPenalty: 300, - quote: 400, - punctuation: 500, - streak: 10_000, - configMultiplier: 2, - }); - }, 500); - void modal.hide(); - }); - modalEl.qs(".toggleFakeChartData")?.on("click", () => { - toggleUserFakeChartData(); - }); - modalEl.qs(".toggleCaretDebug")?.on("click", () => { - toggleCaretDebug(); - }); - modalEl.qs(".disableSlowTimerFail")?.on("click", () => { - disableSlowTimerFail(); - }); -} - -const modal = new AnimatedModal({ - dialogId: "devOptionsModal", - setup, -}); - -export function appendButton(): void { - qsr("body").prependHtml( - ` - - - - - `, - ); - document - .querySelector("#devButtons .button.showDevOptionsModal") - ?.addEventListener("click", () => { - show(); - }); -} diff --git a/frontend/src/ts/stores/modals.ts b/frontend/src/ts/stores/modals.ts index 749ebf11a593..6a4c4a0a18e4 100644 --- a/frontend/src/ts/stores/modals.ts +++ b/frontend/src/ts/stores/modals.ts @@ -1,6 +1,11 @@ import { createStore } from "solid-js/store"; -export type ModalId = "VersionHistory" | "Contact" | "Support" | "Commandline"; +export type ModalId = + | "VersionHistory" + | "Contact" + | "Support" + | "Commandline" + | "DevOptions"; export type ModalVisibility = { visible: boolean; diff --git a/frontend/src/ts/utils/async-modules.ts b/frontend/src/ts/utils/async-modules.ts index d9f8064b083c..e69de29bb2d1 100644 --- a/frontend/src/ts/utils/async-modules.ts +++ b/frontend/src/ts/utils/async-modules.ts @@ -1,32 +0,0 @@ -import { showLoaderBar, hideLoaderBar } from "../signals/loader-bar"; -import * as Notifications from "../elements/notifications"; -import { createErrorMessage } from "./misc"; -import * as Skeleton from "../utils/skeleton"; - -Skeleton.save("devOptionsModal"); - -export async function getDevOptionsModal(): Promise< - typeof import("../modals/dev-options.js") -> { - try { - showLoaderBar(); - const module = await import("../modals/dev-options.js"); - hideLoaderBar(); - return module; - } catch (e) { - hideLoaderBar(); - if ( - e instanceof Error && - e.message.includes("Failed to fetch dynamically imported module") - ) { - Notifications.add( - "Failed to load dev options module: could not fetch", - -1, - ); - } else { - const msg = createErrorMessage(e, "Failed to load dev options module"); - Notifications.add(msg, -1); - } - throw e; - } -}