import { FC, useEffect, useState } from 'react' import './assets/fonts/irancell/style.css' import 'react-quill-new/dist/quill.snow.css'; import 'react-loading-skeleton/dist/skeleton.css' import 'swiper/swiper-bundle.css'; import { BrowserRouter } from 'react-router-dom' import Main from './shared/Main' import i18next from 'i18next' import FaJson from './langs/fa.json' import { I18nextProvider } from 'react-i18next' import NewMessage from './components/newMessage/NewMessage'; import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { IApiErrorRepsonse } from './types/error.types'; import ToastContainer, { toast } from './components/Toast'; import { getRefreshToken, getToken, removeRefreshToken, removeToken, setRefreshToken, setToken } from './config/func'; import { refreshToken } from './pages/auth/service/AuthService'; import { Paths } from './utils/Paths'; import Login from './pages/auth/Login'; import { EmailWebSocketProvider } from './contexts/EmailWebSocketContext'; declare global { interface Window { isRefreshingToken?: boolean; } } i18next.init({ interpolation: { escapeValue: false }, lng: 'fa', defaultNS: 'global', resources: { fa: { global: FaJson } } }) const queryClient = new QueryClient({ queryCache: new QueryCache({ onError: async (error: IApiErrorRepsonse) => { // نمایش پیغام خطا از API const errorMessage = error?.response?.error?.message?.[0] || undefined; if (errorMessage) { toast(errorMessage, 'error'); } if (error?.response?.status === 401) { try { // Use a flag to track if refresh is in progress if (window.isRefreshingToken) { // Wait for the refresh to complete await new Promise(resolve => { const checkComplete = setInterval(() => { if (!window.isRefreshingToken) { clearInterval(checkComplete); resolve(true); } }, 100); }); return; } // Set the flag to indicate refresh is in progress window.isRefreshingToken = true; try { const refreshTokenValue = await getRefreshToken(); const { data } = await refreshToken({ refreshToken: refreshTokenValue || '' }); if (data?.accessToken?.token) { // Save the new token await setToken(data?.accessToken?.token); // If refresh token also returned, update it if (data.refreshToken?.token) { await setRefreshToken(data.refreshToken?.token); } // Retry the failed request queryClient.invalidateQueries(); return; } else { throw new Error('Invalid token response'); } } finally { window.isRefreshingToken = false; } } catch (refreshError: unknown) { console.error('Token refresh failed:', refreshError); // Clear tokens and redirect to login await removeToken(); await removeRefreshToken(); window.location.href = '/auth/login'; } } } }), defaultOptions: { queries: { refetchOnWindowFocus: false, retry: false // staleTime: 86400000 // 1 day in milliseconds } } }); const App: FC = () => { const [isLogin, setIsLogin] = useState<'checking' | 'isLogin' | 'isNotLogin'>('checking') const [userToken, setUserToken] = useState('') useEffect(() => { const checkLogin = async () => { const token = await getToken(); if (token) { setIsLogin('isLogin') setUserToken(token) } else { setIsLogin('isNotLogin') if (window.location.href.split('auth').length === 1) { window.location.href = Paths.login } } } checkLogin(); }, []); return ( {isLogin === 'isLogin' && userToken ? (
) : isLogin === 'isNotLogin' ? ( ) : null} ) } export default App