Refactor App component to implement lazy loading for user and admin pages, enhancing performance and reducing initial load time. Introduce LazyPage wrapper for suspense handling during component loading.

This commit is contained in:
PEDZEO
2026-01-19 01:51:52 +03:00
parent 3fc22d2b4d
commit 5f123a39d9
3 changed files with 98 additions and 61 deletions

View File

@@ -1,39 +1,46 @@
import { lazy, Suspense } from 'react'
import { Routes, Route, Navigate, useLocation } from 'react-router-dom'
import { useAuthStore } from './store/auth'
import Layout from './components/layout/Layout'
import PageLoader from './components/common/PageLoader'
import { saveReturnUrl } from './utils/token'
// Auth pages - load immediately (small)
import Login from './pages/Login'
import TelegramCallback from './pages/TelegramCallback'
import TelegramRedirect from './pages/TelegramRedirect'
import DeepLinkRedirect from './pages/DeepLinkRedirect'
import Dashboard from './pages/Dashboard'
import Subscription from './pages/Subscription'
import Balance from './pages/Balance'
import Referral from './pages/Referral'
import Support from './pages/Support'
import Profile from './pages/Profile'
import AdminTickets from './pages/AdminTickets'
import AdminSettings from './pages/AdminSettings'
import AdminApps from './pages/AdminApps'
import VerifyEmail from './pages/VerifyEmail'
import Contests from './pages/Contests'
import Polls from './pages/Polls'
import Info from './pages/Info'
import Wheel from './pages/Wheel'
import AdminWheel from './pages/AdminWheel'
import AdminTariffs from './pages/AdminTariffs'
import AdminServers from './pages/AdminServers'
import AdminPanel from './pages/AdminPanel'
import AdminDashboard from './pages/AdminDashboard'
import AdminBanSystem from './pages/AdminBanSystem'
import AdminBroadcasts from './pages/AdminBroadcasts'
import AdminPromocodes from './pages/AdminPromocodes'
import AdminCampaigns from './pages/AdminCampaigns'
import AdminUsers from './pages/AdminUsers'
import AdminPayments from './pages/AdminPayments'
import AdminPromoOffers from './pages/AdminPromoOffers'
import AdminRemnawave from './pages/AdminRemnawave'
// User pages - lazy load
const Dashboard = lazy(() => import('./pages/Dashboard'))
const Subscription = lazy(() => import('./pages/Subscription'))
const Balance = lazy(() => import('./pages/Balance'))
const Referral = lazy(() => import('./pages/Referral'))
const Support = lazy(() => import('./pages/Support'))
const Profile = lazy(() => import('./pages/Profile'))
const Contests = lazy(() => import('./pages/Contests'))
const Polls = lazy(() => import('./pages/Polls'))
const Info = lazy(() => import('./pages/Info'))
const Wheel = lazy(() => import('./pages/Wheel'))
// Admin pages - lazy load (only for admins)
const AdminPanel = lazy(() => import('./pages/AdminPanel'))
const AdminTickets = lazy(() => import('./pages/AdminTickets'))
const AdminSettings = lazy(() => import('./pages/AdminSettings'))
const AdminApps = lazy(() => import('./pages/AdminApps'))
const AdminWheel = lazy(() => import('./pages/AdminWheel'))
const AdminTariffs = lazy(() => import('./pages/AdminTariffs'))
const AdminServers = lazy(() => import('./pages/AdminServers'))
const AdminDashboard = lazy(() => import('./pages/AdminDashboard'))
const AdminBanSystem = lazy(() => import('./pages/AdminBanSystem'))
const AdminBroadcasts = lazy(() => import('./pages/AdminBroadcasts'))
const AdminPromocodes = lazy(() => import('./pages/AdminPromocodes'))
const AdminCampaigns = lazy(() => import('./pages/AdminCampaigns'))
const AdminUsers = lazy(() => import('./pages/AdminUsers'))
const AdminPayments = lazy(() => import('./pages/AdminPayments'))
const AdminPromoOffers = lazy(() => import('./pages/AdminPromoOffers'))
const AdminRemnawave = lazy(() => import('./pages/AdminRemnawave'))
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { isAuthenticated, isLoading } = useAuthStore()
@@ -73,6 +80,15 @@ function AdminRoute({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
// Suspense wrapper for lazy components
function LazyPage({ children }: { children: React.ReactNode }) {
return (
<Suspense fallback={<PageLoader variant="dark" />}>
{children}
</Suspense>
)
}
function App() {
return (
<Routes>
@@ -90,7 +106,7 @@ function App() {
path="/"
element={
<ProtectedRoute>
<Dashboard />
<LazyPage><Dashboard /></LazyPage>
</ProtectedRoute>
}
/>
@@ -98,7 +114,7 @@ function App() {
path="/subscription"
element={
<ProtectedRoute>
<Subscription />
<LazyPage><Subscription /></LazyPage>
</ProtectedRoute>
}
/>
@@ -106,7 +122,7 @@ function App() {
path="/balance"
element={
<ProtectedRoute>
<Balance />
<LazyPage><Balance /></LazyPage>
</ProtectedRoute>
}
/>
@@ -114,7 +130,7 @@ function App() {
path="/referral"
element={
<ProtectedRoute>
<Referral />
<LazyPage><Referral /></LazyPage>
</ProtectedRoute>
}
/>
@@ -122,7 +138,7 @@ function App() {
path="/support"
element={
<ProtectedRoute>
<Support />
<LazyPage><Support /></LazyPage>
</ProtectedRoute>
}
/>
@@ -130,7 +146,7 @@ function App() {
path="/profile"
element={
<ProtectedRoute>
<Profile />
<LazyPage><Profile /></LazyPage>
</ProtectedRoute>
}
/>
@@ -138,7 +154,7 @@ function App() {
path="/contests"
element={
<ProtectedRoute>
<Contests />
<LazyPage><Contests /></LazyPage>
</ProtectedRoute>
}
/>
@@ -146,7 +162,7 @@ function App() {
path="/polls"
element={
<ProtectedRoute>
<Polls />
<LazyPage><Polls /></LazyPage>
</ProtectedRoute>
}
/>
@@ -154,7 +170,7 @@ function App() {
path="/info"
element={
<ProtectedRoute>
<Info />
<LazyPage><Info /></LazyPage>
</ProtectedRoute>
}
/>
@@ -162,7 +178,7 @@ function App() {
path="/wheel"
element={
<ProtectedRoute>
<Wheel />
<LazyPage><Wheel /></LazyPage>
</ProtectedRoute>
}
/>
@@ -172,7 +188,7 @@ function App() {
path="/admin"
element={
<AdminRoute>
<AdminPanel />
<LazyPage><AdminPanel /></LazyPage>
</AdminRoute>
}
/>
@@ -180,7 +196,7 @@ function App() {
path="/admin/tickets"
element={
<AdminRoute>
<AdminTickets />
<LazyPage><AdminTickets /></LazyPage>
</AdminRoute>
}
/>
@@ -188,7 +204,7 @@ function App() {
path="/admin/settings"
element={
<AdminRoute>
<AdminSettings />
<LazyPage><AdminSettings /></LazyPage>
</AdminRoute>
}
/>
@@ -196,7 +212,7 @@ function App() {
path="/admin/apps"
element={
<AdminRoute>
<AdminApps />
<LazyPage><AdminApps /></LazyPage>
</AdminRoute>
}
/>
@@ -204,7 +220,7 @@ function App() {
path="/admin/wheel"
element={
<AdminRoute>
<AdminWheel />
<LazyPage><AdminWheel /></LazyPage>
</AdminRoute>
}
/>
@@ -212,7 +228,7 @@ function App() {
path="/admin/tariffs"
element={
<AdminRoute>
<AdminTariffs />
<LazyPage><AdminTariffs /></LazyPage>
</AdminRoute>
}
/>
@@ -220,7 +236,7 @@ function App() {
path="/admin/servers"
element={
<AdminRoute>
<AdminServers />
<LazyPage><AdminServers /></LazyPage>
</AdminRoute>
}
/>
@@ -228,7 +244,7 @@ function App() {
path="/admin/dashboard"
element={
<AdminRoute>
<AdminDashboard />
<LazyPage><AdminDashboard /></LazyPage>
</AdminRoute>
}
/>
@@ -236,7 +252,7 @@ function App() {
path="/admin/ban-system"
element={
<AdminRoute>
<AdminBanSystem />
<LazyPage><AdminBanSystem /></LazyPage>
</AdminRoute>
}
/>
@@ -244,7 +260,7 @@ function App() {
path="/admin/broadcasts"
element={
<AdminRoute>
<AdminBroadcasts />
<LazyPage><AdminBroadcasts /></LazyPage>
</AdminRoute>
}
/>
@@ -252,7 +268,7 @@ function App() {
path="/admin/promocodes"
element={
<AdminRoute>
<AdminPromocodes />
<LazyPage><AdminPromocodes /></LazyPage>
</AdminRoute>
}
/>
@@ -260,7 +276,7 @@ function App() {
path="/admin/campaigns"
element={
<AdminRoute>
<AdminCampaigns />
<LazyPage><AdminCampaigns /></LazyPage>
</AdminRoute>
}
/>
@@ -268,7 +284,7 @@ function App() {
path="/admin/users"
element={
<AdminRoute>
<AdminUsers />
<LazyPage><AdminUsers /></LazyPage>
</AdminRoute>
}
/>
@@ -276,7 +292,7 @@ function App() {
path="/admin/payments"
element={
<AdminRoute>
<AdminPayments />
<LazyPage><AdminPayments /></LazyPage>
</AdminRoute>
}
/>
@@ -284,7 +300,7 @@ function App() {
path="/admin/promo-offers"
element={
<AdminRoute>
<AdminPromoOffers />
<LazyPage><AdminPromoOffers /></LazyPage>
</AdminRoute>
}
/>
@@ -292,7 +308,7 @@ function App() {
path="/admin/remnawave"
element={
<AdminRoute>
<AdminRemnawave />
<LazyPage><AdminRemnawave /></LazyPage>
</AdminRoute>
}
/>

View File

@@ -1,4 +1,4 @@
import { createContext, useContext, useState, useCallback, ReactNode } from 'react'
import { createContext, useContext, useState, useCallback, useRef, useEffect, ReactNode } from 'react'
interface ToastOptions {
type?: 'success' | 'error' | 'info' | 'warning'
@@ -29,22 +29,41 @@ export function useToast() {
export function ToastProvider({ children }: { children: ReactNode }) {
const [toasts, setToasts] = useState<Toast[]>([])
const timersRef = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map())
const showToast = useCallback((options: ToastOptions) => {
const id = Date.now()
const id = Date.now() + Math.random() // Avoid ID collision
const toast: Toast = { id, duration: 5000, type: 'info', ...options }
setToasts(prev => [...prev, toast])
setTimeout(() => {
const timer = setTimeout(() => {
setToasts(prev => prev.filter(t => t.id !== id))
timersRef.current.delete(id)
}, toast.duration)
timersRef.current.set(id, timer)
}, [])
const removeToast = useCallback((id: number) => {
// Clear timer when manually removing
const timer = timersRef.current.get(id)
if (timer) {
clearTimeout(timer)
timersRef.current.delete(id)
}
setToasts(prev => prev.filter(t => t.id !== id))
}, [])
// Cleanup all timers on unmount
useEffect(() => {
const timers = timersRef.current
return () => {
timers.forEach(timer => clearTimeout(timer))
timers.clear()
}
}, [])
return (
<ToastContext.Provider value={{ showToast }}>
{children}

View File

@@ -1,6 +1,8 @@
import { useEffect, useRef, useCallback, useState } from 'react'
import { useAuthStore } from '../store/auth'
const isDev = import.meta.env.DEV
export interface WSMessage {
type: string
ticket_id?: number
@@ -80,7 +82,7 @@ export function useWebSocket(options: UseWebSocketOptions = {}) {
wsRef.current = ws
ws.onopen = () => {
console.log('[WS] Connected')
if (isDev) console.log('[WS] Connected')
setIsConnected(true)
reconnectAttemptsRef.current = 0
optionsRef.current.onConnect?.()
@@ -104,12 +106,12 @@ export function useWebSocket(options: UseWebSocketOptions = {}) {
optionsRef.current.onMessage?.(message)
} catch (e) {
console.error('[WS] Failed to parse message:', e)
if (isDev) console.error('[WS] Failed to parse message:', e)
}
}
ws.onclose = (event) => {
console.log('[WS] Disconnected:', event.code, event.reason)
if (isDev) console.log('[WS] Disconnected:', event.code, event.reason)
setIsConnected(false)
optionsRef.current.onDisconnect?.()
@@ -121,7 +123,7 @@ export function useWebSocket(options: UseWebSocketOptions = {}) {
// 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)
console.log(`[WS] Reconnecting in ${delay}ms (attempt ${reconnectAttemptsRef.current + 1})`)
if (isDev) console.log(`[WS] Reconnecting in ${delay}ms (attempt ${reconnectAttemptsRef.current + 1})`)
reconnectTimeoutRef.current = setTimeout(() => {
reconnectAttemptsRef.current++
@@ -131,10 +133,10 @@ export function useWebSocket(options: UseWebSocketOptions = {}) {
}
ws.onerror = (error) => {
console.error('[WS] Error:', error)
if (isDev) console.error('[WS] Error:', error)
}
} catch (e) {
console.error('[WS] Failed to connect:', e)
if (isDev) console.error('[WS] Failed to connect:', e)
}
}, [accessToken, isAuthenticated, cleanup])