Files
lislgosms/src/layouts/PortalSessionBoundary.tsx
T

57 lines
1.7 KiB
TypeScript

import { useEffect, useRef, useState, type ReactNode } from 'react';
import { Navigate, useLocation } from 'react-router-dom';
import { portalSessionApi } from '@/api/adminApi';
import {
clearSession,
readSession,
saveSessionRecovery,
writeSession,
type LoginSession,
type Portal,
} from '@/api/session';
type PortalSessionBoundaryProps = {
portal: Portal;
children(session: LoginSession): ReactNode;
};
export function PortalSessionBoundary({ portal, children }: PortalSessionBoundaryProps) {
const location = useLocation();
const targetRoute = useRef(`${location.pathname}${location.search}${location.hash}`);
const [state, setState] = useState<{ checking: boolean; session: LoginSession | null }>({
checking: true,
session: readSession(portal),
});
useEffect(() => {
let active = true;
setState((current) => ({ ...current, checking: true }));
portalSessionApi.current(portal)
.then((session) => {
if (!active) return;
writeSession(session);
setState({ checking: false, session });
})
.catch((error: unknown) => {
if (!active) return;
clearSession(portal);
saveSessionRecovery(portal, {
returnUrl: targetRoute.current,
message: error instanceof Error ? error.message : '登录会话已失效,请重新登录',
});
setState({ checking: false, session: null });
});
return () => { active = false; };
}, [portal]);
if (state.checking) {
return (
<main aria-busy="true" aria-live="polite" className="page-loading-state">
<p>正在恢复登录会话…</p>
</main>
);
}
if (!state.session) return <Navigate to={`/${portal}/login`} replace />;
return children(state.session);
}