From 1ab0178d88f92ad2403de371ce610def602f2b84 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Mon, 28 Jul 2025 02:38:18 +0200 Subject: [PATCH] Fix local light app theme flicker on initial render MUIs "useColorScheme" hook always returned "system" on the initial render. (Probably because it's used outside the AppProvider context) --- src/lib/mui/MUI.constants.ts | 9 +++++++++ src/modules/settings/screens/Appearance.tsx | 3 ++- .../theme/contexts/AppThemeContextProvider.tsx | 17 +++++++++-------- 3 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 src/lib/mui/MUI.constants.ts diff --git a/src/lib/mui/MUI.constants.ts b/src/lib/mui/MUI.constants.ts new file mode 100644 index 00000000..a2b673e6 --- /dev/null +++ b/src/lib/mui/MUI.constants.ts @@ -0,0 +1,9 @@ +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +export const MUI_THEME_MODE_KEY = 'mui-mode'; diff --git a/src/modules/settings/screens/Appearance.tsx b/src/modules/settings/screens/Appearance.tsx index b0b58427..e4f58c80 100644 --- a/src/modules/settings/screens/Appearance.tsx +++ b/src/modules/settings/screens/Appearance.tsx @@ -35,6 +35,7 @@ import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { AppStorage } from '@/lib/storage/AppStorage.ts'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; import { MANGA_GRID_WIDTH, SERVER_SETTINGS_METADATA_DEFAULT } from '@/modules/settings/Settings.constants.ts'; +import { MUI_THEME_MODE_KEY } from '@/lib/mui/MUI.constants.ts'; export const Appearance = () => { const { t, i18n } = useTranslation(); @@ -86,7 +87,7 @@ export const Appearance = () => { setThemeMode(newMode as ThemeMode); setMode(newMode); // in case a non "colorSchemes" mui theme is active, "setMode" does not update the mode ("mui-mode") value - AppStorage.local.setItem('mui-mode', newMode, true); + AppStorage.local.setItem(MUI_THEME_MODE_KEY, newMode, true); }} > diff --git a/src/modules/theme/contexts/AppThemeContextProvider.tsx b/src/modules/theme/contexts/AppThemeContextProvider.tsx index cd14a294..00f1b2b1 100644 --- a/src/modules/theme/contexts/AppThemeContextProvider.tsx +++ b/src/modules/theme/contexts/AppThemeContextProvider.tsx @@ -7,7 +7,7 @@ */ import { ReactNode, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import { Direction, ThemeProvider, useColorScheme } from '@mui/material/styles'; +import { Direction, ThemeProvider } from '@mui/material/styles'; import { CacheProvider } from '@emotion/react'; import { useTranslation } from 'react-i18next'; import { AppThemeContext, TAppThemeContext, ThemeMode } from '@/modules/theme/contexts/AppThemeContext.tsx'; @@ -23,10 +23,10 @@ import { makeToast } from '@/modules/core/utils/Toast.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { useLocalStorage } from '@/modules/core/hooks/useStorage.tsx'; import { AppStorage } from '@/lib/storage/AppStorage.ts'; +import { MUI_THEME_MODE_KEY } from '@/lib/mui/MUI.constants.ts'; export const AppThemeContextProvider = ({ children }: { children: ReactNode }) => { const { t, i18n } = useTranslation(); - const { mode } = useColorScheme(); const { request: metadataServerSettingsRequest, settings: { appTheme: serverAppTheme, themeMode, shouldUsePureBlackMode, customThemes }, @@ -35,17 +35,18 @@ export const AppThemeContextProvider = ({ children }: { children: ReactNode }) = 'appTheme', getTheme(serverAppTheme, customThemes), ); + const [localThemeMode] = useLocalStorage(MUI_THEME_MODE_KEY, themeMode); const directionRef = useRef('ltr'); const [systemThemeMode, setSystemThemeMode] = useState(MediaQuery.getSystemThemeMode()); const [dynamicColor, setDynamicColor] = useState(null); - const appTheme = - metadataServerSettingsRequest.loading || metadataServerSettingsRequest.error - ? localAppTheme.id - : serverAppTheme; - const actualThemeMode = mode ?? themeMode ?? 'dark'; + const areMetadataServerSettingsReady = + !metadataServerSettingsRequest.loading && !metadataServerSettingsRequest.error; + + const appTheme = areMetadataServerSettingsReady ? serverAppTheme : localAppTheme.id; + const actualThemeMode = areMetadataServerSettingsReady ? themeMode : localThemeMode; const currentDirection = i18n.dir(); const updateSetting = createUpdateMetadataServerSettings<'appTheme' | 'themeMode' | 'shouldUsePureBlackMode'>((e) => @@ -94,7 +95,7 @@ export const AppThemeContextProvider = ({ children }: { children: ReactNode }) = }, []); useEffect(() => { - if (metadataServerSettingsRequest.loading || metadataServerSettingsRequest.error) { + if (!areMetadataServerSettingsReady) { return; }