Skip to content
Open
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ You can also check the

# Unreleased

Nothing yet.
- Feature
- flags can now be toggle by clicking "ctrl + t + d" on the keyboard (t,d =>
toggle debug)

# [5.2.4] - 2025-02-06

Expand Down
39 changes: 38 additions & 1 deletion app/flags/useFlag.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import qs from "qs";
import { useEffect, useState } from "react";

import { flag } from "./flag";
import { useKeyboardShortcut } from "@/utils/use-keyboard-shortcuts";

import { flag, FLAG_PREFIX } from "./flag";
import { FlagName } from "./types";

export default function useFlagValue(name: FlagName) {
Expand Down Expand Up @@ -40,3 +43,37 @@ export function useFlags() {

return flags;
}

export const useDebugShortcut = (enable?: boolean) => {
useKeyboardShortcut(
{
keys: ["t", "d"],
ctrlKey: true,
},
(event) => {
event.preventDefault();
enable && addDebugFlag();
}
);
};

const addDebugFlag = () => {
const currentUrl = new URL(window.location.href);
const currentParams = qs.parse(currentUrl.search.substring(1));

const debugFlagKey = `${FLAG_PREFIX}debug`;
const hasDebugFlag = currentParams[debugFlagKey] === "true";

if (hasDebugFlag) {
delete currentParams[debugFlagKey];
} else {
currentParams[debugFlagKey] = "true";
}

const newSearch = qs.stringify(currentParams);
const newUrl = `${window.location.pathname}${newSearch ? `?${newSearch}` : ""}`;
window.history.pushState({}, "", newUrl);

const event = new Event("popstate");
window.dispatchEvent(event);
};
6 changes: 4 additions & 2 deletions app/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import { useRouter } from "next/router";
import { useEffect } from "react";

import { SnackbarProvider } from "@/components/snackbar";
import "@/configurator/components/color-picker.css";
import { PUBLIC_URL } from "@/domain/env";
import { flag } from "@/flags/flag";
import { useDebugShortcut } from "@/flags/useFlag";
import { GraphqlProvider } from "@/graphql/GraphqlProvider";
import { i18n, parseLocaleString } from "@/locales/locales";
import { LocaleProvider } from "@/locales/use-locale";
Expand All @@ -24,8 +26,6 @@ import AsyncLocalizationProvider from "@/utils/l10n-provider";
import "@/utils/nprogress.css";
import { useNProgress } from "@/utils/use-nprogress";

import "@/configurator/components/color-picker.css";

const GQLDebugPanel = dynamic(() => import("@/gql-flamegraph/devtool"), {
ssr: false,
});
Expand Down Expand Up @@ -68,6 +68,8 @@ export default function App({
const shouldShowGQLDebug =
process.env.NODE_ENV === "development" || flag("debug");

useDebugShortcut(shouldShowGQLDebug);

return (
<>
<Head>
Expand Down
62 changes: 62 additions & 0 deletions app/utils/use-keyboard-shortcuts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useCallback, useEffect, useState } from "react";

type KeyCombo = {
keys: string[];
ctrlKey?: boolean;
metaKey?: boolean;
};

type KeyHandler = (event: KeyboardEvent) => void;

export const useKeyboardShortcut = (
keyCombo: KeyCombo,
handler: KeyHandler,
options: { preventDefault?: boolean } = {}
) => {
const [pressedKeys, setPressedKeys] = useState<string[]>([]);

const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
setPressedKeys((prev) => {
const key = event.key.toLowerCase();
if (!prev.includes(key)) {
return [...prev, key];
}
return prev;
});

const targetKeys = keyCombo.keys.map((k) => k.toLowerCase());
const currentKeys = [...pressedKeys, event.key.toLowerCase()];

const hasAllKeys = targetKeys.every((k) => currentKeys.includes(k));
const matchesCtrl =
keyCombo.ctrlKey === undefined || event.ctrlKey === keyCombo.ctrlKey;
const matchesMeta =
keyCombo.metaKey === undefined || event.metaKey === keyCombo.metaKey;

if (hasAllKeys && matchesCtrl && matchesMeta) {
if (options.preventDefault) {
event.preventDefault();
}
handler(event);
}
},
[keyCombo, handler, options.preventDefault, pressedKeys]
);

const handleKeyUp = useCallback((event: KeyboardEvent) => {
setPressedKeys((prev) => {
const key = event.key.toLowerCase();
return prev.filter((k) => k !== key);
});
}, []);

useEffect(() => {
window.addEventListener("keydown", handleKeyDown);
window.addEventListener("keyup", handleKeyUp);
return () => {
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener("keyup", handleKeyUp);
};
}, [handleKeyDown, handleKeyUp]);
};
Loading