58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import { Suspense, useEffect, useState } from "react";
|
|
import { Outlet, useLocation } from "react-router-dom";
|
|
import { useQueryClient } from "@tanstack/react-query";
|
|
import { SessionTimeoutWarning } from "./components/SessionTimeoutWarning";
|
|
import NavBar from "./components/NavBar";
|
|
import { useAuthSession } from "./hooks/useAuthSession";
|
|
import { http } from "./api/http";
|
|
import UpdateNoticeModal from "./components/UpdateNoticeModal";
|
|
|
|
export default function App() {
|
|
const location = useLocation();
|
|
const qc = useQueryClient();
|
|
const session = useAuthSession({ retry: false });
|
|
const [dismissedVersion, setDismissedVersion] = useState<number | null>(null);
|
|
|
|
const notice = session.data?.updateNotice;
|
|
const isPublicRoute =
|
|
location.pathname.startsWith("/login") ||
|
|
location.pathname.startsWith("/register") ||
|
|
location.pathname.startsWith("/verify") ||
|
|
location.pathname.startsWith("/beta");
|
|
|
|
const showUpdateModal = !isPublicRoute && !!notice && dismissedVersion !== notice.version;
|
|
|
|
useEffect(() => {
|
|
setDismissedVersion(null);
|
|
}, [session.data?.userId]);
|
|
|
|
const acknowledgeNotice = async () => {
|
|
if (!notice) return;
|
|
await http("/app/update-notice/ack", {
|
|
method: "POST",
|
|
body: { version: notice.version },
|
|
});
|
|
setDismissedVersion(notice.version);
|
|
await qc.invalidateQueries({ queryKey: ["auth", "session"] });
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<SessionTimeoutWarning />
|
|
<NavBar />
|
|
<main className="container py-6 h-full">
|
|
<Suspense fallback={<div className="muted text-sm">Loading…</div>}>
|
|
<Outlet />
|
|
</Suspense>
|
|
</main>
|
|
{showUpdateModal && notice ? (
|
|
<UpdateNoticeModal
|
|
title={notice.title}
|
|
body={notice.body}
|
|
onAcknowledge={acknowledgeNotice}
|
|
/>
|
|
) : null}
|
|
</>
|
|
);
|
|
}
|