fix: custom theme persisting after signing out (#1780)

* fix: custom theme persistence

* chore: remove console logs

* fix: build error

* fix: change theme from command k
This commit is contained in:
Aaryan Khandelwal 2023-08-03 15:01:31 +05:30 committed by GitHub
parent 5aad6c71da
commit 97c3fb40e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 287 additions and 307 deletions

View File

@ -5,6 +5,8 @@ import { Command } from "cmdk";
import { THEMES_OBJ } from "constants/themes"; import { THEMES_OBJ } from "constants/themes";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import { SettingIcon } from "components/icons"; import { SettingIcon } from "components/icons";
import userService from "services/user.service";
import useUser from "hooks/use-user";
type Props = { type Props = {
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>; setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
@ -12,24 +14,50 @@ type Props = {
export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => { export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => {
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
const { setTheme } = useTheme(); const { setTheme } = useTheme();
const { user, mutateUser } = useUser();
const updateUserTheme = (newTheme: string) => {
if (!user) return;
setTheme(newTheme);
mutateUser((prevData) => {
if (!prevData) return prevData;
return {
...prevData,
theme: {
...prevData.theme,
theme: newTheme,
},
};
}, false);
userService.updateUser({
theme: {
...user.theme,
theme: newTheme,
},
});
};
// useEffect only runs on the client, so now we can safely show the UI // useEffect only runs on the client, so now we can safely show the UI
useEffect(() => { useEffect(() => {
setMounted(true); setMounted(true);
}, []); }, []);
if (!mounted) { if (!mounted) return null;
return null;
}
return ( return (
<> <>
{THEMES_OBJ.map((theme) => ( {THEMES_OBJ.filter((t) => t.value !== "custom").map((theme) => (
<Command.Item <Command.Item
key={theme.value} key={theme.value}
onSelect={() => { onSelect={() => {
setTheme(theme.value); updateUserTheme(theme.value);
setIsPaletteOpen(false); setIsPaletteOpen(false);
}} }}
className="focus:outline-none" className="focus:outline-none"

View File

@ -28,6 +28,7 @@ const defaultValues: ICustomTheme = {
sidebarText: "#c5c5c5", sidebarText: "#c5c5c5",
darkPalette: false, darkPalette: false,
palette: "", palette: "",
theme: "custom",
}; };
export const CustomThemeSelector: React.FC<Props> = ({ preLoadedData }) => { export const CustomThemeSelector: React.FC<Props> = ({ preLoadedData }) => {
@ -56,6 +57,7 @@ export const CustomThemeSelector: React.FC<Props> = ({ preLoadedData }) => {
sidebarText: formData.sidebarText, sidebarText: formData.sidebarText,
darkPalette: darkPalette, darkPalette: darkPalette,
palette: `${formData.background},${formData.text},${formData.primary},${formData.sidebarBackground},${formData.sidebarText}`, palette: `${formData.background},${formData.text},${formData.primary},${formData.sidebarBackground},${formData.sidebarText}`,
theme: "custom",
}; };
await userService await userService

View File

@ -1,142 +1,161 @@
import { useState, useEffect, Dispatch, SetStateAction } from "react"; import { useState, useEffect } from "react";
// next-themes
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
// services
import userService from "services/user.service";
// hooks
import useUser from "hooks/use-user";
// constants // constants
import { THEMES_OBJ } from "constants/themes"; import { THEMES_OBJ } from "constants/themes";
// ui // ui
import { CustomSelect } from "components/ui"; import { CustomSelect } from "components/ui";
// types // types
import { ICustomTheme, IUser } from "types"; import { ICustomTheme } from "types";
import { unsetCustomCssVariables } from "helpers/theme.helper";
type Props = { type Props = {
user: IUser | undefined; setPreLoadedData: React.Dispatch<React.SetStateAction<ICustomTheme | null>>;
setPreLoadedData: Dispatch<SetStateAction<ICustomTheme | null>>;
customThemeSelectorOptions: boolean; customThemeSelectorOptions: boolean;
setCustomThemeSelectorOptions: Dispatch<SetStateAction<boolean>>; setCustomThemeSelectorOptions: React.Dispatch<React.SetStateAction<boolean>>;
}; };
export const ThemeSwitch: React.FC<Props> = ({ export const ThemeSwitch: React.FC<Props> = ({
user,
setPreLoadedData, setPreLoadedData,
customThemeSelectorOptions, customThemeSelectorOptions,
setCustomThemeSelectorOptions, setCustomThemeSelectorOptions,
}) => { }) => {
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
const { theme, setTheme } = useTheme(); const { theme, setTheme } = useTheme();
const { user, mutateUser } = useUser();
const updateUserTheme = (newTheme: string) => {
if (!user) return;
setTheme(newTheme);
mutateUser((prevData) => {
if (!prevData) return prevData;
return {
...prevData,
theme: {
...prevData.theme,
theme: newTheme,
},
};
}, false);
userService.updateUser({
theme: {
...user.theme,
theme: newTheme,
},
});
};
// useEffect only runs on the client, so now we can safely show the UI // useEffect only runs on the client, so now we can safely show the UI
useEffect(() => { useEffect(() => {
setMounted(true); setMounted(true);
}, []); }, []);
if (!mounted) { if (!mounted) return null;
return null;
}
const currentThemeObj = THEMES_OBJ.find((t) => t.value === theme); const currentThemeObj = THEMES_OBJ.find((t) => t.value === theme);
return ( return (
<> <CustomSelect
<CustomSelect value={theme}
value={theme} label={
label={ currentThemeObj ? (
currentThemeObj ? ( <div className="flex items-center gap-2">
<div className="flex items-center gap-2"> <div
className="border-1 relative flex h-4 w-4 rotate-45 transform items-center justify-center rounded-full border"
style={{
borderColor: currentThemeObj.icon.border,
}}
>
<div <div
className="border-1 relative flex h-4 w-4 rotate-45 transform items-center justify-center rounded-full border" className="h-full w-1/2 rounded-l-full"
style={{ style={{
borderColor: currentThemeObj.icon.border, background: currentThemeObj.icon.color1,
}} }}
> />
<div <div
className="h-full w-1/2 rounded-l-full" className="h-full w-1/2 rounded-r-full border-l"
style={{ style={{
background: currentThemeObj.icon.color1, borderLeftColor: currentThemeObj.icon.border,
}} background: currentThemeObj.icon.color2,
/> }}
<div />
className="h-full w-1/2 rounded-r-full border-l"
style={{
borderLeftColor: currentThemeObj.icon.border,
background: currentThemeObj.icon.color2,
}}
/>
</div>
{currentThemeObj.label}
</div> </div>
) : ( {currentThemeObj.label}
"Select your theme" </div>
) ) : (
} "Select your theme"
onChange={({ value, type }: { value: string; type: string }) => { )
if (value === "custom") { }
if (user?.theme.palette) { onChange={({ value, type }: { value: string; type: string }) => {
setPreLoadedData({ if (value === "custom") {
background: user.theme.background !== "" ? user.theme.background : "#0d101b", if (user?.theme.palette) {
text: user.theme.text !== "" ? user.theme.text : "#c5c5c5", setPreLoadedData({
primary: user.theme.primary !== "" ? user.theme.primary : "#3f76ff", background: user.theme.background !== "" ? user.theme.background : "#0d101b",
sidebarBackground: text: user.theme.text !== "" ? user.theme.text : "#c5c5c5",
user.theme.sidebarBackground !== "" ? user.theme.sidebarBackground : "#0d101b", primary: user.theme.primary !== "" ? user.theme.primary : "#3f76ff",
sidebarText: user.theme.sidebarText !== "" ? user.theme.sidebarText : "#c5c5c5", sidebarBackground:
darkPalette: false, user.theme.sidebarBackground !== "" ? user.theme.sidebarBackground : "#0d101b",
palette: sidebarText: user.theme.sidebarText !== "" ? user.theme.sidebarText : "#c5c5c5",
user.theme.palette !== ",,,," darkPalette: false,
? user.theme.palette palette:
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5", user.theme.palette !== ",,,,"
}); ? user.theme.palette
} : "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
theme: "custom",
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true); });
} else {
if (customThemeSelectorOptions) setCustomThemeSelectorOptions(false);
for (let i = 10; i <= 900; i >= 100 ? (i += 100) : (i += 10)) {
document.documentElement.style.removeProperty(`--color-background-${i}`);
document.documentElement.style.removeProperty(`--color-text-${i}`);
document.documentElement.style.removeProperty(`--color-border-${i}`);
document.documentElement.style.removeProperty(`--color-primary-${i}`);
document.documentElement.style.removeProperty(`--color-sidebar-background-${i}`);
document.documentElement.style.removeProperty(`--color-sidebar-text-${i}`);
document.documentElement.style.removeProperty(`--color-sidebar-border-${i}`);
}
} }
setTheme(value); if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
document.documentElement.style.setProperty("color-scheme", type); } else {
}} if (customThemeSelectorOptions) setCustomThemeSelectorOptions(false);
input unsetCustomCssVariables();
width="w-full" }
position="right"
> updateUserTheme(value);
{THEMES_OBJ.map(({ value, label, type, icon }) => ( document.documentElement.style.setProperty("--color-scheme", type);
<CustomSelect.Option key={value} value={{ value, type }}> }}
<div className="flex items-center gap-2"> input
width="w-full"
position="right"
>
{THEMES_OBJ.map(({ value, label, type, icon }) => (
<CustomSelect.Option key={value} value={{ value, type }}>
<div className="flex items-center gap-2">
<div
className="border-1 relative flex h-4 w-4 rotate-45 transform items-center justify-center rounded-full border"
style={{
borderColor: icon.border,
}}
>
<div <div
className="border-1 relative flex h-4 w-4 rotate-45 transform items-center justify-center rounded-full border" className="h-full w-1/2 rounded-l-full"
style={{ style={{
borderColor: icon.border, background: icon.color1,
}} }}
> />
<div <div
className="h-full w-1/2 rounded-l-full" className="h-full w-1/2 rounded-r-full border-l"
style={{ style={{
background: icon.color1, borderLeftColor: icon.border,
}} background: icon.color2,
/> }}
<div />
className="h-full w-1/2 rounded-r-full border-l"
style={{
borderLeftColor: icon.border,
background: icon.color2,
}}
/>
</div>
{label}
</div> </div>
</CustomSelect.Option> {label}
))} </div>
</CustomSelect> </CustomSelect.Option>
</> ))}
</CustomSelect>
); );
}; };

View File

@ -3,10 +3,10 @@ import { Fragment } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Link from "next/link"; import Link from "next/link";
// next-themes
import { useTheme } from "next-themes";
// headless ui // headless ui
import { Menu, Transition } from "@headlessui/react"; import { Menu, Transition } from "@headlessui/react";
// next-themes
import { useTheme } from "next-themes";
// hooks // hooks
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
import useThemeHook from "hooks/use-theme"; import useThemeHook from "hooks/use-theme";
@ -91,7 +91,7 @@ export const WorkspaceSidebarDropdown = () => {
.then(() => { .then(() => {
mutateUser(undefined); mutateUser(undefined);
router.push("/"); router.push("/");
setTheme("dark"); setTheme("system");
}) })
.catch(() => .catch(() =>
setToastAlert({ setToastAlert({

View File

@ -1,6 +1,16 @@
export const THEMES = ["light", "dark", "light-contrast", "dark-contrast", "custom"]; export const THEMES = ["light", "dark", "light-contrast", "dark-contrast", "custom"];
export const THEMES_OBJ = [ export const THEMES_OBJ = [
{
value: "system",
label: "System Preference",
type: "light",
icon: {
border: "#DEE2E6",
color1: "#FAFAFA",
color2: "#3F76FF",
},
},
{ {
value: "light", value: "light",
label: "Light", label: "Light",

View File

@ -11,7 +11,7 @@ import projectService from "services/project.service";
// fetch-keys // fetch-keys
import { USER_PROJECT_VIEW } from "constants/fetch-keys"; import { USER_PROJECT_VIEW } from "constants/fetch-keys";
// helper // helper
import { applyTheme } from "helpers/theme.helper"; import { applyTheme, unsetCustomCssVariables } from "helpers/theme.helper";
// constants // constants
export const themeContext = createContext<ContextType>({} as ContextType); export const themeContext = createContext<ContextType>({} as ContextType);
@ -92,15 +92,18 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
useEffect(() => { useEffect(() => {
const theme = localStorage.getItem("theme"); const theme = localStorage.getItem("theme");
if (theme && theme === "custom") {
if (user && user.theme.palette) { if (theme) {
applyTheme( if (theme === "custom") {
user.theme.palette !== ",,,," if (user && user.theme.palette) {
? user.theme.palette applyTheme(
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5", user.theme.palette !== ",,,,"
user.theme.darkPalette ? user.theme.palette
); : "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
} user.theme.darkPalette
);
}
} else unsetCustomCssVariables();
} }
}, [user]); }, [user]);

View File

@ -60,6 +60,7 @@ const calculateShades = (hexValue: string): TShades => {
}; };
export const applyTheme = (palette: string, isDarkPalette: boolean) => { export const applyTheme = (palette: string, isDarkPalette: boolean) => {
const dom = document?.querySelector<HTMLElement>("[data-theme='custom']");
// palette: [bg, text, primary, sidebarBg, sidebarText] // palette: [bg, text, primary, sidebarBg, sidebarText]
const values: string[] = palette.split(","); const values: string[] = palette.split(",");
values.push(isDarkPalette ? "dark" : "light"); values.push(isDarkPalette ? "dark" : "light");
@ -79,41 +80,40 @@ export const applyTheme = (palette: string, isDarkPalette: boolean) => {
const sidebarBackgroundRgbValues = `${sidebarBackgroundShades[shade].r}, ${sidebarBackgroundShades[shade].g}, ${sidebarBackgroundShades[shade].b}`; const sidebarBackgroundRgbValues = `${sidebarBackgroundShades[shade].r}, ${sidebarBackgroundShades[shade].g}, ${sidebarBackgroundShades[shade].b}`;
const sidebarTextRgbValues = `${sidebarTextShades[shade].r}, ${sidebarTextShades[shade].g}, ${sidebarTextShades[shade].b}`; const sidebarTextRgbValues = `${sidebarTextShades[shade].r}, ${sidebarTextShades[shade].g}, ${sidebarTextShades[shade].b}`;
document dom?.style.setProperty(`--color-background-${shade}`, bgRgbValues);
.querySelector<HTMLElement>("[data-theme='custom']") dom?.style.setProperty(`--color-text-${shade}`, textRgbValues);
?.style.setProperty(`--color-background-${shade}`, bgRgbValues); dom?.style.setProperty(`--color-primary-${shade}`, primaryRgbValues);
document dom?.style.setProperty(`--color-sidebar-background-${shade}`, sidebarBackgroundRgbValues);
.querySelector<HTMLElement>("[data-theme='custom']") dom?.style.setProperty(`--color-sidebar-text-${shade}`, sidebarTextRgbValues);
?.style.setProperty(`--color-text-${shade}`, textRgbValues);
document
.querySelector<HTMLElement>("[data-theme='custom']")
?.style.setProperty(`--color-primary-${shade}`, primaryRgbValues);
document
.querySelector<HTMLElement>("[data-theme='custom']")
?.style.setProperty(`--color-sidebar-background-${shade}`, sidebarBackgroundRgbValues);
document
.querySelector<HTMLElement>("[data-theme='custom']")
?.style.setProperty(`--color-sidebar-text-${shade}`, sidebarTextRgbValues);
if (i >= 100 && i <= 400) { if (i >= 100 && i <= 400) {
const borderShade = i === 100 ? 70 : i === 200 ? 80 : i === 300 ? 90 : 100; const borderShade = i === 100 ? 70 : i === 200 ? 80 : i === 300 ? 90 : 100;
document dom?.style.setProperty(
.querySelector<HTMLElement>("[data-theme='custom']") `--color-border-${shade}`,
?.style.setProperty( `${bgShades[borderShade].r}, ${bgShades[borderShade].g}, ${bgShades[borderShade].b}`
`--color-border-${shade}`, );
`${bgShades[borderShade].r}, ${bgShades[borderShade].g}, ${bgShades[borderShade].b}` dom?.style.setProperty(
); `--color-sidebar-border-${shade}`,
document `${sidebarBackgroundShades[borderShade].r}, ${sidebarBackgroundShades[borderShade].g}, ${sidebarBackgroundShades[borderShade].b}`
.querySelector<HTMLElement>("[data-theme='custom']") );
?.style.setProperty(
`--color-sidebar-border-${shade}`,
`${sidebarBackgroundShades[borderShade].r}, ${sidebarBackgroundShades[borderShade].g}, ${sidebarBackgroundShades[borderShade].b}`
);
} }
} }
document dom?.style.setProperty("--color-scheme", values[5]);
.querySelector<HTMLElement>("[data-theme='custom']") };
?.style.setProperty("--color-scheme", values[5]);
export const unsetCustomCssVariables = () => {
for (let i = 10; i <= 900; i >= 100 ? (i += 100) : (i += 10)) {
const dom = document.querySelector<HTMLElement>("[data-theme='custom']");
dom?.style.removeProperty(`--color-background-${i}`);
dom?.style.removeProperty(`--color-text-${i}`);
dom?.style.removeProperty(`--color-border-${i}`);
dom?.style.removeProperty(`--color-primary-${i}`);
dom?.style.removeProperty(`--color-sidebar-background-${i}`);
dom?.style.removeProperty(`--color-sidebar-text-${i}`);
dom?.style.removeProperty(`--color-sidebar-border-${i}`);
dom?.style.removeProperty("--color-scheme");
}
}; };

View File

@ -1,6 +1,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useTheme } from "next-themes";
// next-themes
import { useTheme } from "next-themes";
// hooks // hooks
import useUserAuth from "hooks/use-user-auth"; import useUserAuth from "hooks/use-user-auth";
// layouts // layouts
@ -15,11 +16,13 @@ import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
import { ICustomTheme } from "types"; import { ICustomTheme } from "types";
const ProfilePreferences = () => { const ProfilePreferences = () => {
const { user: myProfile } = useUserAuth();
const { theme } = useTheme();
const [customThemeSelectorOptions, setCustomThemeSelectorOptions] = useState(false); const [customThemeSelectorOptions, setCustomThemeSelectorOptions] = useState(false);
const [preLoadedData, setPreLoadedData] = useState<ICustomTheme | null>(null); const [preLoadedData, setPreLoadedData] = useState<ICustomTheme | null>(null);
const { theme } = useTheme();
const { user: myProfile } = useUserAuth();
useEffect(() => { useEffect(() => {
if (theme === "custom") { if (theme === "custom") {
if (myProfile?.theme.palette) if (myProfile?.theme.palette)
@ -37,6 +40,7 @@ const ProfilePreferences = () => {
myProfile.theme.palette !== ",,,," myProfile.theme.palette !== ",,,,"
? myProfile.theme.palette ? myProfile.theme.palette
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5", : "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
theme: "custom",
}); });
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true); if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
} }
@ -71,7 +75,6 @@ const ProfilePreferences = () => {
</div> </div>
<div className="col-span-12 sm:col-span-6"> <div className="col-span-12 sm:col-span-6">
<ThemeSwitch <ThemeSwitch
user={myProfile}
setPreLoadedData={setPreLoadedData} setPreLoadedData={setPreLoadedData}
customThemeSelectorOptions={customThemeSelectorOptions} customThemeSelectorOptions={customThemeSelectorOptions}
setCustomThemeSelectorOptions={setCustomThemeSelectorOptions} setCustomThemeSelectorOptions={setCustomThemeSelectorOptions}

View File

@ -45,7 +45,7 @@ Router.events.on("routeChangeComplete", NProgress.done);
function MyApp({ Component, pageProps }: AppProps) { function MyApp({ Component, pageProps }: AppProps) {
return ( return (
// <UserProvider> // <UserProvider>
<ThemeProvider themes={THEMES} defaultTheme="dark"> <ThemeProvider themes={THEMES} defaultTheme="system">
<ToastContextProvider> <ToastContextProvider>
<ThemeContextProvider> <ThemeContextProvider>
<CrispWithNoSSR /> <CrispWithNoSSR />

View File

@ -1,143 +0,0 @@
import React from "react";
// layouts
import DefaultLayout from "layouts/default-layout";
import { UserAuthorizationLayout } from "layouts/auth-layout/user-authorization-wrapper";
// types
import type { NextPage } from "next";
const Colors: NextPage = () => (
<UserAuthorizationLayout>
<DefaultLayout>
<div className="space-y-8 p-8">
<div>
Primary:
<div className="flex flex-wrap">
<div className="h-12 w-12 bg-custom-primary-0" />
<div className="h-12 w-12 bg-custom-primary-10" />
<div className="h-12 w-12 bg-custom-primary-20" />
<div className="h-12 w-12 bg-custom-primary-30" />
<div className="h-12 w-12 bg-custom-primary-40" />
<div className="h-12 w-12 bg-custom-primary-50" />
<div className="h-12 w-12 bg-custom-primary-60" />
<div className="h-12 w-12 bg-custom-primary-70" />
<div className="h-12 w-12 bg-custom-primary-80" />
<div className="h-12 w-12 bg-custom-primary-90" />
<div className="h-12 w-12 bg-custom-primary-100" />
<div className="h-12 w-12 bg-custom-primary-200" />
<div className="h-12 w-12 bg-custom-primary-300" />
<div className="h-12 w-12 bg-custom-primary-400" />
<div className="h-12 w-12 bg-custom-primary-500" />
<div className="h-12 w-12 bg-custom-primary-600" />
<div className="h-12 w-12 bg-custom-primary-700" />
<div className="h-12 w-12 bg-custom-primary-800" />
<div className="h-12 w-12 bg-custom-primary-900" />
<div className="h-12 w-12 bg-custom-primary-1000" />
</div>
</div>
<div>
Background:
<div className="flex flex-wrap">
<div className="h-12 w-12 bg-custom-background-0" />
<div className="h-12 w-12 bg-custom-background-10" />
<div className="h-12 w-12 bg-custom-background-20" />
<div className="h-12 w-12 bg-custom-background-30" />
<div className="h-12 w-12 bg-custom-background-40" />
<div className="h-12 w-12 bg-custom-background-50" />
<div className="h-12 w-12 bg-custom-background-60" />
<div className="h-12 w-12 bg-custom-background-70" />
<div className="h-12 w-12 bg-custom-background-80" />
<div className="h-12 w-12 bg-custom-background-90" />
<div className="h-12 w-12 bg-custom-background-100" />
<div className="h-12 w-12 bg-custom-background-200" />
<div className="h-12 w-12 bg-custom-background-300" />
<div className="h-12 w-12 bg-custom-background-400" />
<div className="h-12 w-12 bg-custom-background-500" />
<div className="h-12 w-12 bg-custom-background-600" />
<div className="h-12 w-12 bg-custom-background-700" />
<div className="h-12 w-12 bg-custom-background-800" />
<div className="h-12 w-12 bg-custom-background-900" />
<div className="h-12 w-12 bg-custom-background-1000" />
</div>
</div>
<div>
Text:
<div className="flex flex-wrap">
<div className="h-12 w-12 bg-custom-text-0" />
<div className="h-12 w-12 bg-custom-text-10" />
<div className="h-12 w-12 bg-custom-text-20" />
<div className="h-12 w-12 bg-custom-text-30" />
<div className="h-12 w-12 bg-custom-text-40" />
<div className="h-12 w-12 bg-custom-text-50" />
<div className="h-12 w-12 bg-custom-text-60" />
<div className="h-12 w-12 bg-custom-text-70" />
<div className="h-12 w-12 bg-custom-text-80" />
<div className="h-12 w-12 bg-custom-text-90" />
<div className="h-12 w-12 bg-custom-text-100" />
<div className="h-12 w-12 bg-custom-text-200" />
<div className="h-12 w-12 bg-custom-text-300" />
<div className="h-12 w-12 bg-custom-text-400" />
<div className="h-12 w-12 bg-custom-text-500" />
<div className="h-12 w-12 bg-custom-text-600" />
<div className="h-12 w-12 bg-custom-text-700" />
<div className="h-12 w-12 bg-custom-text-800" />
<div className="h-12 w-12 bg-custom-text-900" />
<div className="h-12 w-12 bg-custom-text-1000" />
</div>
</div>
<div>
Sidebar Background:
<div className="flex flex-wrap">
<div className="h-12 w-12 bg-custom-sidebar-background-0" />
<div className="h-12 w-12 bg-custom-sidebar-background-10" />
<div className="h-12 w-12 bg-custom-sidebar-background-20" />
<div className="h-12 w-12 bg-custom-sidebar-background-30" />
<div className="h-12 w-12 bg-custom-sidebar-background-40" />
<div className="h-12 w-12 bg-custom-sidebar-background-50" />
<div className="h-12 w-12 bg-custom-sidebar-background-60" />
<div className="h-12 w-12 bg-custom-sidebar-background-70" />
<div className="h-12 w-12 bg-custom-sidebar-background-80" />
<div className="h-12 w-12 bg-custom-sidebar-background-90" />
<div className="h-12 w-12 bg-custom-sidebar-background-100" />
<div className="h-12 w-12 bg-custom-sidebar-background-200" />
<div className="h-12 w-12 bg-custom-sidebar-background-300" />
<div className="h-12 w-12 bg-custom-sidebar-background-400" />
<div className="h-12 w-12 bg-custom-sidebar-background-500" />
<div className="h-12 w-12 bg-custom-sidebar-background-600" />
<div className="h-12 w-12 bg-custom-sidebar-background-700" />
<div className="h-12 w-12 bg-custom-sidebar-background-800" />
<div className="h-12 w-12 bg-custom-sidebar-background-900" />
<div className="h-12 w-12 bg-custom-sidebar-background-1000" />
</div>
</div>
<div>
Sidebar Text:
<div className="flex flex-wrap">
<div className="h-12 w-12 bg-custom-sidebar-text-0" />
<div className="h-12 w-12 bg-custom-sidebar-text-10" />
<div className="h-12 w-12 bg-custom-sidebar-text-20" />
<div className="h-12 w-12 bg-custom-sidebar-text-30" />
<div className="h-12 w-12 bg-custom-sidebar-text-40" />
<div className="h-12 w-12 bg-custom-sidebar-text-50" />
<div className="h-12 w-12 bg-custom-sidebar-text-60" />
<div className="h-12 w-12 bg-custom-sidebar-text-70" />
<div className="h-12 w-12 bg-custom-sidebar-text-80" />
<div className="h-12 w-12 bg-custom-sidebar-text-90" />
<div className="h-12 w-12 bg-custom-sidebar-text-100" />
<div className="h-12 w-12 bg-custom-sidebar-text-200" />
<div className="h-12 w-12 bg-custom-sidebar-text-300" />
<div className="h-12 w-12 bg-custom-sidebar-text-400" />
<div className="h-12 w-12 bg-custom-sidebar-text-500" />
<div className="h-12 w-12 bg-custom-sidebar-text-600" />
<div className="h-12 w-12 bg-custom-sidebar-text-700" />
<div className="h-12 w-12 bg-custom-sidebar-text-800" />
<div className="h-12 w-12 bg-custom-sidebar-text-900" />
<div className="h-12 w-12 bg-custom-sidebar-text-1000" />
</div>
</div>
</div>
</DefaultLayout>
</UserAuthorizationLayout>
);
export default Colors;

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useEffect } from "react";
import Image from "next/image"; import Image from "next/image";
@ -22,6 +22,8 @@ import {
import { Spinner } from "components/ui"; import { Spinner } from "components/ui";
// images // images
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png"; import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
import { useTheme } from "next-themes";
import { ICurrentUserResponse, IUser } from "types";
// types // types
type EmailPasswordFormValues = { type EmailPasswordFormValues = {
email: string; email: string;
@ -34,6 +36,12 @@ const HomePage: NextPage = () => {
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const { setTheme } = useTheme();
const changeTheme = (user: IUser) => {
setTheme(user.theme.theme ?? "system");
};
const handleGoogleSignIn = async ({ clientId, credential }: any) => { const handleGoogleSignIn = async ({ clientId, credential }: any) => {
try { try {
if (clientId && credential) { if (clientId && credential) {
@ -43,7 +51,10 @@ const HomePage: NextPage = () => {
clientId, clientId,
}; };
const response = await authenticationService.socialAuth(socialAuthPayload); const response = await authenticationService.socialAuth(socialAuthPayload);
if (response && response?.user) mutateUser(); if (response && response?.user) {
mutateUser();
changeTheme(response.user);
}
} else { } else {
throw Error("Cant find credentials"); throw Error("Cant find credentials");
} }
@ -66,7 +77,10 @@ const HomePage: NextPage = () => {
clientId: process.env.NEXT_PUBLIC_GITHUB_ID, clientId: process.env.NEXT_PUBLIC_GITHUB_ID,
}; };
const response = await authenticationService.socialAuth(socialAuthPayload); const response = await authenticationService.socialAuth(socialAuthPayload);
if (response && response?.user) mutateUser(); if (response && response?.user) {
mutateUser();
changeTheme(response.user);
}
} else { } else {
throw Error("Cant find credentials"); throw Error("Cant find credentials");
} }
@ -85,7 +99,10 @@ const HomePage: NextPage = () => {
.emailLogin(formData) .emailLogin(formData)
.then((response) => { .then((response) => {
try { try {
if (response) mutateUser(); if (response) {
mutateUser();
changeTheme(response.user);
}
} catch (err: any) { } catch (err: any) {
setToastAlert({ setToastAlert({
type: "error", type: "error",
@ -109,7 +126,10 @@ const HomePage: NextPage = () => {
const handleEmailCodeSignIn = async (response: any) => { const handleEmailCodeSignIn = async (response: any) => {
try { try {
if (response) mutateUser(); if (response) {
mutateUser();
changeTheme(response.user);
}
} catch (err: any) { } catch (err: any) {
setToastAlert({ setToastAlert({
type: "error", type: "error",
@ -120,6 +140,10 @@ const HomePage: NextPage = () => {
} }
}; };
useEffect(() => {
setTheme("system");
}, [setTheme]);
return ( return (
<DefaultLayout> <DefaultLayout>
{isLoading ? ( {isLoading ? (

View File

@ -1,6 +1,9 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
// next imports
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// next-themes
import { useTheme } from "next-themes";
// layouts // layouts
import DefaultLayout from "layouts/default-layout"; import DefaultLayout from "layouts/default-layout";
// services // services
@ -17,11 +20,17 @@ const MagicSignIn: NextPage = () => {
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const { user, isLoading, mutateUser } = useUserAuth("sign-in"); const { setTheme } = useTheme();
const { mutateUser } = useUserAuth("sign-in");
const [isSigningIn, setIsSigningIn] = useState(false); const [isSigningIn, setIsSigningIn] = useState(false);
const [errorSigningIn, setErrorSignIn] = useState<string | undefined>(); const [errorSigningIn, setErrorSignIn] = useState<string | undefined>();
useEffect(() => {
setTheme("system");
}, [setTheme]);
useEffect(() => { useEffect(() => {
setIsSigningIn(() => false); setIsSigningIn(() => false);
setErrorSignIn(() => undefined); setErrorSignIn(() => undefined);

View File

@ -1,6 +1,5 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Router from "next/router";
import Image from "next/image"; import Image from "next/image";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
@ -32,7 +31,7 @@ import { CURRENT_USER, USER_WORKSPACE_INVITATIONS } from "constants/fetch-keys";
const Onboarding: NextPage = () => { const Onboarding: NextPage = () => {
const [step, setStep] = useState<number | null>(null); const [step, setStep] = useState<number | null>(null);
const { theme } = useTheme(); const { theme, setTheme } = useTheme();
const { user, isLoading: userLoading } = useUserAuth("onboarding"); const { user, isLoading: userLoading } = useUserAuth("onboarding");
@ -117,6 +116,10 @@ const Onboarding: NextPage = () => {
await userService.updateUserOnBoard({ userRole: user.role }, user); await userService.updateUserOnBoard({ userRole: user.role }, user);
}; };
useEffect(() => {
setTheme("system");
}, [setTheme]);
useEffect(() => { useEffect(() => {
const handleStepChange = async () => { const handleStepChange = async () => {
if (!user || !invitations) return; if (!user || !invitations) return;

View File

@ -3,6 +3,8 @@ import React, { useEffect, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Image from "next/image"; import Image from "next/image";
// next-themes
import { useTheme } from "next-themes";
// react-hook-form // react-hook-form
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
// hooks // hooks
@ -31,6 +33,8 @@ const ResetPasswordPage: NextPage = () => {
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const { setTheme } = useTheme();
const { const {
register, register,
handleSubmit, handleSubmit,
@ -76,6 +80,10 @@ const ResetPasswordPage: NextPage = () => {
); );
}; };
useEffect(() => {
setTheme("system");
}, [setTheme]);
useEffect(() => { useEffect(() => {
if (parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0")) router.push("/"); if (parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0")) router.push("/");
else setIsLoading(false); else setIsLoading(false);

View File

@ -3,6 +3,8 @@ import React, { useEffect, useState } from "react";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// next-themes
import { useTheme } from "next-themes";
// services // services
import authenticationService from "services/authentication.service"; import authenticationService from "services/authentication.service";
// hooks // hooks
@ -31,6 +33,8 @@ const SignUp: NextPage = () => {
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const { setTheme } = useTheme();
const { mutateUser } = useUserAuth("sign-in"); const { mutateUser } = useUserAuth("sign-in");
const handleSignUp = async (formData: EmailPasswordFormValues) => { const handleSignUp = async (formData: EmailPasswordFormValues) => {
@ -62,6 +66,10 @@ const SignUp: NextPage = () => {
); );
}; };
useEffect(() => {
setTheme("system");
}, [setTheme]);
useEffect(() => { useEffect(() => {
if (parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0")) router.push("/"); if (parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0")) router.push("/");
else setIsLoading(false); else setIsLoading(false);

View File

@ -1,5 +1,6 @@
// services // services
import APIService from "services/api.service"; import APIService from "services/api.service";
import { ICurrentUserResponse } from "types";
const { NEXT_PUBLIC_API_BASE_URL } = process.env; const { NEXT_PUBLIC_API_BASE_URL } = process.env;
@ -32,7 +33,11 @@ class AuthService extends APIService {
}); });
} }
async socialAuth(data: any) { async socialAuth(data: any): Promise<{
access_token: string;
refresh_toke: string;
user: ICurrentUserResponse;
}> {
return this.post("/api/social-auth/", data, { headers: {} }) return this.post("/api/social-auth/", data, { headers: {} })
.then((response) => { .then((response) => {
this.setAccessToken(response?.data?.access_token); this.setAccessToken(response?.data?.access_token);

View File

@ -46,6 +46,7 @@ export interface ICustomTheme {
sidebarText: string; sidebarText: string;
darkPalette: boolean; darkPalette: boolean;
palette: string; palette: string;
theme: string;
} }
export interface ICurrentUserResponse extends IUser { export interface ICurrentUserResponse extends IUser {