From 59a251cb8c706b3029990e58fc6003ce620f80d3 Mon Sep 17 00:00:00 2001 From: c0mrade Date: Wed, 4 Feb 2026 16:00:05 +0300 Subject: [PATCH] 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. --- src/components/layout/AppShell/Aurora.tsx | 32 +++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/components/layout/AppShell/Aurora.tsx b/src/components/layout/AppShell/Aurora.tsx index 31e9578..415f7d5 100644 --- a/src/components/layout/AppShell/Aurora.tsx +++ b/src/components/layout/AppShell/Aurora.tsx @@ -1,9 +1,10 @@ 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 { brandingApi } from '@/api/branding'; +import { themeColorsApi } from '@/api/themeColors'; 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 in vec2 position; @@ -132,8 +133,6 @@ export function Aurora() { const rendererRef = useRef(null); const programRef = useRef(null); - const queryClient = useQueryClient(); - // Fetch animation setting const { data: animationSetting } = useQuery({ queryKey: ['animation-enabled'], @@ -143,15 +142,19 @@ export function Aurora() { const isEnabled = animationSetting?.enabled ?? false; - // Get theme colors from cache (already fetched by ThemeColorsProvider) - const themeColors = - queryClient.getQueryData(['theme-colors']) || DEFAULT_THEME_COLORS; + // Subscribe reactively to theme-colors cache so Aurora re-renders on setQueryData + const { data: themeColors = DEFAULT_THEME_COLORS } = useQuery({ + queryKey: ['theme-colors'], + queryFn: themeColorsApi.getColors, + staleTime: 5 * 60 * 1000, + }); // Pick background and surface based on current theme const { isDark } = useTheme(); const background = isDark ? themeColors.darkBackground : themeColors.lightBackground; const surface = isDark ? themeColors.darkSurface : themeColors.lightSurface; + // Initialize WebGL context once (only depends on isEnabled) useEffect(() => { if (!isEnabled || !containerRef.current) return; @@ -235,7 +238,20 @@ export function Aurora() { rendererRef.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) { return null;