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 (