/// import type { QueryClient } from "@tanstack/react-query"; import { HeadContent, Scripts, createRootRouteWithContext, useRouter, useRouterState } from "@tanstack/react-router"; import * as React from "react"; import { DefaultCatchBoundary } from "~/components/DefaultCatchBoundary"; import { NotFound } from "~/components/NotFound"; import { SiteHeader } from "~/components/SiteHeader"; import { SiteFooter } from "~/components/SiteFooter"; import { HovercardLayer } from "~/components/HovercardLayer"; import { AccountNotices } from "~/components/AccountNotices"; import { UpsellDialog } from "~/components/billing/UpsellDialog"; import { DisabledAccountNotice } from "~/components/DisabledAccountNotice"; import { reachableWhileDisabled } from "~/lib/account-disable"; import { getSession } from "~/fns/auth"; import appCss from "~/styles/app.css?url"; import { seo } from "~/utils/seo"; import { SITE_DESCRIPTION } from "~/lib/og"; import { capturePageview, identifyViewer, initAnalytics, onNavigate } from "~/lib/analytics"; export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({ beforeLoad: async () => { const session = await getSession(); return { viewer: session.viewer, supporter: session.supporter, unreadNotifications: session.unreadNotifications, analytics: session.analytics, disabled: session.disabled, publicUrl: session.publicUrl, }; }, head: ({ match }) => ({ meta: [ { charSet: "utf-8" }, { name: "viewport", content: "width=device-width, initial-scale=1" }, { name: "theme-color", content: "#0a0a0a" }, ...seo({ origin: match.context?.publicUrl, title: "gitfor ยท Open-source Git hosting, code review and CI", description: SITE_DESCRIPTION, }), ], links: [ { rel: "stylesheet", href: appCss }, { rel: "icon", href: "/favicon.svg", type: "image/svg+xml" }, { rel: "icon", href: "/favicon.ico", sizes: "32x32" }, { rel: "apple-touch-icon", href: "/apple-touch-icon.png" }, { rel: "manifest", href: "/site.webmanifest" }, ], }), errorComponent: DefaultCatchBoundary, notFoundComponent: () => , shellComponent: RootDocument, }); /** Starts optional analytics once, keeps the identified account in sync and pauses replay before navigation. */ function useAnalytics() { const { analytics, viewer } = Route.useRouteContext(); const router = useRouter(); React.useEffect(() => { void initAnalytics(analytics).then(() => identifyViewer(analytics, viewer)); }, [analytics, viewer]); React.useEffect( () => router.subscribe("onBeforeNavigate", (event) => onNavigate(event.toLocation.pathname)), [router], ); React.useEffect(() => router.subscribe("onResolved", () => capturePageview()), [router]); } function RootDocument({ children }: { children: React.ReactNode }) { const { disabled } = Route.useRouteContext(); const pathname = useRouterState({ select: (state) => state.location.pathname }); const blocked = disabled !== null && !reachableWhileDisabled(pathname); React.useEffect(() => { if (import.meta.env.DEV) void import("react-grab"); }, []); useAnalytics(); return (
{blocked && disabled ? : children}
); }