Skip to content

Commit

Permalink
feat(feature menu): add a hidden feature menu.
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcMcIntosh committed Jan 14, 2025
1 parent 821dd6b commit 8b04e47
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/components/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useAppSelector, useAppDispatch } from "../../hooks";
import { deleteChatById } from "../../features/History/historySlice";
import { push } from "../../features/Pages/pagesSlice";
import { restoreChat, type ChatThread } from "../../features/Chat/Thread";
import { FeatureMenu } from "../../features/Config/FeatureMenu";

export type SidebarProps = {
takingNotes: boolean;
Expand Down Expand Up @@ -43,6 +44,7 @@ export const Sidebar: React.FC<SidebarProps> = ({ takingNotes, style }) => {

return (
<Flex style={style}>
<FeatureMenu />
<Flex mt="4">
<Box position="absolute" ml="5" mt="2">
<Spinner loading={takingNotes} title="taking notes" />
Expand Down
102 changes: 102 additions & 0 deletions src/features/Config/FeatureMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React, { useState, useEffect, useCallback } from "react";
import { Dialog, Flex, Button, Checkbox, Text } from "@radix-ui/themes";
import { useAppDispatch, useAppSelector } from "../../hooks";
import { selectFeatures, changeFeature } from "./configSlice";

const useInputEvent = () => {
const [key, setKey] = useState<string | null>(null);
useEffect(() => {
const keyDownHandler = (event: KeyboardEvent) => setKey(event.code);
const keyUpHandler = () => setKey(null);
window.addEventListener("keydown", keyDownHandler);
window.addEventListener("keyup", keyUpHandler);
return () => {
window.removeEventListener("keydown", keyDownHandler);
window.removeEventListener("keyup", keyUpHandler);
};
}, []);

return key;
};

const konamiCode = [
"ArrowUp",
"ArrowUp",
"ArrowDown",
"ArrowDown",
"ArrowLeft",
"ArrowRight",
"ArrowLeft",
"ArrowRight",
"Escape",
"Enter",
];

const useKonamiCode = () => {
const [count, setCount] = useState(0);
const [success, setSuccess] = useState(false);
const key = useInputEvent();

useEffect(() => {
if (success) {
return;
} else if (count === konamiCode.length) {
setSuccess(true);
} else if (key === konamiCode[count]) {
setCount((n) => n + 1);
}
}, [key, count, success]);

const reset = useCallback(() => {
setSuccess(false);
setCount(0);
}, []);

return { success, reset };
};

export const FeatureMenu: React.FC = () => {
const { success, reset } = useKonamiCode();
const dispatch = useAppDispatch();
const features = useAppSelector(selectFeatures);

// if (!success) return false;

const keysAndValues = Object.entries(features ?? {});

return (
<Dialog.Root open={success} onOpenChange={reset}>
<Dialog.Content>
<Dialog.Title>Hidden Features Menu</Dialog.Title>
{keysAndValues.length === 0 && (
<Dialog.Description>No hidden features</Dialog.Description>
)}
<Flex direction="column" gap="3">
{keysAndValues.map(([feature, value]) => {
return (
<Text key={feature} as="label" size="2">
<Flex as="span" gap="2">
<Checkbox
checked={value}
onCheckedChange={() => {
dispatch(changeFeature({ feature, value: !value }));
}}
/>{" "}
{feature}
</Flex>
</Text>
);
})}
</Flex>

<Flex gap="3" mt="4" justify="end">
<Dialog.Close>
<Button variant="soft" color="gray">
Close
</Button>
</Dialog.Close>
</Flex>
</Dialog.Content>
</Dialog.Root>
);
};
12 changes: 12 additions & 0 deletions src/features/Config/configSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export const setThemeMode = createAction<"light" | "dark" | "inherit">(
);
export const setApiKey = createAction<string | null>("config/setApiKey");

export const changeFeature = createAction<{
feature: string;
value: boolean;
}>("config/feature/change");

export const reducer = createReducer<Config>(initialState, (builder) => {
// TODO: toggle darkmode for web host?
builder.addCase(updateConfig, (state, action) => {
Expand Down Expand Up @@ -76,6 +81,13 @@ export const reducer = createReducer<Config>(initialState, (builder) => {
builder.addCase(setApiKey, (state, action) => {
state.apiKey = action.payload;
});

builder.addCase(changeFeature, (state, action) => {
state.features = {
...(state.features ?? {}),
[action.payload.feature]: action.payload.value,
};
});
});

export const selectThemeMode = (state: RootState) =>
Expand Down

0 comments on commit 8b04e47

Please sign in to comment.