mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: update theme logic
This commit is contained in:
parent
c389c6b440
commit
9134d704e5
2
packages/types/src/users.d.ts
vendored
2
packages/types/src/users.d.ts
vendored
@ -48,7 +48,7 @@ export type TUserProfile = {
|
|||||||
palette: string | undefined;
|
palette: string | undefined;
|
||||||
primary: string | undefined;
|
primary: string | undefined;
|
||||||
background: string | undefined;
|
background: string | undefined;
|
||||||
darkPalette: string | undefined;
|
darkPalette: boolean | undefined;
|
||||||
sidebarText: string | undefined;
|
sidebarText: string | undefined;
|
||||||
sidebarBackground: string | undefined;
|
sidebarBackground: string | undefined;
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import React, { FC, useEffect, useState } from "react";
|
import { FC, useEffect, useState } from "react";
|
||||||
import { Command } from "cmdk";
|
import { Command } from "cmdk";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
// icons
|
|
||||||
import { Settings } from "lucide-react";
|
import { Settings } from "lucide-react";
|
||||||
|
// ui
|
||||||
|
import { TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
import { THEME_OPTIONS } from "@/constants/themes";
|
import { THEME_OPTIONS } from "@/constants/themes";
|
||||||
|
// hooks
|
||||||
|
import { useUserProfile } from "@/hooks/store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
closePalette: () => void;
|
closePalette: () => void;
|
||||||
@ -15,18 +18,20 @@ export const CommandPaletteThemeActions: FC<Props> = observer((props) => {
|
|||||||
const { closePalette } = props;
|
const { closePalette } = props;
|
||||||
// states
|
// states
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
// hooks
|
// store hooks
|
||||||
|
const { updateCurrentUserTheme } = useUserProfile();
|
||||||
|
// next-themes
|
||||||
const { setTheme } = useTheme();
|
const { setTheme } = useTheme();
|
||||||
|
|
||||||
const updateUserTheme = async (newTheme: string) => {
|
const updateUserTheme = async (newTheme: string) => {
|
||||||
setTheme(newTheme);
|
setTheme(newTheme);
|
||||||
|
|
||||||
// return updateUserProfile({ theme: newTheme }).catch(() => {
|
return updateCurrentUserTheme(newTheme).catch(() => {
|
||||||
// setToast({
|
setToast({
|
||||||
// type: TOAST_TYPE.ERROR,
|
type: TOAST_TYPE.ERROR,
|
||||||
// title: "Failed to save user theme settings!",
|
title: "Failed to save user theme settings!",
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -26,7 +26,7 @@ const inputRules = {
|
|||||||
|
|
||||||
export const CustomThemeSelector: React.FC = observer(() => {
|
export const CustomThemeSelector: React.FC = observer(() => {
|
||||||
const {
|
const {
|
||||||
profile: { data: userProfile },
|
profile: { data: userProfile, updateUserProfile },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
|
|
||||||
const userTheme: any = userProfile?.theme;
|
const userTheme: any = userProfile?.theme;
|
||||||
@ -64,9 +64,9 @@ export const CustomThemeSelector: React.FC = observer(() => {
|
|||||||
|
|
||||||
setTheme("custom");
|
setTheme("custom");
|
||||||
|
|
||||||
console.log(payload);
|
return updateUserProfile({
|
||||||
|
theme: payload,
|
||||||
// return updateUserProfile({ theme: payload });
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleValueChange = (val: string | undefined, onChange: any) => {
|
const handleValueChange = (val: string | undefined, onChange: any) => {
|
||||||
|
@ -33,3 +33,4 @@ export * from "./use-instance";
|
|||||||
export * from "./use-app-theme";
|
export * from "./use-app-theme";
|
||||||
export * from "./use-command-palette";
|
export * from "./use-command-palette";
|
||||||
export * from "./use-app-router";
|
export * from "./use-app-router";
|
||||||
|
export * from "./use-app-theme";
|
||||||
|
@ -2,7 +2,7 @@ import { FC, ReactNode } from "react";
|
|||||||
// layout
|
// layout
|
||||||
import { SidebarHamburgerToggle } from "@/components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "@/components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
import { PreferencesMobileHeader } from "@/components/profile/preferences/preferences-mobile-header";
|
import { PreferencesMobileHeader } from "@/components/profile/preferences/preferences-mobile-header";
|
||||||
import { useApplication } from "@/hooks/store";
|
import { useAppTheme } from "@/hooks/store";
|
||||||
import { ProfileSettingsLayout } from "@/layouts/settings-layout";
|
import { ProfileSettingsLayout } from "@/layouts/settings-layout";
|
||||||
// local components
|
// local components
|
||||||
import { ProfilePreferenceSettingsSidebar } from "./sidebar";
|
import { ProfilePreferenceSettingsSidebar } from "./sidebar";
|
||||||
@ -14,13 +14,14 @@ interface IProfilePreferenceSettingsLayout {
|
|||||||
|
|
||||||
export const ProfilePreferenceSettingsLayout: FC<IProfilePreferenceSettingsLayout> = (props) => {
|
export const ProfilePreferenceSettingsLayout: FC<IProfilePreferenceSettingsLayout> = (props) => {
|
||||||
const { children, header } = props;
|
const { children, header } = props;
|
||||||
const { theme: themeStore } = useApplication();
|
// store hooks
|
||||||
|
const { toggleSidebar } = useAppTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProfileSettingsLayout
|
<ProfileSettingsLayout
|
||||||
header={
|
header={
|
||||||
<div className="md:hidden flex flex-shrink-0 gap-4 items-center justify-start border-b border-custom-border-200 p-4">
|
<div className="md:hidden flex flex-shrink-0 gap-4 items-center justify-start border-b border-custom-border-200 p-4">
|
||||||
<SidebarHamburgerToggle onClick={() => themeStore.toggleSidebar()} />
|
<SidebarHamburgerToggle onClick={toggleSidebar} />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -43,11 +43,19 @@ const StoreWrapper: FC<TStoreWrapper> = observer((props) => {
|
|||||||
if (!userProfile) return;
|
if (!userProfile) return;
|
||||||
if (window) setDom(window.document?.querySelector<HTMLElement>("[data-theme='custom']") || undefined);
|
if (window) setDom(window.document?.querySelector<HTMLElement>("[data-theme='custom']") || undefined);
|
||||||
|
|
||||||
setTheme(userProfile?.theme?.theme || "system");
|
const themeData = userProfile.theme;
|
||||||
if (userProfile?.theme?.theme === "custom" && userProfile?.theme?.palette && dom)
|
|
||||||
applyTheme(userProfile?.theme?.palette, false);
|
console.log("userProfile", userProfile.theme.theme);
|
||||||
else unsetCustomCssVariables();
|
|
||||||
}, [userProfile, setTheme, dom]);
|
setTheme(themeData?.theme || "system");
|
||||||
|
if (themeData?.theme === "custom" && themeData?.palette && dom) {
|
||||||
|
console.log("Setting...");
|
||||||
|
applyTheme(themeData?.palette, false);
|
||||||
|
} else {
|
||||||
|
console.log("Unsetting...");
|
||||||
|
unsetCustomCssVariables();
|
||||||
|
}
|
||||||
|
}, [dom, setTheme, userProfile]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!router.query) return;
|
if (!router.query) return;
|
||||||
|
@ -2,10 +2,7 @@ import { useEffect, useState, ReactElement } from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
// ui
|
// ui
|
||||||
import {
|
import { Spinner, setPromiseToast } from "@plane/ui";
|
||||||
Spinner,
|
|
||||||
// setPromiseToast
|
|
||||||
} from "@plane/ui";
|
|
||||||
// components
|
// components
|
||||||
import { CustomThemeSelector, ThemeSwitch, PageHead } from "@/components/core";
|
import { CustomThemeSelector, ThemeSwitch, PageHead } from "@/components/core";
|
||||||
// constants
|
// constants
|
||||||
@ -23,7 +20,7 @@ const ProfilePreferencesThemePage: NextPageWithLayout = observer(() => {
|
|||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const {
|
||||||
data: currentUser,
|
data: currentUser,
|
||||||
profile: { data: userProfile },
|
profile: { data: userProfile, updateCurrentUserTheme },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
// computed
|
// computed
|
||||||
const userTheme = userProfile?.theme;
|
const userTheme = userProfile?.theme;
|
||||||
@ -41,24 +38,24 @@ const ProfilePreferencesThemePage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
const handleThemeChange = (themeOption: I_THEME_OPTION) => {
|
const handleThemeChange = (themeOption: I_THEME_OPTION) => {
|
||||||
setTheme(themeOption.value);
|
setTheme(themeOption.value);
|
||||||
// const updateCurrentUserThemePromise = updateCurrentUserTheme(themeOption.value);
|
const updateCurrentUserThemePromise = updateCurrentUserTheme(themeOption.value);
|
||||||
|
|
||||||
// setPromiseToast(updateCurrentUserThemePromise, {
|
setPromiseToast(updateCurrentUserThemePromise, {
|
||||||
// loading: "Updating theme...",
|
loading: "Updating theme...",
|
||||||
// success: {
|
success: {
|
||||||
// title: "Success!",
|
title: "Success!",
|
||||||
// message: () => "Theme updated successfully!",
|
message: () => "Theme updated successfully!",
|
||||||
// },
|
},
|
||||||
// error: {
|
error: {
|
||||||
// title: "Error!",
|
title: "Error!",
|
||||||
// message: () => "Failed to Update the theme",
|
message: () => "Failed to Update the theme",
|
||||||
// },
|
},
|
||||||
// });
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHead title="Profile - Theme Prefrence" />
|
<PageHead title="Profile - Theme Preference" />
|
||||||
{currentUser ? (
|
{currentUser ? (
|
||||||
<div className="mx-auto mt-10 h-full w-full overflow-y-auto md:px-6 px-4 pb-8 md:mt-14 lg:px-20 vertical-scrollbar scrollbar-md">
|
<div className="mx-auto mt-10 h-full w-full overflow-y-auto md:px-6 px-4 pb-8 md:mt-14 lg:px-20 vertical-scrollbar scrollbar-md">
|
||||||
<div className="flex items-center border-b border-custom-border-100 pb-3.5">
|
<div className="flex items-center border-b border-custom-border-100 pb-3.5">
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
// mobx
|
|
||||||
import { action, observable, makeObservable } from "mobx";
|
import { action, observable, makeObservable } from "mobx";
|
||||||
// helper
|
|
||||||
import { applyTheme, unsetCustomCssVariables } from "@/helpers/theme.helper";
|
|
||||||
|
|
||||||
export interface IThemeStore {
|
export interface IThemeStore {
|
||||||
// observables
|
// observables
|
||||||
theme: string | null;
|
|
||||||
sidebarCollapsed: boolean | undefined;
|
sidebarCollapsed: boolean | undefined;
|
||||||
profileSidebarCollapsed: boolean | undefined;
|
profileSidebarCollapsed: boolean | undefined;
|
||||||
workspaceAnalyticsSidebarCollapsed: boolean | undefined;
|
workspaceAnalyticsSidebarCollapsed: boolean | undefined;
|
||||||
issueDetailSidebarCollapsed: boolean | undefined;
|
issueDetailSidebarCollapsed: boolean | undefined;
|
||||||
// actions
|
// actions
|
||||||
toggleSidebar: (collapsed?: boolean) => void;
|
toggleSidebar: (collapsed?: boolean) => void;
|
||||||
setTheme: (theme: any) => void;
|
|
||||||
toggleProfileSidebar: (collapsed?: boolean) => void;
|
toggleProfileSidebar: (collapsed?: boolean) => void;
|
||||||
toggleWorkspaceAnalyticsSidebar: (collapsed?: boolean) => void;
|
toggleWorkspaceAnalyticsSidebar: (collapsed?: boolean) => void;
|
||||||
toggleIssueDetailSidebar: (collapsed?: boolean) => void;
|
toggleIssueDetailSidebar: (collapsed?: boolean) => void;
|
||||||
@ -21,7 +16,6 @@ export interface IThemeStore {
|
|||||||
export class ThemeStore implements IThemeStore {
|
export class ThemeStore implements IThemeStore {
|
||||||
// observables
|
// observables
|
||||||
sidebarCollapsed: boolean | undefined = undefined;
|
sidebarCollapsed: boolean | undefined = undefined;
|
||||||
theme: string | null = null;
|
|
||||||
profileSidebarCollapsed: boolean | undefined = undefined;
|
profileSidebarCollapsed: boolean | undefined = undefined;
|
||||||
workspaceAnalyticsSidebarCollapsed: boolean | undefined = undefined;
|
workspaceAnalyticsSidebarCollapsed: boolean | undefined = undefined;
|
||||||
issueDetailSidebarCollapsed: boolean | undefined = undefined;
|
issueDetailSidebarCollapsed: boolean | undefined = undefined;
|
||||||
@ -32,13 +26,11 @@ export class ThemeStore implements IThemeStore {
|
|||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
// observable
|
// observable
|
||||||
sidebarCollapsed: observable.ref,
|
sidebarCollapsed: observable.ref,
|
||||||
theme: observable.ref,
|
|
||||||
profileSidebarCollapsed: observable.ref,
|
profileSidebarCollapsed: observable.ref,
|
||||||
workspaceAnalyticsSidebarCollapsed: observable.ref,
|
workspaceAnalyticsSidebarCollapsed: observable.ref,
|
||||||
issueDetailSidebarCollapsed: observable.ref,
|
issueDetailSidebarCollapsed: observable.ref,
|
||||||
// action
|
// action
|
||||||
toggleSidebar: action,
|
toggleSidebar: action,
|
||||||
setTheme: action,
|
|
||||||
toggleProfileSidebar: action,
|
toggleProfileSidebar: action,
|
||||||
toggleWorkspaceAnalyticsSidebar: action,
|
toggleWorkspaceAnalyticsSidebar: action,
|
||||||
toggleIssueDetailSidebar: action,
|
toggleIssueDetailSidebar: action,
|
||||||
@ -95,30 +87,4 @@ export class ThemeStore implements IThemeStore {
|
|||||||
}
|
}
|
||||||
localStorage.setItem("issue_detail_sidebar_collapsed", this.issueDetailSidebarCollapsed.toString());
|
localStorage.setItem("issue_detail_sidebar_collapsed", this.issueDetailSidebarCollapsed.toString());
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the user theme and applies it to the platform
|
|
||||||
* @param _theme
|
|
||||||
*/
|
|
||||||
setTheme = async (_theme: { theme: any }) => {
|
|
||||||
try {
|
|
||||||
const currentTheme: string = _theme?.theme?.theme?.toString();
|
|
||||||
// updating the local storage theme value
|
|
||||||
localStorage.setItem("theme", currentTheme);
|
|
||||||
// updating the mobx theme value
|
|
||||||
this.theme = currentTheme;
|
|
||||||
// applying the theme to platform if the selected theme is custom
|
|
||||||
if (currentTheme === "custom") {
|
|
||||||
const themeSettings = this.rootStore.user.currentUserSettings || null;
|
|
||||||
applyTheme(
|
|
||||||
themeSettings?.theme?.palette !== ",,,,"
|
|
||||||
? themeSettings?.theme?.palette
|
|
||||||
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
|
||||||
themeSettings?.theme?.darkPalette
|
|
||||||
);
|
|
||||||
} else unsetCustomCssVariables();
|
|
||||||
} catch (error) {
|
|
||||||
console.error("setting user theme error", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
import set from "lodash/set";
|
||||||
import { action, makeObservable, observable, runInAction } from "mobx";
|
import { action, makeObservable, observable, runInAction } from "mobx";
|
||||||
// services
|
|
||||||
import { UserService } from "services/user.service";
|
|
||||||
// types
|
// types
|
||||||
import { TUserProfile } from "@plane/types";
|
import { TUserProfile } from "@plane/types";
|
||||||
|
// services
|
||||||
|
import { UserService } from "@/services/user.service";
|
||||||
|
|
||||||
type TError = {
|
type TError = {
|
||||||
status: string;
|
status: string;
|
||||||
@ -19,6 +20,7 @@ export interface IProfileStore {
|
|||||||
updateUserProfile: (data: Partial<TUserProfile>) => Promise<void>;
|
updateUserProfile: (data: Partial<TUserProfile>) => Promise<void>;
|
||||||
updateUserOnBoard: () => Promise<void>;
|
updateUserOnBoard: () => Promise<void>;
|
||||||
updateTourCompleted: () => Promise<void>;
|
updateTourCompleted: () => Promise<void>;
|
||||||
|
updateCurrentUserTheme: (theme: string) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProfileStore implements IProfileStore {
|
export class ProfileStore implements IProfileStore {
|
||||||
@ -68,6 +70,7 @@ export class ProfileStore implements IProfileStore {
|
|||||||
updateUserProfile: action,
|
updateUserProfile: action,
|
||||||
updateUserOnBoard: action,
|
updateUserOnBoard: action,
|
||||||
updateTourCompleted: action,
|
updateTourCompleted: action,
|
||||||
|
updateCurrentUserTheme: action,
|
||||||
});
|
});
|
||||||
// services
|
// services
|
||||||
this.userService = new UserService();
|
this.userService = new UserService();
|
||||||
@ -164,4 +167,26 @@ export class ProfileStore implements IProfileStore {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the current user theme
|
||||||
|
* @param theme
|
||||||
|
* @returns Promise<void>
|
||||||
|
*/
|
||||||
|
updateCurrentUserTheme = async (theme: string) => {
|
||||||
|
const originalTheme = this.data?.theme?.theme;
|
||||||
|
try {
|
||||||
|
set(this.data, "theme.theme", theme);
|
||||||
|
const response = await this.updateUserProfile({
|
||||||
|
theme: {
|
||||||
|
...this.data?.theme,
|
||||||
|
theme,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
set(this.data, "theme.theme", originalTheme);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user