refactor: full codebase cleanup, dependency updates, and lint fixes

Phase 0: Remove ~920 lines of dead code (ThemeBentoPicker, PromoDiscountBadge,
AdminLayout, SettingsSidebar, MovingGradient, EmptyState, miniapp API, unused
types/functions/transitions/skeleton helpers). Fix API barrel file (add 20 missing exports).

Phase 1: Add ErrorBoundary (app/page/widget levels), centralize constants,
add axios timeout, fix staleTime:0 in React Query.

Phase 2: Consolidate hexToHsl, extract email validation, fix duplicate code.

Phase 3: Fix auth race condition (await checkAdminStatus), memoize useCurrency,
add WebSocket message validation.

Phase 4: Extract useBranding, useFeatureFlags, useScrollRestoration from AppShell.

Phase 5: Remove all eslint-disable react-hooks/exhaustive-deps (14 total),
simplify logger, remove deprecated hooks (useBackButton, useTelegramDnd,
useTelegramWebApp).

Dependencies: React 19, react-router 7, zustand 5, i18next 25, react-i18next 16,
eslint-plugin-react-refresh 0.5. Remove unused @lottiefiles/dotlottie-react.
Convert vite manualChunks to function-based approach for react-router v7 compat.

Lint: Fix logger.ts no-unused-expressions, fix react-refresh/only-export-components
in 6 Radix primitive files (const re-exports → direct re-exports), fix
WebSocketProvider exhaustive-deps. Result: 0 errors, 0 warnings.

Add CLAUDE.md to .gitignore.
This commit is contained in:
c0mrade
2026-02-06 01:35:12 +03:00
parent c5cad20a6f
commit 562ab7abf7
118 changed files with 1243 additions and 2746 deletions

View File

@@ -1,6 +1,7 @@
import { useEffect, useRef, useState, useCallback } from 'react';
import { useAuthStore } from '../store/auth';
import { WebSocketContext, type MessageHandler, type WSMessage } from './WebSocketContext';
import { WS } from '../config/constants';
// Re-export for backward compatibility
export type { WSMessage } from './WebSocketContext';
@@ -14,7 +15,7 @@ export function WebSocketProvider({ children }: { children: React.ReactNode }) {
const pingIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
const [isConnected, setIsConnected] = useState(false);
const reconnectAttemptsRef = useRef(0);
const maxReconnectAttempts = 5;
const maxReconnectAttempts = WS.MAX_RECONNECT_ATTEMPTS;
// Store message handlers
const handlersRef = useRef<Set<MessageHandler>>(new Set());
@@ -76,12 +77,17 @@ export function WebSocketProvider({ children }: { children: React.ReactNode }) {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }));
}
}, 25000);
}, WS.PING_INTERVAL_MS);
};
ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data) as WSMessage;
const parsed = JSON.parse(event.data);
if (!parsed || typeof parsed !== 'object' || typeof parsed.type !== 'string') {
if (isDev) console.warn('[WS] Invalid message format:', parsed);
return;
}
const message = parsed as WSMessage;
// Ignore pong messages
if (message.type === 'pong' || message.type === 'connected') {
@@ -112,7 +118,10 @@ export function WebSocketProvider({ children }: { children: React.ReactNode }) {
// Attempt to reconnect if not closed intentionally
if (event.code !== 1000 && reconnectAttemptsRef.current < maxReconnectAttempts) {
const delay = Math.min(1000 * Math.pow(2, reconnectAttemptsRef.current), 30000);
const delay = Math.min(
1000 * Math.pow(2, reconnectAttemptsRef.current),
WS.MAX_RECONNECT_DELAY_MS,
);
if (isDev)
console.log(
`[WS] Reconnecting in ${delay}ms (attempt ${reconnectAttemptsRef.current + 1})`,
@@ -131,7 +140,7 @@ export function WebSocketProvider({ children }: { children: React.ReactNode }) {
} catch (e) {
if (isDev) console.error('[WS] Failed to connect:', e);
}
}, [accessToken, isAuthenticated, cleanup]);
}, [accessToken, isAuthenticated, cleanup, maxReconnectAttempts]);
// Connect when authenticated
useEffect(() => {