Doceditor: Fixed theme

This commit is contained in:
Akmal Isomadinov 2024-07-24 14:18:06 +05:00
parent 3eb4671598
commit 098a540aaa
5 changed files with 122 additions and 34 deletions

View File

@ -24,15 +24,18 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { redirect } from "next/navigation"; 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 { 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 Scripts from "@/components/Scripts";
import StyledComponentsRegistry from "@/utils/registry"; import StyledComponentsRegistry from "@/utils/registry";
import { getColorTheme, getSettings, getUser } from "@/utils/actions";
import "@/styles/globals.scss"; import "@/styles/globals.scss";
import Providers from "@/providers";
import { getSettings, getUser } from "@/utils/actions";
export default async function RootLayout({ export default async function RootLayout({
children, children,
@ -41,13 +44,23 @@ export default async function RootLayout({
}) { }) {
const hdrs = headers(); 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")) { if (hdrs.get("x-health-check") || hdrs.get("referer")?.includes("/health")) {
console.log("is health check"); console.log("is health check");
return <></>; return <></>;
} }
const startDate = new Date(); 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(); const timer = new Date().getTime() - startDate.getTime();
if (settings === "access-restricted") redirect(`${getBaseUrl()}/${settings}`); if (settings === "access-restricted") redirect(`${getBaseUrl()}/${settings}`);
@ -70,7 +83,7 @@ export default async function RootLayout({
<body> <body>
<StyledComponentsRegistry> <StyledComponentsRegistry>
<Providers <Providers
contextData={{ user, settings }} contextData={{ user, settings, systemTheme, colorTheme }}
api_host={api_host} api_host={api_host}
timer={timer} timer={timer}
> >

View File

@ -26,34 +26,59 @@
import React from "react"; import React from "react";
import { i18n } from "i18next"; import { i18n } from "i18next";
import { match, P } from "ts-pattern";
import { Base, Dark, TColorScheme, TTheme } from "@docspace/shared/themes"; import { Base, Dark, TColorScheme, TTheme } from "@docspace/shared/themes";
import { getSystemTheme } from "@docspace/shared/utils"; import { getSystemTheme } from "@docspace/shared/utils";
import { setCookie } from "@docspace/shared/utils/cookie";
import { ThemeKeys } from "@docspace/shared/enums"; import { ThemeKeys } from "@docspace/shared/enums";
import { getAppearanceTheme } from "@docspace/shared/api/settings"; import { getAppearanceTheme } from "@docspace/shared/api/settings";
import { TUser } from "@docspace/shared/api/people/types";
import { getFontFamilyDependingOnLanguage } from "@docspace/shared/utils/rtlUtils"; 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 { export interface UseThemeProps {
user?: TUser; user?: TUser;
i18n?: i18n; i18n?: i18n;
systemTheme?: ThemeKeys;
colorTheme?: TGetColorTheme;
} }
const useTheme = ({ user, i18n }: UseThemeProps) => { const useTheme = ({ user, i18n, systemTheme, colorTheme }: UseThemeProps) => {
const [currentColorTheme, setCurrentColorTheme] = const [currentColorTheme, setCurrentColorTheme] =
React.useState<TColorScheme>({} as TColorScheme); React.useState<TColorScheme>(() => {
console.log("currentColorTheme");
if (!colorTheme) return {} as TColorScheme;
return (
colorTheme.themes.find((theme) => theme.id === colorTheme.selected) ??
({} as TColorScheme)
);
});
const [theme, setTheme] = React.useState<TTheme>(() => { const [theme, setTheme] = React.useState<TTheme>(() => {
if (user?.theme === ThemeKeys.DarkStr) const interfaceDirection = i18n?.dir ? i18n.dir() : "ltr";
return {
...Dark, const newTheme = match<MatchType>([user?.theme, systemTheme])
currentColorScheme: currentColorTheme, .returnType<TTheme>()
}; .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 { return {
...Base, ...newTheme,
currentColorScheme: currentColorTheme, currentColorScheme: currentColorTheme,
interfaceDirection,
fontFamily: getFontFamilyDependingOnLanguage(i18n?.language ?? "en"),
}; };
}); });
@ -73,28 +98,26 @@ const useTheme = ({ user, i18n }: UseThemeProps) => {
}, []); }, []);
const getUserTheme = React.useCallback(() => { const getUserTheme = React.useCallback(() => {
if (!user?.theme) return; const SYSTEM_THEME = getSystemTheme();
let theme = user.theme;
let theme = user?.theme ?? SYSTEM_THEME;
const interfaceDirection = i18n?.dir ? i18n.dir() : "ltr"; 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) { const fontFamily = getFontFamilyDependingOnLanguage(i18n?.language ?? "en");
setTheme({
...Base,
currentColorScheme: currentColorTheme,
interfaceDirection,
fontFamily: getFontFamilyDependingOnLanguage(i18n?.language),
});
return; const isBaseTheme = theme === ThemeKeys.BaseStr;
} const themeCookie = isBaseTheme ? ThemeKeys.BaseStr : ThemeKeys.DarkStr;
setTheme({ setTheme({
...Dark, ...(isBaseTheme ? Base : Dark),
currentColorScheme: currentColorTheme, currentColorScheme: currentColorTheme,
interfaceDirection, interfaceDirection,
fontFamily,
}); });
setCookie(SYSTEM_THEME_KEY, themeCookie);
}, [user?.theme, i18n, currentColorTheme]); }, [user?.theme, i18n, currentColorTheme]);
React.useEffect(() => { React.useEffect(() => {
@ -105,6 +128,16 @@ const useTheme = ({ user, i18n }: UseThemeProps) => {
getUserTheme(); getUserTheme();
}, [currentColorTheme, 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 }; return { theme, currentColorTheme };
}; };

View File

@ -30,7 +30,11 @@ import React from "react";
import { ThemeProvider as ComponentThemeProvider } from "@docspace/shared/components/theme-provider"; import { ThemeProvider as ComponentThemeProvider } from "@docspace/shared/components/theme-provider";
import { TUser } from "@docspace/shared/api/people/types"; 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 useTheme from "@/hooks/useTheme";
import useI18N from "@/hooks/useI18N"; import useI18N from "@/hooks/useI18N";
@ -39,12 +43,25 @@ type TThemeProvider = {
children: React.ReactNode; children: React.ReactNode;
settings: TSettings | undefined; settings: TSettings | undefined;
user: TUser | 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 { i18n } = useI18N({ settings, user });
const { theme, currentColorTheme } = useTheme({ user, i18n }); const { theme, currentColorTheme } = useTheme({
user,
i18n,
systemTheme,
colorTheme,
});
return ( return (
<ComponentThemeProvider <ComponentThemeProvider

View File

@ -24,9 +24,13 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { TUser } from "@docspace/shared/api/people/types";
import { TSettings } from "@docspace/shared/api/settings/types";
import { Toast } from "@docspace/shared/components/toast/Toast"; import { Toast } from "@docspace/shared/components/toast/Toast";
import type { TUser } from "@docspace/shared/api/people/types";
import type {
TGetColorTheme,
TSettings,
} from "@docspace/shared/api/settings/types";
import type { ThemeKeys } from "@docspace/shared/enums";
import ThemeProvider from "./ThemeProvider"; import ThemeProvider from "./ThemeProvider";
import TranslationProvider from "./TranslationProvider"; import TranslationProvider from "./TranslationProvider";
@ -35,6 +39,8 @@ import ErrorProvider from "./ErrorProvider";
export type TContextData = { export type TContextData = {
user: TUser | undefined; user: TUser | undefined;
settings: TSettings | undefined; settings: TSettings | undefined;
systemTheme: ThemeKeys | undefined;
colorTheme: TGetColorTheme | undefined;
}; };
export type TProviders = { export type TProviders = {

View File

@ -36,7 +36,10 @@ import type {
TFile, TFile,
} from "@docspace/shared/api/files/types"; } from "@docspace/shared/api/files/types";
import { TUser } from "@docspace/shared/api/people/types"; 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 { import type {
ActionType, ActionType,
@ -500,3 +503,19 @@ export async function getEditorUrl(
return editorUrl.response as TDocServiceLocation; return editorUrl.response as TDocServiceLocation;
} }
export async function getColorTheme() {
const [getSettings] = createRequest(
[`/settings/colortheme`],
[["", ""]],
"GET",
);
const res = await fetch(getSettings);
if (!res.ok) return;
const colorTheme = await res.json();
return colorTheme.response as TGetColorTheme;
}