mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
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:
parent
5aad6c71da
commit
97c3fb40e7
@ -5,6 +5,8 @@ import { Command } from "cmdk";
|
||||
import { THEMES_OBJ } from "constants/themes";
|
||||
import { useTheme } from "next-themes";
|
||||
import { SettingIcon } from "components/icons";
|
||||
import userService from "services/user.service";
|
||||
import useUser from "hooks/use-user";
|
||||
|
||||
type Props = {
|
||||
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
|
||||
@ -12,24 +14,50 @@ type Props = {
|
||||
|
||||
export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
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(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
if (!mounted) {
|
||||
return null;
|
||||
}
|
||||
if (!mounted) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{THEMES_OBJ.map((theme) => (
|
||||
{THEMES_OBJ.filter((t) => t.value !== "custom").map((theme) => (
|
||||
<Command.Item
|
||||
key={theme.value}
|
||||
onSelect={() => {
|
||||
setTheme(theme.value);
|
||||
updateUserTheme(theme.value);
|
||||
setIsPaletteOpen(false);
|
||||
}}
|
||||
className="focus:outline-none"
|
||||
|
@ -28,6 +28,7 @@ const defaultValues: ICustomTheme = {
|
||||
sidebarText: "#c5c5c5",
|
||||
darkPalette: false,
|
||||
palette: "",
|
||||
theme: "custom",
|
||||
};
|
||||
|
||||
export const CustomThemeSelector: React.FC<Props> = ({ preLoadedData }) => {
|
||||
@ -56,6 +57,7 @@ export const CustomThemeSelector: React.FC<Props> = ({ preLoadedData }) => {
|
||||
sidebarText: formData.sidebarText,
|
||||
darkPalette: darkPalette,
|
||||
palette: `${formData.background},${formData.text},${formData.primary},${formData.sidebarBackground},${formData.sidebarText}`,
|
||||
theme: "custom",
|
||||
};
|
||||
|
||||
await userService
|
||||
|
@ -1,142 +1,161 @@
|
||||
import { useState, useEffect, Dispatch, SetStateAction } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
// next-themes
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
// services
|
||||
import userService from "services/user.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
// constants
|
||||
import { THEMES_OBJ } from "constants/themes";
|
||||
// ui
|
||||
import { CustomSelect } from "components/ui";
|
||||
// types
|
||||
import { ICustomTheme, IUser } from "types";
|
||||
import { ICustomTheme } from "types";
|
||||
import { unsetCustomCssVariables } from "helpers/theme.helper";
|
||||
|
||||
type Props = {
|
||||
user: IUser | undefined;
|
||||
setPreLoadedData: Dispatch<SetStateAction<ICustomTheme | null>>;
|
||||
setPreLoadedData: React.Dispatch<React.SetStateAction<ICustomTheme | null>>;
|
||||
customThemeSelectorOptions: boolean;
|
||||
setCustomThemeSelectorOptions: Dispatch<SetStateAction<boolean>>;
|
||||
setCustomThemeSelectorOptions: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
};
|
||||
|
||||
export const ThemeSwitch: React.FC<Props> = ({
|
||||
user,
|
||||
setPreLoadedData,
|
||||
customThemeSelectorOptions,
|
||||
setCustomThemeSelectorOptions,
|
||||
}) => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
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(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
if (!mounted) {
|
||||
return null;
|
||||
}
|
||||
if (!mounted) return null;
|
||||
|
||||
const currentThemeObj = THEMES_OBJ.find((t) => t.value === theme);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CustomSelect
|
||||
value={theme}
|
||||
label={
|
||||
currentThemeObj ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<CustomSelect
|
||||
value={theme}
|
||||
label={
|
||||
currentThemeObj ? (
|
||||
<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
|
||||
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={{
|
||||
borderColor: currentThemeObj.icon.border,
|
||||
background: currentThemeObj.icon.color1,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="h-full w-1/2 rounded-l-full"
|
||||
style={{
|
||||
background: currentThemeObj.icon.color1,
|
||||
}}
|
||||
/>
|
||||
<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
|
||||
className="h-full w-1/2 rounded-r-full border-l"
|
||||
style={{
|
||||
borderLeftColor: currentThemeObj.icon.border,
|
||||
background: currentThemeObj.icon.color2,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
"Select your theme"
|
||||
)
|
||||
}
|
||||
onChange={({ value, type }: { value: string; type: string }) => {
|
||||
if (value === "custom") {
|
||||
if (user?.theme.palette) {
|
||||
setPreLoadedData({
|
||||
background: user.theme.background !== "" ? user.theme.background : "#0d101b",
|
||||
text: user.theme.text !== "" ? user.theme.text : "#c5c5c5",
|
||||
primary: user.theme.primary !== "" ? user.theme.primary : "#3f76ff",
|
||||
sidebarBackground:
|
||||
user.theme.sidebarBackground !== "" ? user.theme.sidebarBackground : "#0d101b",
|
||||
sidebarText: user.theme.sidebarText !== "" ? user.theme.sidebarText : "#c5c5c5",
|
||||
darkPalette: false,
|
||||
palette:
|
||||
user.theme.palette !== ",,,,"
|
||||
? user.theme.palette
|
||||
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
||||
});
|
||||
}
|
||||
|
||||
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}`);
|
||||
}
|
||||
{currentThemeObj.label}
|
||||
</div>
|
||||
) : (
|
||||
"Select your theme"
|
||||
)
|
||||
}
|
||||
onChange={({ value, type }: { value: string; type: string }) => {
|
||||
if (value === "custom") {
|
||||
if (user?.theme.palette) {
|
||||
setPreLoadedData({
|
||||
background: user.theme.background !== "" ? user.theme.background : "#0d101b",
|
||||
text: user.theme.text !== "" ? user.theme.text : "#c5c5c5",
|
||||
primary: user.theme.primary !== "" ? user.theme.primary : "#3f76ff",
|
||||
sidebarBackground:
|
||||
user.theme.sidebarBackground !== "" ? user.theme.sidebarBackground : "#0d101b",
|
||||
sidebarText: user.theme.sidebarText !== "" ? user.theme.sidebarText : "#c5c5c5",
|
||||
darkPalette: false,
|
||||
palette:
|
||||
user.theme.palette !== ",,,,"
|
||||
? user.theme.palette
|
||||
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
||||
theme: "custom",
|
||||
});
|
||||
}
|
||||
|
||||
setTheme(value);
|
||||
document.documentElement.style.setProperty("color-scheme", type);
|
||||
}}
|
||||
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">
|
||||
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
|
||||
} else {
|
||||
if (customThemeSelectorOptions) setCustomThemeSelectorOptions(false);
|
||||
unsetCustomCssVariables();
|
||||
}
|
||||
|
||||
updateUserTheme(value);
|
||||
document.documentElement.style.setProperty("--color-scheme", type);
|
||||
}}
|
||||
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
|
||||
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={{
|
||||
borderColor: icon.border,
|
||||
background: icon.color1,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="h-full w-1/2 rounded-l-full"
|
||||
style={{
|
||||
background: icon.color1,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="h-full w-1/2 rounded-r-full border-l"
|
||||
style={{
|
||||
borderLeftColor: icon.border,
|
||||
background: icon.color2,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{label}
|
||||
/>
|
||||
<div
|
||||
className="h-full w-1/2 rounded-r-full border-l"
|
||||
style={{
|
||||
borderLeftColor: icon.border,
|
||||
background: icon.color2,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</CustomSelect.Option>
|
||||
))}
|
||||
</CustomSelect>
|
||||
</>
|
||||
{label}
|
||||
</div>
|
||||
</CustomSelect.Option>
|
||||
))}
|
||||
</CustomSelect>
|
||||
);
|
||||
};
|
||||
|
@ -3,10 +3,10 @@ import { Fragment } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Link from "next/link";
|
||||
|
||||
// next-themes
|
||||
import { useTheme } from "next-themes";
|
||||
// headless ui
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
// next-themes
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import useThemeHook from "hooks/use-theme";
|
||||
@ -91,7 +91,7 @@ export const WorkspaceSidebarDropdown = () => {
|
||||
.then(() => {
|
||||
mutateUser(undefined);
|
||||
router.push("/");
|
||||
setTheme("dark");
|
||||
setTheme("system");
|
||||
})
|
||||
.catch(() =>
|
||||
setToastAlert({
|
||||
|
@ -1,6 +1,16 @@
|
||||
export const THEMES = ["light", "dark", "light-contrast", "dark-contrast", "custom"];
|
||||
|
||||
export const THEMES_OBJ = [
|
||||
{
|
||||
value: "system",
|
||||
label: "System Preference",
|
||||
type: "light",
|
||||
icon: {
|
||||
border: "#DEE2E6",
|
||||
color1: "#FAFAFA",
|
||||
color2: "#3F76FF",
|
||||
},
|
||||
},
|
||||
{
|
||||
value: "light",
|
||||
label: "Light",
|
||||
|
@ -11,7 +11,7 @@ import projectService from "services/project.service";
|
||||
// fetch-keys
|
||||
import { USER_PROJECT_VIEW } from "constants/fetch-keys";
|
||||
// helper
|
||||
import { applyTheme } from "helpers/theme.helper";
|
||||
import { applyTheme, unsetCustomCssVariables } from "helpers/theme.helper";
|
||||
// constants
|
||||
|
||||
export const themeContext = createContext<ContextType>({} as ContextType);
|
||||
@ -92,15 +92,18 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
|
||||
useEffect(() => {
|
||||
const theme = localStorage.getItem("theme");
|
||||
if (theme && theme === "custom") {
|
||||
if (user && user.theme.palette) {
|
||||
applyTheme(
|
||||
user.theme.palette !== ",,,,"
|
||||
? user.theme.palette
|
||||
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
||||
user.theme.darkPalette
|
||||
);
|
||||
}
|
||||
|
||||
if (theme) {
|
||||
if (theme === "custom") {
|
||||
if (user && user.theme.palette) {
|
||||
applyTheme(
|
||||
user.theme.palette !== ",,,,"
|
||||
? user.theme.palette
|
||||
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
||||
user.theme.darkPalette
|
||||
);
|
||||
}
|
||||
} else unsetCustomCssVariables();
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
|
@ -60,6 +60,7 @@ const calculateShades = (hexValue: string): TShades => {
|
||||
};
|
||||
|
||||
export const applyTheme = (palette: string, isDarkPalette: boolean) => {
|
||||
const dom = document?.querySelector<HTMLElement>("[data-theme='custom']");
|
||||
// palette: [bg, text, primary, sidebarBg, sidebarText]
|
||||
const values: string[] = palette.split(",");
|
||||
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 sidebarTextRgbValues = `${sidebarTextShades[shade].r}, ${sidebarTextShades[shade].g}, ${sidebarTextShades[shade].b}`;
|
||||
|
||||
document
|
||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
||||
?.style.setProperty(`--color-background-${shade}`, bgRgbValues);
|
||||
document
|
||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
||||
?.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);
|
||||
dom?.style.setProperty(`--color-background-${shade}`, bgRgbValues);
|
||||
dom?.style.setProperty(`--color-text-${shade}`, textRgbValues);
|
||||
dom?.style.setProperty(`--color-primary-${shade}`, primaryRgbValues);
|
||||
dom?.style.setProperty(`--color-sidebar-background-${shade}`, sidebarBackgroundRgbValues);
|
||||
dom?.style.setProperty(`--color-sidebar-text-${shade}`, sidebarTextRgbValues);
|
||||
|
||||
if (i >= 100 && i <= 400) {
|
||||
const borderShade = i === 100 ? 70 : i === 200 ? 80 : i === 300 ? 90 : 100;
|
||||
|
||||
document
|
||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
||||
?.style.setProperty(
|
||||
`--color-border-${shade}`,
|
||||
`${bgShades[borderShade].r}, ${bgShades[borderShade].g}, ${bgShades[borderShade].b}`
|
||||
);
|
||||
document
|
||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
||||
?.style.setProperty(
|
||||
`--color-sidebar-border-${shade}`,
|
||||
`${sidebarBackgroundShades[borderShade].r}, ${sidebarBackgroundShades[borderShade].g}, ${sidebarBackgroundShades[borderShade].b}`
|
||||
);
|
||||
dom?.style.setProperty(
|
||||
`--color-border-${shade}`,
|
||||
`${bgShades[borderShade].r}, ${bgShades[borderShade].g}, ${bgShades[borderShade].b}`
|
||||
);
|
||||
dom?.style.setProperty(
|
||||
`--color-sidebar-border-${shade}`,
|
||||
`${sidebarBackgroundShades[borderShade].r}, ${sidebarBackgroundShades[borderShade].g}, ${sidebarBackgroundShades[borderShade].b}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
document
|
||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
||||
?.style.setProperty("--color-scheme", values[5]);
|
||||
dom?.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");
|
||||
}
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
// next-themes
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
// layouts
|
||||
@ -15,11 +16,13 @@ import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
import { ICustomTheme } from "types";
|
||||
|
||||
const ProfilePreferences = () => {
|
||||
const { user: myProfile } = useUserAuth();
|
||||
const { theme } = useTheme();
|
||||
const [customThemeSelectorOptions, setCustomThemeSelectorOptions] = useState(false);
|
||||
const [preLoadedData, setPreLoadedData] = useState<ICustomTheme | null>(null);
|
||||
|
||||
const { theme } = useTheme();
|
||||
|
||||
const { user: myProfile } = useUserAuth();
|
||||
|
||||
useEffect(() => {
|
||||
if (theme === "custom") {
|
||||
if (myProfile?.theme.palette)
|
||||
@ -37,6 +40,7 @@ const ProfilePreferences = () => {
|
||||
myProfile.theme.palette !== ",,,,"
|
||||
? myProfile.theme.palette
|
||||
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
||||
theme: "custom",
|
||||
});
|
||||
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
|
||||
}
|
||||
@ -71,7 +75,6 @@ const ProfilePreferences = () => {
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<ThemeSwitch
|
||||
user={myProfile}
|
||||
setPreLoadedData={setPreLoadedData}
|
||||
customThemeSelectorOptions={customThemeSelectorOptions}
|
||||
setCustomThemeSelectorOptions={setCustomThemeSelectorOptions}
|
||||
|
@ -45,7 +45,7 @@ Router.events.on("routeChangeComplete", NProgress.done);
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
// <UserProvider>
|
||||
<ThemeProvider themes={THEMES} defaultTheme="dark">
|
||||
<ThemeProvider themes={THEMES} defaultTheme="system">
|
||||
<ToastContextProvider>
|
||||
<ThemeContextProvider>
|
||||
<CrispWithNoSSR />
|
||||
|
@ -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;
|
@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
import Image from "next/image";
|
||||
|
||||
@ -22,6 +22,8 @@ import {
|
||||
import { Spinner } from "components/ui";
|
||||
// images
|
||||
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
||||
import { useTheme } from "next-themes";
|
||||
import { ICurrentUserResponse, IUser } from "types";
|
||||
// types
|
||||
type EmailPasswordFormValues = {
|
||||
email: string;
|
||||
@ -34,6 +36,12 @@ const HomePage: NextPage = () => {
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
const changeTheme = (user: IUser) => {
|
||||
setTheme(user.theme.theme ?? "system");
|
||||
};
|
||||
|
||||
const handleGoogleSignIn = async ({ clientId, credential }: any) => {
|
||||
try {
|
||||
if (clientId && credential) {
|
||||
@ -43,7 +51,10 @@ const HomePage: NextPage = () => {
|
||||
clientId,
|
||||
};
|
||||
const response = await authenticationService.socialAuth(socialAuthPayload);
|
||||
if (response && response?.user) mutateUser();
|
||||
if (response && response?.user) {
|
||||
mutateUser();
|
||||
changeTheme(response.user);
|
||||
}
|
||||
} else {
|
||||
throw Error("Cant find credentials");
|
||||
}
|
||||
@ -66,7 +77,10 @@ const HomePage: NextPage = () => {
|
||||
clientId: process.env.NEXT_PUBLIC_GITHUB_ID,
|
||||
};
|
||||
const response = await authenticationService.socialAuth(socialAuthPayload);
|
||||
if (response && response?.user) mutateUser();
|
||||
if (response && response?.user) {
|
||||
mutateUser();
|
||||
changeTheme(response.user);
|
||||
}
|
||||
} else {
|
||||
throw Error("Cant find credentials");
|
||||
}
|
||||
@ -85,7 +99,10 @@ const HomePage: NextPage = () => {
|
||||
.emailLogin(formData)
|
||||
.then((response) => {
|
||||
try {
|
||||
if (response) mutateUser();
|
||||
if (response) {
|
||||
mutateUser();
|
||||
changeTheme(response.user);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
@ -109,7 +126,10 @@ const HomePage: NextPage = () => {
|
||||
|
||||
const handleEmailCodeSignIn = async (response: any) => {
|
||||
try {
|
||||
if (response) mutateUser();
|
||||
if (response) {
|
||||
mutateUser();
|
||||
changeTheme(response.user);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
@ -120,6 +140,10 @@ const HomePage: NextPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setTheme("system");
|
||||
}, [setTheme]);
|
||||
|
||||
return (
|
||||
<DefaultLayout>
|
||||
{isLoading ? (
|
||||
|
@ -1,6 +1,9 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
// next imports
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
// next-themes
|
||||
import { useTheme } from "next-themes";
|
||||
// layouts
|
||||
import DefaultLayout from "layouts/default-layout";
|
||||
// services
|
||||
@ -17,11 +20,17 @@ const MagicSignIn: NextPage = () => {
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { user, isLoading, mutateUser } = useUserAuth("sign-in");
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
const { mutateUser } = useUserAuth("sign-in");
|
||||
|
||||
const [isSigningIn, setIsSigningIn] = useState(false);
|
||||
const [errorSigningIn, setErrorSignIn] = useState<string | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
setTheme("system");
|
||||
}, [setTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsSigningIn(() => false);
|
||||
setErrorSignIn(() => undefined);
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import Router from "next/router";
|
||||
import Image from "next/image";
|
||||
|
||||
import useSWR, { mutate } from "swr";
|
||||
@ -32,7 +31,7 @@ import { CURRENT_USER, USER_WORKSPACE_INVITATIONS } from "constants/fetch-keys";
|
||||
const Onboarding: NextPage = () => {
|
||||
const [step, setStep] = useState<number | null>(null);
|
||||
|
||||
const { theme } = useTheme();
|
||||
const { theme, setTheme } = useTheme();
|
||||
|
||||
const { user, isLoading: userLoading } = useUserAuth("onboarding");
|
||||
|
||||
@ -117,6 +116,10 @@ const Onboarding: NextPage = () => {
|
||||
await userService.updateUserOnBoard({ userRole: user.role }, user);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setTheme("system");
|
||||
}, [setTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleStepChange = async () => {
|
||||
if (!user || !invitations) return;
|
||||
|
@ -3,6 +3,8 @@ import React, { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Image from "next/image";
|
||||
|
||||
// next-themes
|
||||
import { useTheme } from "next-themes";
|
||||
// react-hook-form
|
||||
import { useForm } from "react-hook-form";
|
||||
// hooks
|
||||
@ -31,6 +33,8 @@ const ResetPasswordPage: NextPage = () => {
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
@ -76,6 +80,10 @@ const ResetPasswordPage: NextPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setTheme("system");
|
||||
}, [setTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
if (parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0")) router.push("/");
|
||||
else setIsLoading(false);
|
||||
|
@ -3,6 +3,8 @@ import React, { useEffect, useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
// next-themes
|
||||
import { useTheme } from "next-themes";
|
||||
// services
|
||||
import authenticationService from "services/authentication.service";
|
||||
// hooks
|
||||
@ -31,6 +33,8 @@ const SignUp: NextPage = () => {
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
const { mutateUser } = useUserAuth("sign-in");
|
||||
|
||||
const handleSignUp = async (formData: EmailPasswordFormValues) => {
|
||||
@ -62,6 +66,10 @@ const SignUp: NextPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setTheme("system");
|
||||
}, [setTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
if (parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0")) router.push("/");
|
||||
else setIsLoading(false);
|
||||
|
@ -1,5 +1,6 @@
|
||||
// services
|
||||
import APIService from "services/api.service";
|
||||
import { ICurrentUserResponse } from "types";
|
||||
|
||||
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: {} })
|
||||
.then((response) => {
|
||||
this.setAccessToken(response?.data?.access_token);
|
||||
|
1
apps/app/types/users.d.ts
vendored
1
apps/app/types/users.d.ts
vendored
@ -46,6 +46,7 @@ export interface ICustomTheme {
|
||||
sidebarText: string;
|
||||
darkPalette: boolean;
|
||||
palette: string;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
export interface ICurrentUserResponse extends IUser {
|
||||
|
Loading…
Reference in New Issue
Block a user