mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
fix(ui): не перезапускать анимированный фон при смене страницы
This commit is contained in:
@@ -31,6 +31,7 @@ import {
|
||||
ServiceUnavailableScreen,
|
||||
} from './components/blocking';
|
||||
import { ErrorBoundary } from './components/ErrorBoundary';
|
||||
import { BackgroundHost } from './components/backgrounds/BackgroundHost';
|
||||
import { PermissionRoute } from '@/components/auth/PermissionRoute';
|
||||
import { saveReturnUrl } from './utils/token';
|
||||
import { useAnalyticsCounters } from './hooks/useAnalyticsCounters';
|
||||
@@ -259,6 +260,8 @@ function App() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Живёт над <Routes>: анимация фона не перезапускается при навигации */}
|
||||
<BackgroundHost />
|
||||
<BlockingOverlay />
|
||||
<Routes>
|
||||
{/* Public routes */}
|
||||
|
||||
45
src/components/backgrounds/BackgroundHost.tsx
Normal file
45
src/components/backgrounds/BackgroundHost.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { create } from 'zustand';
|
||||
import { BackgroundRenderer } from './BackgroundRenderer';
|
||||
|
||||
/**
|
||||
* Держит анимированный фон смонтированным ПОВЕРХ смены роутов.
|
||||
*
|
||||
* Каждый роут оборачивается в собственный <Layout>, поэтому AppShell
|
||||
* перемонтируется при каждой навигации — если рендерить BackgroundRenderer
|
||||
* прямо из AppShell, анимация фона стартует заново на каждой странице.
|
||||
*
|
||||
* Вместо этого AppShell лишь регистрирует своё присутствие через
|
||||
* useBackgroundConsumer(), а BackgroundHost живёт в App (над <Routes>, не
|
||||
* перемонтируется) и держит один экземпляр BackgroundRenderer, пока есть хоть
|
||||
* один потребитель. Страницы без AppShell (логин, QuickPurchase со своим
|
||||
* экземпляром) фон, как и раньше, не получают.
|
||||
*/
|
||||
const useBackgroundConsumers = create<{ count: number }>(() => ({ count: 0 }));
|
||||
|
||||
/** Вызывается из AppShell: «на этом роуте должен быть анимированный фон». */
|
||||
export function useBackgroundConsumer() {
|
||||
useEffect(() => {
|
||||
useBackgroundConsumers.setState((s) => ({ count: s.count + 1 }));
|
||||
return () => useBackgroundConsumers.setState((s) => ({ count: s.count - 1 }));
|
||||
}, []);
|
||||
}
|
||||
|
||||
export function BackgroundHost() {
|
||||
const active = useBackgroundConsumers((s) => s.count > 0);
|
||||
const [mounted, setMounted] = useState(active);
|
||||
|
||||
useEffect(() => {
|
||||
if (active) {
|
||||
setMounted(true);
|
||||
return;
|
||||
}
|
||||
// Грейс-период на случай, когда между размонтированием старого AppShell и
|
||||
// маунтом нового есть зазор (StrictMode, Suspense): без него фон моргал бы
|
||||
// и на таких переходах.
|
||||
const timer = setTimeout(() => setMounted(false), 300);
|
||||
return () => clearTimeout(timer);
|
||||
}, [active]);
|
||||
|
||||
return mounted ? <BackgroundRenderer /> : null;
|
||||
}
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
|
||||
import { MobileBottomNav } from './MobileBottomNav';
|
||||
import { AppHeader } from './AppHeader';
|
||||
import { BackgroundRenderer } from '@/components/backgrounds/BackgroundRenderer';
|
||||
import { useBackgroundConsumer } from '@/components/backgrounds/BackgroundHost';
|
||||
|
||||
interface AppShellProps {
|
||||
children: React.ReactNode;
|
||||
@@ -60,6 +60,9 @@ export function AppShell({ children }: AppShellProps) {
|
||||
const { appName, logoLetter, hasCustomLogo, logoUrl } = useBranding();
|
||||
const { referralEnabled, wheelEnabled, hasContests, hasPolls, giftEnabled } = useFeatureFlags();
|
||||
useScrollRestoration();
|
||||
// Анимированный фон рендерит BackgroundHost в App (не перемонтируется при
|
||||
// смене роута) — здесь только регистрируем, что на этом роуте он нужен.
|
||||
useBackgroundConsumer();
|
||||
|
||||
// Theme toggle visibility
|
||||
const { data: enabledThemes } = useQuery({
|
||||
@@ -180,9 +183,6 @@ export function AppShell({ children }: AppShellProps) {
|
||||
|
||||
return (
|
||||
<div className="min-h-viewport">
|
||||
{/* Animated background renders via portal on document.body at z-index: -1 */}
|
||||
<BackgroundRenderer />
|
||||
|
||||
{/* Global components */}
|
||||
<WebSocketNotifications />
|
||||
<CampaignBonusNotifier />
|
||||
|
||||
Reference in New Issue
Block a user