From 70e71d7bdb2546804bac57f908b948176aab8795 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Fri, 3 Jul 2026 17:16:22 +0330 Subject: [PATCH] remove hard reload --- src/App.tsx | 26 ++++++++++---------- src/config/navigation.tsx | 30 ++++++++++++++++++++++++ src/config/sessionAuth.ts | 19 +++++++++++++++ src/pages/auth/components/LoginStep2.tsx | 6 +++-- src/shared/SidebarItem.tsx | 21 ++++++++--------- 5 files changed, 77 insertions(+), 25 deletions(-) create mode 100644 src/config/navigation.tsx create mode 100644 src/config/sessionAuth.ts diff --git a/src/App.tsx b/src/App.tsx index 030f1f9..3069fd7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,10 +7,12 @@ import { BrowserRouter } from 'react-router-dom' import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query' import { ToastContainer } from 'react-toastify' import Toast from '@/components/Toast' -import { getRefreshToken, getToken, removeRefreshToken, removeToken, setRefreshToken, setToken } from './config/func' +import { getRefreshToken, setRefreshToken, setToken } from './config/func' import MainRouter from './router/MainRouter' import AuthRouter from './router/AuthRouter' import { Paths } from './config/Paths'; +import { useSessionAuth } from './config/sessionAuth'; +import { appNavigate, AppNavigation } from './config/navigation'; import type { IApiErrorRepsonse } from './shared/types/Types'; import { refreshToken } from './pages/auth/service/AuthService'; @@ -36,9 +38,9 @@ const queryClient = new QueryClient({ // اگر خطا از refresh endpoint است، refresh token منقضی شده if (isRefreshEndpoint) { window.isRefreshTokenExpired = true; - await removeToken(); - await removeRefreshToken(); - window.location.href = Paths.auth.login; + useSessionAuth.getState().clearSession(); + queryClient.clear(); + appNavigate(Paths.auth.login, { replace: true }); return; } @@ -65,9 +67,9 @@ const queryClient = new QueryClient({ if (!refreshTokenValue) { window.isRefreshTokenExpired = true; - await removeToken(); - await removeRefreshToken(); - window.location.href = Paths.auth.login; + useSessionAuth.getState().clearSession(); + queryClient.clear(); + appNavigate(Paths.auth.login, { replace: true }); return; } @@ -106,10 +108,9 @@ const queryClient = new QueryClient({ window.isRefreshTokenExpired = true; } - // Clear tokens and redirect to login - await removeToken(); - await removeRefreshToken(); - window.location.href = Paths.auth.login; + useSessionAuth.getState().clearSession(); + queryClient.clear(); + appNavigate(Paths.auth.login, { replace: true }); } } } @@ -125,11 +126,12 @@ const queryClient = new QueryClient({ const App: FC = () => { - const isLoggedIn = getToken() + const isLoggedIn = useSessionAuth((state) => state.isAuthenticated) return ( + {isLoggedIn ? : } diff --git a/src/config/navigation.tsx b/src/config/navigation.tsx new file mode 100644 index 0000000..c96bbc5 --- /dev/null +++ b/src/config/navigation.tsx @@ -0,0 +1,30 @@ +import { useEffect, type FC } from "react"; +import { useNavigate, type NavigateOptions } from "react-router-dom"; + +let navigateRef: ReturnType | null = null; + +export const setAppNavigate = (navigate: ReturnType) => { + navigateRef = navigate; +}; + +export const appNavigate = (to: string, options?: NavigateOptions) => { + if (navigateRef) { + navigateRef(to, options); + return; + } + + window.location.assign(to); +}; + +export const AppNavigation: FC = () => { + const navigate = useNavigate(); + + useEffect(() => { + setAppNavigate(navigate); + return () => { + navigateRef = null; + }; + }, [navigate]); + + return null; +}; diff --git a/src/config/sessionAuth.ts b/src/config/sessionAuth.ts new file mode 100644 index 0000000..6161ba9 --- /dev/null +++ b/src/config/sessionAuth.ts @@ -0,0 +1,19 @@ +import { create } from "zustand"; +import { getToken, removeRefreshToken, removeToken } from "./func"; + +type SessionAuthStore = { + isAuthenticated: boolean; + setAuthenticated: (value: boolean) => void; + clearSession: () => void; +}; + +export const useSessionAuth = create((set) => ({ + isAuthenticated: !!getToken(), + setAuthenticated: (value) => set({ isAuthenticated: value }), + clearSession: () => { + removeToken(); + removeRefreshToken(); + window.isRefreshTokenExpired = false; + set({ isAuthenticated: false }); + }, +})); diff --git a/src/pages/auth/components/LoginStep2.tsx b/src/pages/auth/components/LoginStep2.tsx index 5fbe573..5ac4d9a 100644 --- a/src/pages/auth/components/LoginStep2.tsx +++ b/src/pages/auth/components/LoginStep2.tsx @@ -12,6 +12,8 @@ import { extractErrorMessage, setRefreshToken } from '../../../config/func' import { setToken } from '../../../config/func' import { toast } from '@/shared/toast' import { Paths } from '@/config/Paths' +import { useSessionAuth } from '@/config/sessionAuth' +import { appNavigate } from '@/config/navigation' const LoginStep2: FC = () => { @@ -40,8 +42,8 @@ const LoginStep2: FC = () => { onSuccess(data) { setToken(data?.data?.tokens?.accessToken?.token) setRefreshToken(data?.data?.tokens?.refreshToken?.token) - // Check if there's a redirect parameter in the URL - window.location.href = Paths.home + useSessionAuth.getState().setAuthenticated(true) + appNavigate(Paths.home, { replace: true }) }, onError(error) { toast(extractErrorMessage(error), 'error') diff --git a/src/shared/SidebarItem.tsx b/src/shared/SidebarItem.tsx index ca2ec1e..44584c7 100644 --- a/src/shared/SidebarItem.tsx +++ b/src/shared/SidebarItem.tsx @@ -1,10 +1,10 @@ -import type { FC, ReactNode } from 'react' +import type { FC, MouseEvent, ReactNode } from 'react' import { Link } from 'react-router-dom' +import { useQueryClient } from '@tanstack/react-query' import { clx } from '../helpers/utils' import { Paths } from '../config/Paths' -// import { useLogout } from '../pages/auth/hooks/useAuthData' -import { removeToken } from '../config/func' -import { removeRefreshToken } from '../config/func' +import { useSessionAuth } from '../config/sessionAuth' +import { appNavigate } from '../config/navigation' import { useGetAdminMe } from '@/pages/admin/hooks/useAdminData' import type { PermissionItemType } from '@/pages/admin/types/Types' @@ -22,14 +22,13 @@ type Props = { const SideBarItem: FC = (props: Props) => { const { data:adminPermissions } = useGetAdminMe(); + const queryClient = useQueryClient() - // const logout = useLogout() - - const handleLogout = async () => { - // await logout.mutateAsync() - removeToken() - removeRefreshToken() - window.location.href = Paths.auth.login + const handleLogout = (event: MouseEvent) => { + event.preventDefault() + useSessionAuth.getState().clearSession() + queryClient.clear() + appNavigate(Paths.auth.login, { replace: true }) } const permissions: PermissionItemType[] = adminPermissions?.data?.role?.permissions ?? [] const hasPermission =