From 098a540aaa5d5f42d3f46be952aab1ebf7c78bf2 Mon Sep 17 00:00:00 2001 From: Akmal Isomadinov Date: Wed, 24 Jul 2024 14:18:06 +0500 Subject: [PATCH] Doceditor: Fixed theme --- packages/doceditor/src/app/(root)/layout.tsx | 23 ++++-- packages/doceditor/src/hooks/useTheme.ts | 79 +++++++++++++------ .../doceditor/src/providers/ThemeProvider.tsx | 23 +++++- packages/doceditor/src/providers/index.tsx | 10 ++- packages/doceditor/src/utils/actions.ts | 21 ++++- 5 files changed, 122 insertions(+), 34 deletions(-) diff --git a/packages/doceditor/src/app/(root)/layout.tsx b/packages/doceditor/src/app/(root)/layout.tsx index dffd63eaf9..6e4f1b65b6 100644 --- a/packages/doceditor/src/app/(root)/layout.tsx +++ b/packages/doceditor/src/app/(root)/layout.tsx @@ -24,15 +24,18 @@ // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode import { redirect } from "next/navigation"; -import { headers } from "next/headers"; +import { headers, cookies } from "next/headers"; +import { ThemeKeys } from "@docspace/shared/enums"; import { getBaseUrl } from "@docspace/shared/utils/next-ssr-helper"; +import { SYSTEM_THEME_KEY } from "@docspace/shared/constants"; +import Providers from "@/providers"; import Scripts from "@/components/Scripts"; import StyledComponentsRegistry from "@/utils/registry"; +import { getColorTheme, getSettings, getUser } from "@/utils/actions"; + import "@/styles/globals.scss"; -import Providers from "@/providers"; -import { getSettings, getUser } from "@/utils/actions"; export default async function RootLayout({ children, @@ -41,13 +44,23 @@ export default async function RootLayout({ }) { const hdrs = headers(); + const cookieStore = cookies(); + + const systemTheme = cookieStore.get(SYSTEM_THEME_KEY)?.value as + | ThemeKeys + | undefined; + if (hdrs.get("x-health-check") || hdrs.get("referer")?.includes("/health")) { console.log("is health check"); return <>; } const startDate = new Date(); - const [user, settings] = await Promise.all([getUser(), getSettings()]); + const [user, settings, colorTheme] = await Promise.all([ + getUser(), + getSettings(), + getColorTheme(), + ]); const timer = new Date().getTime() - startDate.getTime(); if (settings === "access-restricted") redirect(`${getBaseUrl()}/${settings}`); @@ -70,7 +83,7 @@ export default async function RootLayout({ diff --git a/packages/doceditor/src/hooks/useTheme.ts b/packages/doceditor/src/hooks/useTheme.ts index d9187d34ad..cb9f3af39f 100644 --- a/packages/doceditor/src/hooks/useTheme.ts +++ b/packages/doceditor/src/hooks/useTheme.ts @@ -26,34 +26,59 @@ import React from "react"; import { i18n } from "i18next"; +import { match, P } from "ts-pattern"; import { Base, Dark, TColorScheme, TTheme } from "@docspace/shared/themes"; import { getSystemTheme } from "@docspace/shared/utils"; +import { setCookie } from "@docspace/shared/utils/cookie"; import { ThemeKeys } from "@docspace/shared/enums"; import { getAppearanceTheme } from "@docspace/shared/api/settings"; -import { TUser } from "@docspace/shared/api/people/types"; import { getFontFamilyDependingOnLanguage } from "@docspace/shared/utils/rtlUtils"; +import { SYSTEM_THEME_KEY } from "@docspace/shared/constants"; -const SYSTEM_THEME = getSystemTheme(); +import type { TUser } from "@docspace/shared/api/people/types"; +import type { TGetColorTheme } from "@docspace/shared/api/settings/types"; + +type MatchType = [ThemeKeys | undefined, ThemeKeys | undefined]; export interface UseThemeProps { user?: TUser; i18n?: i18n; + systemTheme?: ThemeKeys; + colorTheme?: TGetColorTheme; } -const useTheme = ({ user, i18n }: UseThemeProps) => { +const useTheme = ({ user, i18n, systemTheme, colorTheme }: UseThemeProps) => { const [currentColorTheme, setCurrentColorTheme] = - React.useState({} as TColorScheme); + React.useState(() => { + console.log("currentColorTheme"); + + if (!colorTheme) return {} as TColorScheme; + + return ( + colorTheme.themes.find((theme) => theme.id === colorTheme.selected) ?? + ({} as TColorScheme) + ); + }); const [theme, setTheme] = React.useState(() => { - if (user?.theme === ThemeKeys.DarkStr) - return { - ...Dark, - currentColorScheme: currentColorTheme, - }; + const interfaceDirection = i18n?.dir ? i18n.dir() : "ltr"; + + const newTheme = match([user?.theme, systemTheme]) + .returnType() + .with([ThemeKeys.DarkStr, P._], () => Dark) + .with([ThemeKeys.BaseStr, P._], () => Base) + .with([ThemeKeys.SystemStr, ThemeKeys.BaseStr], () => Base) + .with([ThemeKeys.SystemStr, ThemeKeys.DarkStr], () => Dark) + .with([undefined, ThemeKeys.DarkStr], () => Dark) + .with([undefined, ThemeKeys.BaseStr], () => Base) + .otherwise(() => Base); + return { - ...Base, + ...newTheme, currentColorScheme: currentColorTheme, + interfaceDirection, + fontFamily: getFontFamilyDependingOnLanguage(i18n?.language ?? "en"), }; }); @@ -73,28 +98,26 @@ const useTheme = ({ user, i18n }: UseThemeProps) => { }, []); const getUserTheme = React.useCallback(() => { - if (!user?.theme) return; - let theme = user.theme; + const SYSTEM_THEME = getSystemTheme(); + + let theme = user?.theme ?? SYSTEM_THEME; const interfaceDirection = i18n?.dir ? i18n.dir() : "ltr"; - if (user.theme === ThemeKeys.SystemStr) theme = SYSTEM_THEME; + if (user?.theme === ThemeKeys.SystemStr) theme = SYSTEM_THEME; - if (theme === ThemeKeys.BaseStr) { - setTheme({ - ...Base, - currentColorScheme: currentColorTheme, - interfaceDirection, - fontFamily: getFontFamilyDependingOnLanguage(i18n?.language), - }); + const fontFamily = getFontFamilyDependingOnLanguage(i18n?.language ?? "en"); - return; - } + const isBaseTheme = theme === ThemeKeys.BaseStr; + const themeCookie = isBaseTheme ? ThemeKeys.BaseStr : ThemeKeys.DarkStr; setTheme({ - ...Dark, + ...(isBaseTheme ? Base : Dark), currentColorScheme: currentColorTheme, interfaceDirection, + fontFamily, }); + + setCookie(SYSTEM_THEME_KEY, themeCookie); }, [user?.theme, i18n, currentColorTheme]); React.useEffect(() => { @@ -105,6 +128,16 @@ const useTheme = ({ user, i18n }: UseThemeProps) => { getUserTheme(); }, [currentColorTheme, getUserTheme]); + React.useEffect(() => { + const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); + + mediaQuery.addEventListener("change", getUserTheme); + + return () => { + mediaQuery.removeEventListener("change", getUserTheme); + }; + }, [getUserTheme]); + return { theme, currentColorTheme }; }; diff --git a/packages/doceditor/src/providers/ThemeProvider.tsx b/packages/doceditor/src/providers/ThemeProvider.tsx index 6ec9bef2d6..09f79e25e5 100644 --- a/packages/doceditor/src/providers/ThemeProvider.tsx +++ b/packages/doceditor/src/providers/ThemeProvider.tsx @@ -30,7 +30,11 @@ import React from "react"; import { ThemeProvider as ComponentThemeProvider } from "@docspace/shared/components/theme-provider"; import { TUser } from "@docspace/shared/api/people/types"; -import { TSettings } from "@docspace/shared/api/settings/types"; +import type { + TGetColorTheme, + TSettings, +} from "@docspace/shared/api/settings/types"; +import type { ThemeKeys } from "@docspace/shared/enums"; import useTheme from "@/hooks/useTheme"; import useI18N from "@/hooks/useI18N"; @@ -39,12 +43,25 @@ type TThemeProvider = { children: React.ReactNode; settings: TSettings | undefined; user: TUser | undefined; + systemTheme: ThemeKeys | undefined; + colorTheme: TGetColorTheme | undefined; }; -const ThemeProvider = ({ children, user, settings }: TThemeProvider) => { +const ThemeProvider = ({ + children, + user, + settings, + systemTheme, + colorTheme, +}: TThemeProvider) => { const { i18n } = useI18N({ settings, user }); - const { theme, currentColorTheme } = useTheme({ user, i18n }); + const { theme, currentColorTheme } = useTheme({ + user, + i18n, + systemTheme, + colorTheme, + }); return (