[WEB-5772] fix: theme switch flicker (#8428)
This commit is contained in:
parent
70eea50db5
commit
5590bf7198
1 changed files with 58 additions and 15 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import type { ReactNode } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useTheme } from "next-themes";
|
||||
|
|
@ -28,6 +28,13 @@ function StoreWrapper(props: TStoreWrapper) {
|
|||
const { data: userProfile } = useUserProfile();
|
||||
const { changeLanguage } = useTranslation();
|
||||
|
||||
// Track if we've initialized theme from server (one-time only)
|
||||
const hasInitializedThemeRef = useRef(false);
|
||||
// Track current user to reset on logout/login
|
||||
const currentUserIdRef = useRef<string | undefined>(undefined);
|
||||
// Track previous theme to detect transitions from custom theme
|
||||
const previousThemeRef = useRef<string | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* Sidebar collapsed fetching from local storage
|
||||
*/
|
||||
|
|
@ -38,25 +45,61 @@ function StoreWrapper(props: TStoreWrapper) {
|
|||
}, [sidebarCollapsed, setTheme, toggleSidebar]);
|
||||
|
||||
/**
|
||||
* Setting up the theme of the user by fetching it from profile
|
||||
* Effect 1: Initial theme sync from server (one-time only)
|
||||
*
|
||||
* This effect runs ONCE per user session to load theme from server.
|
||||
* After initial load, all theme changes are localStorage-driven (next-themes).
|
||||
* This prevents a feedback loop where server updates trigger UI updates in a cycle.
|
||||
*/
|
||||
useEffect(() => {
|
||||
const userId = userProfile?.id;
|
||||
|
||||
// Reset initialization flag when user changes (logout/login)
|
||||
// This handles both logout (userId becomes undefined) and login (userId changes)
|
||||
if (userId !== currentUserIdRef.current) {
|
||||
hasInitializedThemeRef.current = false;
|
||||
previousThemeRef.current = undefined;
|
||||
currentUserIdRef.current = userId;
|
||||
}
|
||||
|
||||
// Only initialize theme from server on FIRST load for this user
|
||||
if (!userProfile?.theme?.theme || hasInitializedThemeRef.current) {
|
||||
return; // Skip if already initialized or no profile data
|
||||
}
|
||||
|
||||
// Apply theme from server profile (one-time only)
|
||||
setTheme(userProfile?.theme?.theme || "system");
|
||||
|
||||
// Mark as initialized - prevents future syncs from server
|
||||
hasInitializedThemeRef.current = true;
|
||||
}, [userProfile?.theme?.theme, setTheme]);
|
||||
|
||||
/**
|
||||
* Effect 2: Custom theme CSS application (runs on every change)
|
||||
*
|
||||
* This effect applies or clears custom theme CSS variables whenever
|
||||
* the theme changes. It runs independently of the initial sync effect.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!userProfile?.theme?.theme) return;
|
||||
const currentTheme = userProfile?.theme?.theme || "system";
|
||||
const theme = userProfile?.theme;
|
||||
|
||||
if (currentTheme) {
|
||||
setTheme(currentTheme);
|
||||
if (currentTheme === "custom") {
|
||||
// New 2-color palette system
|
||||
if (theme.primary && theme.background && theme.darkPalette !== undefined) {
|
||||
applyCustomTheme(theme.primary, theme.background, theme.darkPalette ? "dark" : "light");
|
||||
}
|
||||
} else {
|
||||
clearCustomTheme();
|
||||
}
|
||||
const currentTheme = userProfile?.theme?.theme;
|
||||
const previousTheme = previousThemeRef.current;
|
||||
const themeData = userProfile?.theme;
|
||||
|
||||
// Apply custom theme if current theme is custom
|
||||
if (currentTheme === "custom" && themeData.primary && themeData.background && themeData.darkPalette !== undefined) {
|
||||
applyCustomTheme(themeData.primary, themeData.background, themeData.darkPalette ? "dark" : "light");
|
||||
}
|
||||
}, [userProfile?.theme, setTheme]);
|
||||
// Clear custom theme CSS when switching away from custom
|
||||
else if (previousTheme === "custom" && currentTheme !== "custom") {
|
||||
clearCustomTheme();
|
||||
// No reload needed - let CSS cascade handle it naturally
|
||||
}
|
||||
|
||||
// Update previous theme for next comparison
|
||||
previousThemeRef.current = currentTheme;
|
||||
}, [userProfile?.theme]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userProfile?.language) return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue