[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 type { ReactNode } from "react";
|
||||||
import { useEffect } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
|
|
@ -28,6 +28,13 @@ function StoreWrapper(props: TStoreWrapper) {
|
||||||
const { data: userProfile } = useUserProfile();
|
const { data: userProfile } = useUserProfile();
|
||||||
const { changeLanguage } = useTranslation();
|
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
|
* Sidebar collapsed fetching from local storage
|
||||||
*/
|
*/
|
||||||
|
|
@ -38,25 +45,61 @@ function StoreWrapper(props: TStoreWrapper) {
|
||||||
}, [sidebarCollapsed, setTheme, toggleSidebar]);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
if (!userProfile?.theme?.theme) return;
|
if (!userProfile?.theme?.theme) return;
|
||||||
const currentTheme = userProfile?.theme?.theme || "system";
|
|
||||||
const theme = userProfile?.theme;
|
|
||||||
|
|
||||||
if (currentTheme) {
|
const currentTheme = userProfile?.theme?.theme;
|
||||||
setTheme(currentTheme);
|
const previousTheme = previousThemeRef.current;
|
||||||
if (currentTheme === "custom") {
|
const themeData = userProfile?.theme;
|
||||||
// New 2-color palette system
|
|
||||||
if (theme.primary && theme.background && theme.darkPalette !== undefined) {
|
// Apply custom theme if current theme is custom
|
||||||
applyCustomTheme(theme.primary, theme.background, theme.darkPalette ? "dark" : "light");
|
if (currentTheme === "custom" && themeData.primary && themeData.background && themeData.darkPalette !== undefined) {
|
||||||
|
applyCustomTheme(themeData.primary, themeData.background, themeData.darkPalette ? "dark" : "light");
|
||||||
}
|
}
|
||||||
} else {
|
// Clear custom theme CSS when switching away from custom
|
||||||
|
else if (previousTheme === "custom" && currentTheme !== "custom") {
|
||||||
clearCustomTheme();
|
clearCustomTheme();
|
||||||
|
// No reload needed - let CSS cascade handle it naturally
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, [userProfile?.theme, setTheme]);
|
// Update previous theme for next comparison
|
||||||
|
previousThemeRef.current = currentTheme;
|
||||||
|
}, [userProfile?.theme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!userProfile?.language) return;
|
if (!userProfile?.language) return;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue