fix: update Aurora colors reactively without recreating WebGL context

Split the monolithic useEffect into two: one for WebGL init (depends
only on isEnabled) and one for updating color uniforms (depends on
theme colors). Previously the entire WebGL context was torn down and
rebuilt on every color change, causing updates to not apply until
page refresh or navigation.
This commit is contained in:
c0mrade
2026-02-04 16:00:05 +03:00
parent 3a042473f0
commit 59a251cb8c

View File

@@ -1,9 +1,10 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { Renderer, Program, Mesh, Color, Triangle } from 'ogl'; import { Renderer, Program, Mesh, Color, Triangle } from 'ogl';
import { brandingApi } from '@/api/branding'; import { brandingApi } from '@/api/branding';
import { themeColorsApi } from '@/api/themeColors';
import { useTheme } from '@/hooks/useTheme'; import { useTheme } from '@/hooks/useTheme';
import { ThemeSettings, DEFAULT_THEME_COLORS } from '@/types/theme'; import { DEFAULT_THEME_COLORS } from '@/types/theme';
const VERT = /* glsl */ `#version 300 es const VERT = /* glsl */ `#version 300 es
in vec2 position; in vec2 position;
@@ -132,8 +133,6 @@ export function Aurora() {
const rendererRef = useRef<Renderer | null>(null); const rendererRef = useRef<Renderer | null>(null);
const programRef = useRef<Program | null>(null); const programRef = useRef<Program | null>(null);
const queryClient = useQueryClient();
// Fetch animation setting // Fetch animation setting
const { data: animationSetting } = useQuery({ const { data: animationSetting } = useQuery({
queryKey: ['animation-enabled'], queryKey: ['animation-enabled'],
@@ -143,15 +142,19 @@ export function Aurora() {
const isEnabled = animationSetting?.enabled ?? false; const isEnabled = animationSetting?.enabled ?? false;
// Get theme colors from cache (already fetched by ThemeColorsProvider) // Subscribe reactively to theme-colors cache so Aurora re-renders on setQueryData
const themeColors = const { data: themeColors = DEFAULT_THEME_COLORS } = useQuery({
queryClient.getQueryData<ThemeSettings>(['theme-colors']) || DEFAULT_THEME_COLORS; queryKey: ['theme-colors'],
queryFn: themeColorsApi.getColors,
staleTime: 5 * 60 * 1000,
});
// Pick background and surface based on current theme // Pick background and surface based on current theme
const { isDark } = useTheme(); const { isDark } = useTheme();
const background = isDark ? themeColors.darkBackground : themeColors.lightBackground; const background = isDark ? themeColors.darkBackground : themeColors.lightBackground;
const surface = isDark ? themeColors.darkSurface : themeColors.lightSurface; const surface = isDark ? themeColors.darkSurface : themeColors.lightSurface;
// Initialize WebGL context once (only depends on isEnabled)
useEffect(() => { useEffect(() => {
if (!isEnabled || !containerRef.current) return; if (!isEnabled || !containerRef.current) return;
@@ -235,7 +238,20 @@ export function Aurora() {
rendererRef.current = null; rendererRef.current = null;
programRef.current = null; programRef.current = null;
}; };
}, [isEnabled, themeColors.accent, background, surface]); }, [isEnabled]); // eslint-disable-line react-hooks/exhaustive-deps
// Update color uniforms reactively without recreating WebGL context
useEffect(() => {
if (!programRef.current) return;
const colorStops = generateColorStops(background, surface, themeColors.accent);
const colorStopsArray = colorStops
.map((hex) => {
const c = new Color(hex);
return [c.r, c.g, c.b];
})
.flat();
programRef.current.uniforms.uColorStops.value = colorStopsArray;
}, [themeColors.accent, background, surface]);
if (!isEnabled) { if (!isEnabled) {
return null; return null;