///
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}
);
}