diff --git a/frontend/src/components/account/ThemeSwitcher.tsx b/frontend/src/components/account/ThemeSwitcher.tsx
new file mode 100644
index 00000000..9bd77828
--- /dev/null
+++ b/frontend/src/components/account/ThemeSwitcher.tsx
@@ -0,0 +1,67 @@
+import {
+ Box,
+ Center,
+ ColorScheme,
+ SegmentedControl,
+ Stack,
+ useMantineColorScheme,
+} from "@mantine/core";
+import { useColorScheme } from "@mantine/hooks";
+import { useState } from "react";
+import { TbDeviceLaptop, TbMoon, TbSun } from "react-icons/tb";
+import usePreferences from "../../hooks/usePreferences";
+
+const ThemeSwitcher = () => {
+ const preferences = usePreferences();
+ const [colorScheme, setColorScheme] = useState(
+ preferences.get("colorScheme")
+ );
+ const { toggleColorScheme } = useMantineColorScheme();
+ const systemColorScheme = useColorScheme();
+
+ return (
+
+ {
+ preferences.set("colorScheme", value);
+ setColorScheme(value);
+ toggleColorScheme(
+ value == "system" ? systemColorScheme : (value as ColorScheme)
+ );
+ }}
+ data={[
+ {
+ label: (
+
+
+ Dark
+
+ ),
+ value: "dark",
+ },
+ {
+ label: (
+
+
+ Light
+
+ ),
+ value: "light",
+ },
+ {
+ label: (
+
+
+ System
+
+ ),
+ value: "system",
+ },
+ ]}
+ />
+
+ );
+};
+
+export default ThemeSwitcher;
diff --git a/frontend/src/hooks/usePreferences.ts b/frontend/src/hooks/usePreferences.ts
new file mode 100644
index 00000000..14d32215
--- /dev/null
+++ b/frontend/src/hooks/usePreferences.ts
@@ -0,0 +1,30 @@
+const defaultPreferences = [
+ {
+ key: "colorScheme",
+ value: "system",
+ },
+];
+
+const get = (key: string) => {
+ if (typeof window !== "undefined") {
+ const preferences = JSON.parse(localStorage.getItem("preferences") ?? "{}");
+ return (
+ preferences[key] ??
+ defaultPreferences.find((p) => p.key == key)?.value ??
+ null
+ );
+ }
+};
+
+const set = (key: string, value: string) => {
+ if (typeof window !== "undefined") {
+ const preferences = JSON.parse(localStorage.getItem("preferences") ?? "{}");
+ preferences[key] = value;
+ localStorage.setItem("preferences", JSON.stringify(preferences));
+ }
+};
+const usePreferences = () => {
+ return { get, set };
+};
+
+export default usePreferences;
diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx
index 6db2d396..93bbbe9d 100644
--- a/frontend/src/pages/_app.tsx
+++ b/frontend/src/pages/_app.tsx
@@ -1,5 +1,6 @@
import {
ColorScheme,
+ ColorSchemeProvider,
Container,
LoadingOverlay,
MantineProvider,
@@ -11,7 +12,8 @@ import type { AppProps } from "next/app";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import Header from "../components/navBar/NavBar";
-import useConfig, { ConfigContext } from "../hooks/config.hook";
+import { ConfigContext } from "../hooks/config.hook";
+import usePreferences from "../hooks/usePreferences";
import { UserContext } from "../hooks/user.hook";
import authService from "../services/auth.service";
import configService from "../services/config.service";
@@ -25,9 +27,9 @@ import { GlobalLoadingContext } from "../utils/loading.util";
function App({ Component, pageProps }: AppProps) {
const systemTheme = useColorScheme();
const router = useRouter();
- const config = useConfig();
+ const preferences = usePreferences();
- const [colorScheme, setColorScheme] = useState();
+ const [colorScheme, setColorScheme] = useState("light");
const [isLoading, setIsLoading] = useState(true);
const [user, setUser] = useState(null);
const [configVariables, setConfigVariables] = useState(null);
@@ -56,7 +58,11 @@ function App({ Component, pageProps }: AppProps) {
}, [router.asPath]);
useEffect(() => {
- setColorScheme(systemTheme);
+ setColorScheme(
+ preferences.get("colorScheme") == "system"
+ ? systemTheme
+ : preferences.get("colorScheme")
+ );
}, [systemTheme]);
return (
@@ -65,26 +71,31 @@ function App({ Component, pageProps }: AppProps) {
withNormalizeCSS
theme={{ colorScheme, ...globalStyle }}
>
-
-
-
-
- {isLoading ? (
-
- ) : (
-
-
-
-
-
-
-
- {" "}
-
- )}
-
-
-
+ setColorScheme(value ?? "light")}
+ >
+
+
+
+
+ {isLoading ? (
+
+ ) : (
+
+
+
+
+
+
+
+ {" "}
+
+ )}
+
+
+
+
);
}
diff --git a/frontend/src/pages/account/index.tsx b/frontend/src/pages/account/index.tsx
index 3fde8370..1248c31d 100644
--- a/frontend/src/pages/account/index.tsx
+++ b/frontend/src/pages/account/index.tsx
@@ -17,6 +17,7 @@ import { useRouter } from "next/router";
import { Tb2Fa } from "react-icons/tb";
import * as yup from "yup";
import showEnableTotpModal from "../../components/account/showEnableTotpModal";
+import ThemeSwitcher from "../../components/account/ThemeSwitcher";
import useUser from "../../hooks/user.hook";
import authService from "../../services/auth.service";
import userService from "../../services/user.service";
@@ -164,8 +165,6 @@ const Account = () => {
- {/* TODO: This is ugly, make it prettier */}
- {/* If we have totp enabled, show different text */}
{user.totpVerified ? (
<>
-
-
+
+
+ Color scheme
+
+
+
+