fix: resolve all 14 ESLint warnings across the codebase

- Add varsIgnorePattern to no-unused-vars for destructuring patterns
- Fix all react-hooks/exhaustive-deps by adding missing dependencies
- Refactor useAnimatedNumber to use ref instead of stale state closure
- Wrap handleLinkResult in useCallback for stable deps
- Extract OAuth utilities from OAuthCallback.tsx to utils/oauth.ts
- Extract background config utilities to utils/backgroundConfig.ts
- Remove unused catch parameter in GiftSubscription
This commit is contained in:
c0mrade
2026-03-13 19:48:32 +03:00
parent 2dab25c5a0
commit 62188b8d2e
15 changed files with 262 additions and 241 deletions

View File

@@ -103,7 +103,7 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto
setScriptLoaded(true);
initTelegramLogin();
}
}, [isOIDC, widgetConfig?.oidc_client_id, widgetConfig?.request_access]);
}, [isOIDC, widgetConfig?.oidc_client_id, widgetConfig?.request_access, t]);
// Legacy widget effect (only when NOT OIDC)
const loginWithTelegramWidget = useAuthStore((s) => s.loginWithTelegramWidget);

View File

@@ -2,7 +2,7 @@ import { useState, useCallback, useRef, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { brandingApi } from '@/api/branding';
import { setCachedAnimationConfig } from '@/components/backgrounds/BackgroundRenderer';
import { setCachedAnimationConfig } from '@/utils/backgroundConfig';
import type { AnimationConfig } from '@/components/ui/backgrounds/types';
import { DEFAULT_ANIMATION_CONFIG } from '@/components/ui/backgrounds/types';
import { BackgroundConfigEditor } from './BackgroundConfigEditor';

View File

@@ -5,57 +5,7 @@ import { brandingApi } from '@/api/branding';
import type { AnimationConfig, BackgroundType } from '@/components/ui/backgrounds/types';
import { DEFAULT_ANIMATION_CONFIG } from '@/components/ui/backgrounds/types';
import { backgroundComponents, prefetchBackground } from '@/components/ui/backgrounds/registry';
const ANIMATION_CACHE_KEY = 'cabinet_animation_config';
const MAX_CONFIG_JSON_LENGTH = 10_000;
const VALID_TYPES: ReadonlySet<string> = new Set<BackgroundType>([
'aurora',
'sparkles',
'vortex',
'shooting-stars',
'background-beams',
'background-beams-collision',
'gradient-animation',
'wavy',
'background-lines',
'boxes',
'meteors',
'grid',
'dots',
'spotlight',
'ripple',
'none',
]);
function validateConfig(parsed: unknown): AnimationConfig | null {
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;
const obj = parsed as Record<string, unknown>;
if (typeof obj.enabled !== 'boolean') return null;
if (typeof obj.type !== 'string' || !VALID_TYPES.has(obj.type)) return null;
if (typeof obj.opacity !== 'number') return null;
if (typeof obj.blur !== 'number') return null;
if (obj.settings != null && (typeof obj.settings !== 'object' || Array.isArray(obj.settings)))
return null;
return {
enabled: obj.enabled,
type: obj.type as BackgroundType,
opacity: Math.max(0, Math.min(1, obj.opacity)),
blur: Math.max(0, Math.min(50, obj.blur)),
settings: (obj.settings as Record<string, unknown>) ?? {},
reducedOnMobile: typeof obj.reducedOnMobile === 'boolean' ? obj.reducedOnMobile : true,
};
}
function getCachedConfig(): AnimationConfig | null {
try {
const cached = localStorage.getItem(ANIMATION_CACHE_KEY);
if (!cached || cached.length > MAX_CONFIG_JSON_LENGTH) return null;
return validateConfig(JSON.parse(cached));
} catch {
return null;
}
}
import { validateConfig, getCachedConfig, setCachedConfig } from '@/utils/backgroundConfig';
// Prefetch the background JS chunk immediately based on localStorage cache.
const cachedConfig = getCachedConfig();
@@ -63,17 +13,6 @@ if (cachedConfig?.enabled && cachedConfig.type && cachedConfig.type !== 'none')
prefetchBackground(cachedConfig.type);
}
function setCachedConfig(config: AnimationConfig) {
try {
localStorage.setItem(ANIMATION_CACHE_KEY, JSON.stringify(config));
} catch {}
}
export function setCachedAnimationConfig(config: AnimationConfig) {
const validated = validateConfig(config);
if (validated) setCachedConfig(validated);
}
function reduceMobileSettings(settings: Record<string, unknown>): Record<string, unknown> {
const reduced = { ...settings };
// 75% reduction (divide by 4) instead of 50% — much less GPU work
@@ -94,7 +33,6 @@ function reduceMobileSettings(settings: Record<string, unknown>): Record<string,
return reduced;
}
/** Renders the background animation into a portal at document.body */
function RenderBackground({ config }: { config: AnimationConfig }) {
const prefersReducedMotion = useMemo(
() => window.matchMedia('(prefers-reduced-motion: reduce)').matches,
@@ -136,7 +74,6 @@ function RenderBackground({ config }: { config: AnimationConfig }) {
);
}
/** BackgroundRenderer that fetches config from the branding API (for authenticated app shell) */
export function BackgroundRenderer() {
const { data: config } = useQuery({
queryKey: ['animation-config'],
@@ -155,7 +92,6 @@ export function BackgroundRenderer() {
return <RenderBackground config={effectiveConfig} />;
}
/** StaticBackgroundRenderer that uses a provided config (for public landing pages) */
export function StaticBackgroundRenderer({ config }: { config: AnimationConfig }) {
const validated = useMemo(() => validateConfig(config), [config]);
if (!validated) return null;

View File

@@ -125,7 +125,7 @@ function CollisionMechanism({
}, 2000);
return () => clearTimeout(timer);
}, [collision.detected]);
}, [collision.detected, collision.coordinates]);
return (
<>