mirror of
https://github.com/stonith404/pingvin-share.git
synced 2025-01-29 01:28:59 -05:00
feat: manually switch color scheme
This commit is contained in:
parent
cabaee588b
commit
ef21bac59b
4 changed files with 140 additions and 28 deletions
67
frontend/src/components/account/ThemeSwitcher.tsx
Normal file
67
frontend/src/components/account/ThemeSwitcher.tsx
Normal file
|
@ -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 (
|
||||||
|
<Stack>
|
||||||
|
<SegmentedControl
|
||||||
|
value={colorScheme}
|
||||||
|
onChange={(value) => {
|
||||||
|
preferences.set("colorScheme", value);
|
||||||
|
setColorScheme(value);
|
||||||
|
toggleColorScheme(
|
||||||
|
value == "system" ? systemColorScheme : (value as ColorScheme)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
label: (
|
||||||
|
<Center>
|
||||||
|
<TbMoon size={16} />
|
||||||
|
<Box ml={10}>Dark</Box>
|
||||||
|
</Center>
|
||||||
|
),
|
||||||
|
value: "dark",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: (
|
||||||
|
<Center>
|
||||||
|
<TbSun size={16} />
|
||||||
|
<Box ml={10}>Light</Box>
|
||||||
|
</Center>
|
||||||
|
),
|
||||||
|
value: "light",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: (
|
||||||
|
<Center>
|
||||||
|
<TbDeviceLaptop size={16} />
|
||||||
|
<Box ml={10}>System</Box>
|
||||||
|
</Center>
|
||||||
|
),
|
||||||
|
value: "system",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeSwitcher;
|
30
frontend/src/hooks/usePreferences.ts
Normal file
30
frontend/src/hooks/usePreferences.ts
Normal file
|
@ -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;
|
|
@ -1,5 +1,6 @@
|
||||||
import {
|
import {
|
||||||
ColorScheme,
|
ColorScheme,
|
||||||
|
ColorSchemeProvider,
|
||||||
Container,
|
Container,
|
||||||
LoadingOverlay,
|
LoadingOverlay,
|
||||||
MantineProvider,
|
MantineProvider,
|
||||||
|
@ -11,7 +12,8 @@ import type { AppProps } from "next/app";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Header from "../components/navBar/NavBar";
|
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 { UserContext } from "../hooks/user.hook";
|
||||||
import authService from "../services/auth.service";
|
import authService from "../services/auth.service";
|
||||||
import configService from "../services/config.service";
|
import configService from "../services/config.service";
|
||||||
|
@ -25,9 +27,9 @@ import { GlobalLoadingContext } from "../utils/loading.util";
|
||||||
function App({ Component, pageProps }: AppProps) {
|
function App({ Component, pageProps }: AppProps) {
|
||||||
const systemTheme = useColorScheme();
|
const systemTheme = useColorScheme();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const config = useConfig();
|
const preferences = usePreferences();
|
||||||
|
|
||||||
const [colorScheme, setColorScheme] = useState<ColorScheme>();
|
const [colorScheme, setColorScheme] = useState<ColorScheme>("light");
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [user, setUser] = useState<CurrentUser | null>(null);
|
const [user, setUser] = useState<CurrentUser | null>(null);
|
||||||
const [configVariables, setConfigVariables] = useState<Config[] | null>(null);
|
const [configVariables, setConfigVariables] = useState<Config[] | null>(null);
|
||||||
|
@ -56,7 +58,11 @@ function App({ Component, pageProps }: AppProps) {
|
||||||
}, [router.asPath]);
|
}, [router.asPath]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setColorScheme(systemTheme);
|
setColorScheme(
|
||||||
|
preferences.get("colorScheme") == "system"
|
||||||
|
? systemTheme
|
||||||
|
: preferences.get("colorScheme")
|
||||||
|
);
|
||||||
}, [systemTheme]);
|
}, [systemTheme]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -64,6 +70,10 @@ function App({ Component, pageProps }: AppProps) {
|
||||||
withGlobalStyles
|
withGlobalStyles
|
||||||
withNormalizeCSS
|
withNormalizeCSS
|
||||||
theme={{ colorScheme, ...globalStyle }}
|
theme={{ colorScheme, ...globalStyle }}
|
||||||
|
>
|
||||||
|
<ColorSchemeProvider
|
||||||
|
colorScheme={colorScheme}
|
||||||
|
toggleColorScheme={(value) => setColorScheme(value ?? "light")}
|
||||||
>
|
>
|
||||||
<GlobalStyle />
|
<GlobalStyle />
|
||||||
<NotificationsProvider>
|
<NotificationsProvider>
|
||||||
|
@ -85,6 +95,7 @@ function App({ Component, pageProps }: AppProps) {
|
||||||
</GlobalLoadingContext.Provider>
|
</GlobalLoadingContext.Provider>
|
||||||
</ModalsProvider>
|
</ModalsProvider>
|
||||||
</NotificationsProvider>
|
</NotificationsProvider>
|
||||||
|
</ColorSchemeProvider>
|
||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { useRouter } from "next/router";
|
||||||
import { Tb2Fa } from "react-icons/tb";
|
import { Tb2Fa } from "react-icons/tb";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import showEnableTotpModal from "../../components/account/showEnableTotpModal";
|
import showEnableTotpModal from "../../components/account/showEnableTotpModal";
|
||||||
|
import ThemeSwitcher from "../../components/account/ThemeSwitcher";
|
||||||
import useUser from "../../hooks/user.hook";
|
import useUser from "../../hooks/user.hook";
|
||||||
import authService from "../../services/auth.service";
|
import authService from "../../services/auth.service";
|
||||||
import userService from "../../services/user.service";
|
import userService from "../../services/user.service";
|
||||||
|
@ -164,8 +165,6 @@ const Account = () => {
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
|
|
||||||
<Tabs.Panel value="totp" pt="xs">
|
<Tabs.Panel value="totp" pt="xs">
|
||||||
{/* TODO: This is ugly, make it prettier */}
|
|
||||||
{/* If we have totp enabled, show different text */}
|
|
||||||
{user.totpVerified ? (
|
{user.totpVerified ? (
|
||||||
<>
|
<>
|
||||||
<form
|
<form
|
||||||
|
@ -236,8 +235,13 @@ const Account = () => {
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
<Paper withBorder p="xl" mt="lg">
|
||||||
<Center mt={80}>
|
<Title order={5} mb="xs">
|
||||||
|
Color scheme
|
||||||
|
</Title>
|
||||||
|
<ThemeSwitcher />
|
||||||
|
</Paper>
|
||||||
|
<Center mt={80} mb="lg">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
|
|
Loading…
Add table
Reference in a new issue