diff --git a/apps/app/components/core/theme/custom-theme-selector.tsx b/apps/app/components/core/theme/custom-theme-selector.tsx index 9db7b1dd8..3f6752e99 100644 --- a/apps/app/components/core/theme/custom-theme-selector.tsx +++ b/apps/app/components/core/theme/custom-theme-selector.tsx @@ -4,17 +4,13 @@ import { useTheme } from "next-themes"; import { useForm } from "react-hook-form"; -// hooks -import useUser from "hooks/use-user"; // ui import { PrimaryButton } from "components/ui"; import { ColorPickerInput } from "components/core"; -// services -import userService from "services/user.service"; -// helper -import { applyTheme } from "helpers/theme.helper"; // types import { ICustomTheme } from "types"; +// mobx react lite +import { observer } from "mobx-react-lite"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; @@ -33,11 +29,11 @@ const defaultValues: ICustomTheme = { theme: "custom", }; -export const CustomThemeSelector: React.FC = ({ preLoadedData }) => { +export const CustomThemeSelector: React.FC = observer(({ preLoadedData }) => { const store: any = useMobxStore(); + const { setTheme } = useTheme(); const [darkPalette, setDarkPalette] = useState(false); - const { register, formState: { errors, isSubmitting }, @@ -48,11 +44,14 @@ export const CustomThemeSelector: React.FC = ({ preLoadedData }) => { } = useForm({ defaultValues, }); + useEffect(() => { + reset({ + ...defaultValues, + ...preLoadedData, + }); + }, [preLoadedData, reset]); - const { setTheme } = useTheme(); - const { mutateUser } = useUser(); - - const handleFormSubmit = async (formData: ICustomTheme) => { + const handleUpdateTheme = async (formData: any) => { const payload: ICustomTheme = { background: formData.background, text: formData.text, @@ -64,28 +63,14 @@ export const CustomThemeSelector: React.FC = ({ preLoadedData }) => { theme: "custom", }; - store.user + setTheme("custom"); + + return store.user .updateCurrentUserSettings({ theme: payload }) - .then((response: any) => { - setTheme("custom"); - applyTheme(payload.palette, darkPalette); - }) - .catch((error: any) => { - console.log("error", error); - }); + .then((response: any) => response) + .catch((error: any) => error); }; - const handleUpdateTheme = async (formData: any) => { - await handleFormSubmit({ ...formData, darkPalette }); - }; - - useEffect(() => { - reset({ - ...defaultValues, - ...preLoadedData, - }); - }, [preLoadedData, reset]); - return (
@@ -164,4 +149,4 @@ export const CustomThemeSelector: React.FC = ({ preLoadedData }) => {
); -}; +}); diff --git a/apps/app/components/core/theme/theme-switch.tsx b/apps/app/components/core/theme/theme-switch.tsx index 8ac950b8c..687998c25 100644 --- a/apps/app/components/core/theme/theme-switch.tsx +++ b/apps/app/components/core/theme/theme-switch.tsx @@ -1,9 +1,5 @@ -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 @@ -13,6 +9,10 @@ import { CustomSelect } from "components/ui"; // types import { ICustomTheme } from "types"; import { unsetCustomCssVariables } from "helpers/theme.helper"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; type Props = { setPreLoadedData: React.Dispatch>; @@ -20,142 +20,116 @@ type Props = { setCustomThemeSelectorOptions: React.Dispatch>; }; -export const ThemeSwitch: React.FC = ({ - setPreLoadedData, - customThemeSelectorOptions, - setCustomThemeSelectorOptions, -}) => { - const [mounted, setMounted] = useState(false); +export const ThemeSwitch: React.FC = observer( + ({ setPreLoadedData, customThemeSelectorOptions, setCustomThemeSelectorOptions }) => { + const store: any = useMobxStore(); - const { theme, setTheme } = useTheme(); + const { user, mutateUser } = useUser(); + const { theme, setTheme } = useTheme(); - const { user, mutateUser } = useUser(); + const updateUserTheme = (newTheme: string) => { + if (!user) return; + setTheme(newTheme); + return store.user + .updateCurrentUserSettings({ theme: { ...user.theme, theme: newTheme } }) + .then((response: any) => response) + .catch((error: any) => error); + }; - const updateUserTheme = (newTheme: string) => { - if (!user) return; + const currentThemeObj = THEMES_OBJ.find((t) => t.value === theme); - 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; - - const currentThemeObj = THEMES_OBJ.find((t) => t.value === theme); - - return ( - -
+ return ( +
-
+ > +
+
+
+ {currentThemeObj.label}
- {currentThemeObj.label} -
- ) : ( - "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", - }); + ) : ( + "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", + }); + } + + if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true); + } else { + if (customThemeSelectorOptions) setCustomThemeSelectorOptions(false); + unsetCustomCssVariables(); } - 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 }) => ( - -
-
+ updateUserTheme(value); + document.documentElement.style.setProperty("--color-scheme", type); + }} + input + width="w-full" + position="right" + > + {THEMES_OBJ.map(({ value, label, type, icon }) => ( + +
-
+ > +
+
+
+ {label}
- {label} -
- - ))} - - ); -}; + + ))} + + ); + } +); diff --git a/apps/app/lib/mobx/store-init.tsx b/apps/app/lib/mobx/store-init.tsx index 9eb0d7493..5ec2165b1 100644 --- a/apps/app/lib/mobx/store-init.tsx +++ b/apps/app/lib/mobx/store-init.tsx @@ -1,9 +1,14 @@ import { useEffect } from "react"; +// next themes +import { useTheme } from "next-themes"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; +// theme helpers +import { applyTheme, unsetCustomCssVariables } from "helpers/theme.helper"; const MobxStoreInit = () => { const store: any = useMobxStore(); + const { setTheme } = useTheme(); useEffect(() => { // sidebar collapsed toggle @@ -21,11 +26,25 @@ const MobxStoreInit = () => { ); // theme - if (localStorage && localStorage.getItem("theme") && store.theme.theme === null) - store.theme.setTheme( - localStorage.getItem("theme") ? localStorage.getItem("theme") : "system" - ); - }, [store?.theme]); + if (store.theme.theme === null && store?.user?.currentUserSettings) { + let currentTheme = localStorage.getItem("theme"); + currentTheme = currentTheme ? currentTheme : "system"; + + // validating the theme and applying for initial state + if (currentTheme) { + setTheme(currentTheme); + store.theme.setTheme({ theme: { theme: currentTheme } }); + } + } + }, [store?.theme, store?.user, setTheme]); + + useEffect(() => { + // current user + if (store?.user?.currentUser === null) store.user.setCurrentUser(); + + // current user settings + if (store?.user?.currentUserSettings === null) store.user.setCurrentUserSettings(); + }, [store?.user]); return <>; }; diff --git a/apps/app/pages/[workspaceSlug]/me/profile/preferences.tsx b/apps/app/pages/[workspaceSlug]/me/profile/preferences.tsx index 61e743fc3..c73c20bfb 100644 --- a/apps/app/pages/[workspaceSlug]/me/profile/preferences.tsx +++ b/apps/app/pages/[workspaceSlug]/me/profile/preferences.tsx @@ -1,7 +1,4 @@ import { useEffect, useState } from "react"; - -// next-themes -import { useTheme } from "next-themes"; // hooks import useUserAuth from "hooks/use-user-auth"; // layouts @@ -14,37 +11,47 @@ import { Spinner } from "components/ui"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; // types import { ICustomTheme } from "types"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// next themes +import { useTheme } from "next-themes"; -const ProfilePreferences = () => { - const [customThemeSelectorOptions, setCustomThemeSelectorOptions] = useState(false); - const [preLoadedData, setPreLoadedData] = useState(null); - - const { theme } = useTheme(); - +const ProfilePreferences = observer(() => { const { user: myProfile } = useUserAuth(); + const store: any = useMobxStore(); + const { theme } = useTheme(); + + console.log("store", store?.theme?.theme); + console.log("theme", theme); + + const [customThemeSelectorOptions, setCustomThemeSelectorOptions] = useState(false); + + const [preLoadedData, setPreLoadedData] = useState(null); + useEffect(() => { - if (theme === "custom") { - if (myProfile?.theme.palette) + if (store?.user && store?.theme?.theme === "custom") { + const currentTheme = store?.user?.currentUserSettings?.theme; + if (currentTheme.palette) setPreLoadedData({ - background: myProfile.theme.background !== "" ? myProfile.theme.background : "#0d101b", - text: myProfile.theme.text !== "" ? myProfile.theme.text : "#c5c5c5", - primary: myProfile.theme.primary !== "" ? myProfile.theme.primary : "#3f76ff", + background: currentTheme.background !== "" ? currentTheme.background : "#0d101b", + text: currentTheme.text !== "" ? currentTheme.text : "#c5c5c5", + primary: currentTheme.primary !== "" ? currentTheme.primary : "#3f76ff", sidebarBackground: - myProfile.theme.sidebarBackground !== "" - ? myProfile.theme.sidebarBackground - : "#0d101b", - sidebarText: myProfile.theme.sidebarText !== "" ? myProfile.theme.sidebarText : "#c5c5c5", + currentTheme.sidebarBackground !== "" ? currentTheme.sidebarBackground : "#0d101b", + sidebarText: currentTheme.sidebarText !== "" ? currentTheme.sidebarText : "#c5c5c5", darkPalette: false, palette: - myProfile.theme.palette !== ",,,," - ? myProfile.theme.palette + currentTheme.palette !== ",,,," + ? currentTheme.palette : "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5", theme: "custom", }); - if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true); + setCustomThemeSelectorOptions((prevData) => true); } - }, [myProfile, theme, customThemeSelectorOptions]); + }, [store, store?.theme?.theme]); return ( { )} ); -}; +}); export default ProfilePreferences; diff --git a/apps/app/pages/index.tsx b/apps/app/pages/index.tsx index f9e4d9844..28c5acc98 100644 --- a/apps/app/pages/index.tsx +++ b/apps/app/pages/index.tsx @@ -22,8 +22,14 @@ import { import { Spinner } from "components/ui"; // images import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// next themes import { useTheme } from "next-themes"; -import { ICurrentUserResponse, IUser } from "types"; +import { IUser } from "types"; + // types type EmailPasswordFormValues = { email: string; @@ -31,15 +37,18 @@ type EmailPasswordFormValues = { medium?: string; }; -const HomePage: NextPage = () => { +const HomePage: NextPage = observer(() => { + const store: any = useMobxStore(); + const { setTheme } = useTheme(); + const { isLoading, mutateUser } = useUserAuth("sign-in"); const { setToastAlert } = useToast(); - const { setTheme } = useTheme(); - - const changeTheme = (user: IUser) => { - setTheme(user.theme.theme ?? "system"); + const handleTheme = (user: IUser) => { + const currentTheme = user.theme.theme ?? "system"; + setTheme(currentTheme); + store?.user?.setCurrentUserSettings(); }; const handleGoogleSignIn = async ({ clientId, credential }: any) => { @@ -53,7 +62,7 @@ const HomePage: NextPage = () => { const response = await authenticationService.socialAuth(socialAuthPayload); if (response && response?.user) { mutateUser(); - changeTheme(response.user); + handleTheme(response?.user); } } else { throw Error("Cant find credentials"); @@ -79,7 +88,7 @@ const HomePage: NextPage = () => { const response = await authenticationService.socialAuth(socialAuthPayload); if (response && response?.user) { mutateUser(); - changeTheme(response.user); + handleTheme(response?.user); } } else { throw Error("Cant find credentials"); @@ -101,7 +110,7 @@ const HomePage: NextPage = () => { try { if (response) { mutateUser(); - changeTheme(response.user); + handleTheme(response?.user); } } catch (err: any) { setToastAlert({ @@ -128,7 +137,7 @@ const HomePage: NextPage = () => { try { if (response) { mutateUser(); - changeTheme(response.user); + handleTheme(response?.user); } } catch (err: any) { setToastAlert({ @@ -140,10 +149,6 @@ const HomePage: NextPage = () => { } }; - useEffect(() => { - setTheme("system"); - }, [setTheme]); - return ( {isLoading ? ( @@ -202,6 +207,6 @@ const HomePage: NextPage = () => { )} ); -}; +}); export default HomePage; diff --git a/apps/app/store/theme.ts b/apps/app/store/theme.ts index ba84f3ba4..ecd02ec97 100644 --- a/apps/app/store/theme.ts +++ b/apps/app/store/theme.ts @@ -38,21 +38,24 @@ class ThemeStore { } } - setTheme = async (_theme: ICurrentUserSettings) => { + setTheme = async (_theme: { theme: ICurrentUserSettings }) => { try { - localStorage.setItem("theme", _theme.theme.toString()); - this.theme = _theme.theme.toString(); + const currentTheme: string = _theme.theme.theme.toString(); - if (this.theme === "custom") { - let themeSettings = this.rootStore.user.currentUserSettings || null; - if (themeSettings && themeSettings.theme.palette) { - applyTheme( - themeSettings.theme.palette !== ",,,," - ? themeSettings.theme.palette - : "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5", - themeSettings.theme.darkPalette - ); - } + // 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); diff --git a/apps/app/store/user.ts b/apps/app/store/user.ts index dde652656..28dd3b8d2 100644 --- a/apps/app/store/user.ts +++ b/apps/app/store/user.ts @@ -1,5 +1,5 @@ // mobx -import { action, observable, computed, makeObservable } from "mobx"; +import { action, observable, computed, runInAction, makeObservable } from "mobx"; // services import UserService from "services/user.service"; // interfaces @@ -31,8 +31,9 @@ class UserStore { try { let userResponse: ICurrentUser | null = await UserService.currentUser(); userResponse = userResponse || null; + if (userResponse) { - this.currentUser = { + const userPayload: ICurrentUser = { id: userResponse?.id, avatar: userResponse?.avatar, first_name: userResponse?.first_name, @@ -46,6 +47,9 @@ class UserStore { is_onboarded: userResponse?.is_onboarded, role: userResponse?.role, }; + runInAction(() => { + this.currentUser = userPayload; + }); } } catch (error) { console.error("Fetching current user error", error); @@ -56,11 +60,15 @@ class UserStore { try { let userSettingsResponse: ICurrentUserSettings | null = await UserService.currentUser(); userSettingsResponse = userSettingsResponse || null; + if (userSettingsResponse) { - this.currentUserSettings = { - theme: userSettingsResponse?.theme, + const themePayload = { + theme: { ...userSettingsResponse?.theme }, }; - this.rootStore.theme.setTheme(); + runInAction(() => { + this.currentUserSettings = themePayload; + this.rootStore.theme.setTheme(themePayload); + }); } } catch (error) { console.error("Fetching current user error", error); @@ -71,9 +79,26 @@ class UserStore { try { let userResponse: ICurrentUser = await UserService.updateUser(user); userResponse = userResponse || null; + if (userResponse) { - this.currentUser = userResponse; - return userResponse; + const userPayload: ICurrentUser = { + id: userResponse?.id, + avatar: userResponse?.avatar, + first_name: userResponse?.first_name, + last_name: userResponse?.last_name, + username: userResponse?.username, + email: userResponse?.email, + mobile_number: userResponse?.mobile_number, + is_email_verified: userResponse?.is_email_verified, + is_tour_completed: userResponse?.is_tour_completed, + onboarding_step: userResponse?.onboarding_step, + is_onboarded: userResponse?.is_onboarded, + role: userResponse?.role, + }; + runInAction(() => { + this.currentUser = userPayload; + }); + return userPayload; } } catch (error) { console.error("Updating user error", error); @@ -86,9 +111,14 @@ class UserStore { let userSettingsResponse: ICurrentUserSettings = await UserService.updateUser(userTheme); userSettingsResponse = userSettingsResponse || null; if (userSettingsResponse) { - this.currentUserSettings = userSettingsResponse; - this.rootStore.theme.setTheme(userTheme); - return userSettingsResponse; + const themePayload = { + theme: { ...userSettingsResponse?.theme }, + }; + runInAction(() => { + this.currentUserSettings = themePayload; + this.rootStore.theme.setTheme(themePayload); + }); + return themePayload; } } catch (error) { console.error("Updating user settings error", error); @@ -97,10 +127,7 @@ class UserStore { }; // init load - initialLoad() { - this.setCurrentUser(); - this.setCurrentUserSettings(); - } + initialLoad() {} } export default UserStore;