Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions apps/app/components/command-palette/change-interface-theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,59 @@ 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>>;
};

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"
Expand Down
2 changes: 2 additions & 0 deletions apps/app/components/core/theme/custom-theme-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const defaultValues: ICustomTheme = {
sidebarText: "#c5c5c5",
darkPalette: false,
palette: "",
theme: "custom",
};

export const CustomThemeSelector: React.FC<Props> = ({ preLoadedData }) => {
Expand Down Expand Up @@ -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
Expand Down
221 changes: 120 additions & 101 deletions apps/app/components/core/theme/theme-switch.tsx
Original file line number Diff line number Diff line change
@@ -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="h-full w-1/2 rounded-l-full"
style={{
background: icon.color1,
}}
/>
<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-r-full border-l"
style={{
borderColor: icon.border,
borderLeftColor: icon.border,
background: icon.color2,
}}
>
<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>
</CustomSelect.Option>
))}
</CustomSelect>
</>
{label}
</div>
</CustomSelect.Option>
))}
</CustomSelect>
);
};
6 changes: 3 additions & 3 deletions apps/app/components/workspace/sidebar-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -91,7 +91,7 @@ export const WorkspaceSidebarDropdown = () => {
.then(() => {
mutateUser(undefined);
router.push("/");
setTheme("dark");
setTheme("system");
})
.catch(() =>
setToastAlert({
Expand Down
10 changes: 10 additions & 0 deletions apps/app/constants/themes.ts
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Loading