clash-verge-rev/src/main.tsx
Tunglies a73fafaf9f
refactor: migrate SWR to TanStack Query v5 (#6713)
Replace swr with @tanstack/react-query v5 across all hooks, providers,
and components. Introduce singleton QueryClient, WS subscription pattern
via useQuery+useEffect, and enforce component-layer cache access contract.
2026-04-03 08:15:51 +00:00

110 lines
3.2 KiB
TypeScript

import './assets/styles/index.scss'
import './services/monaco'
import { ResizeObserver } from '@juggle/resize-observer'
import { QueryClientProvider } from '@tanstack/react-query'
import { ComposeContextProvider } from 'foxact/compose-context-provider'
import React from 'react'
import { createRoot } from 'react-dom/client'
import { RouterProvider } from 'react-router'
import { MihomoWebSocket } from 'tauri-plugin-mihomo-api'
import { BaseErrorBoundary } from './components/base'
import { router } from './pages/_routers'
import { AppDataProvider } from './providers/app-data-provider'
import { WindowProvider } from './providers/window'
import { FALLBACK_LANGUAGE, initializeLanguage } from './services/i18n'
import {
preloadAppData,
resolveThemeMode,
getPreloadConfig,
} from './services/preload'
import { queryClient } from './services/query-client'
import {
LoadingCacheProvider,
ThemeModeProvider,
UpdateStateProvider,
} from './services/states'
import { disableWebViewShortcuts } from './utils/disable-webview-shortcuts'
if (!window.ResizeObserver) {
window.ResizeObserver = ResizeObserver
}
const mainElementId = 'root'
const container = document.getElementById(mainElementId)
if (!container) {
throw new Error(`No container '${mainElementId}' found to render application`)
}
disableWebViewShortcuts()
const initializeApp = (initialThemeMode: 'light' | 'dark') => {
const contexts = [
<ThemeModeProvider key="theme" initialState={initialThemeMode} />,
<LoadingCacheProvider key="loading" />,
<UpdateStateProvider key="update" />,
]
const root = createRoot(container)
root.render(
<React.StrictMode>
<ComposeContextProvider contexts={contexts}>
<BaseErrorBoundary>
<QueryClientProvider client={queryClient}>
<WindowProvider>
<AppDataProvider>
<RouterProvider router={router} />
</AppDataProvider>
</WindowProvider>
</QueryClientProvider>
</BaseErrorBoundary>
</ComposeContextProvider>
</React.StrictMode>,
)
}
const bootstrap = async () => {
const { initialThemeMode } = await preloadAppData()
initializeApp(initialThemeMode)
}
bootstrap().catch((error) => {
console.error(
'[main.tsx] App bootstrap failed, falling back to default language:',
error,
)
initializeLanguage(FALLBACK_LANGUAGE)
.catch((fallbackError) => {
console.error(
'[main.tsx] Fallback language initialization failed:',
fallbackError,
)
})
.finally(() => {
initializeApp(resolveThemeMode(getPreloadConfig()))
})
})
// Error handling
window.addEventListener('error', (event) => {
console.error('[main.tsx] Global error:', event.error)
})
window.addEventListener('unhandledrejection', (event) => {
console.error('[main.tsx] Unhandled promise rejection:', event.reason)
})
// Page close/refresh events
window.addEventListener('beforeunload', () => {
// Clean up all WebSocket instances to prevent memory leaks
MihomoWebSocket.cleanupAll()
})
// Page loaded event
window.addEventListener('DOMContentLoaded', () => {
// Clean up all WebSocket instances to prevent memory leaks
MihomoWebSocket.cleanupAll()
})