mirror of
https://github.com/hanzoai/sign.git
synced 2026-10-21 01:21:51 +00:00
Emails, the signing page's title, the sealed PDF's reason, the sign-in refusal and the dialogs still said Hanzo Sign. The catalogs change with the strings, message for message, so every translation keeps its id.
177 lines
5.6 KiB
TypeScript
177 lines
5.6 KiB
TypeScript
import {
|
|
Links,
|
|
Meta,
|
|
Outlet,
|
|
Scripts,
|
|
ScrollRestoration,
|
|
data,
|
|
isRouteErrorResponse,
|
|
useLoaderData,
|
|
} from 'react-router';
|
|
import { PreventFlashOnWrongTheme, ThemeProvider, useTheme } from 'remix-themes';
|
|
|
|
import { getOptionalSession } from '@hanzo/esign-auth/server/lib/utils/get-session';
|
|
import { SessionProvider } from '@hanzo/esign-lib/client-only/providers/session';
|
|
import { APP_I18N_OPTIONS, type SupportedLanguageCodes } from '@hanzo/esign-lib/constants/i18n';
|
|
import { createPublicEnv } from '@hanzo/esign-lib/utils/env';
|
|
import { extractLocaleData } from '@hanzo/esign-lib/utils/i18n';
|
|
import { getOrganisationSession } from '@hanzo/esign-trpc/server/organisation-router/get-organisation-session';
|
|
import { ZapProvider } from '@hanzo/esign-trpc/zap/react';
|
|
import { Toaster } from '@hanzo/esign-ui/primitives/toaster';
|
|
import { TooltipProvider } from '@hanzo/esign-ui/primitives/tooltip';
|
|
|
|
import type { Route } from './+types/root';
|
|
import stylesheet from './app.css?url';
|
|
import { GenericErrorLayout } from './components/general/generic-error-layout';
|
|
import { langCookie } from './storage/lang-cookie.server';
|
|
import { themeSessionResolver } from './storage/theme-session.server';
|
|
import { appMetaTags } from './utils/meta';
|
|
|
|
export const links: Route.LinksFunction = () => [{ rel: 'stylesheet', href: stylesheet }];
|
|
|
|
export function meta() {
|
|
return appMetaTags();
|
|
}
|
|
|
|
/**
|
|
* Don't revalidate (run the loader on sequential navigations) on the root layout
|
|
*
|
|
* Update values via providers.
|
|
*/
|
|
export const shouldRevalidate = () => false;
|
|
|
|
export async function loader({ request }: Route.LoaderArgs) {
|
|
const session = await getOptionalSession(request);
|
|
|
|
const { getTheme } = await themeSessionResolver(request);
|
|
|
|
const cookieHeader = request.headers.get('cookie') ?? '';
|
|
|
|
let lang: SupportedLanguageCodes = await langCookie.parse(cookieHeader);
|
|
|
|
if (!APP_I18N_OPTIONS.supportedLangs.includes(lang)) {
|
|
lang = extractLocaleData({ headers: request.headers }).lang;
|
|
}
|
|
|
|
const disableAnimations = cookieHeader.includes('__disable_animations=true');
|
|
|
|
let organisations = null;
|
|
|
|
if (session.isAuthenticated) {
|
|
organisations = await getOrganisationSession({ userId: session.user.id });
|
|
}
|
|
|
|
return data(
|
|
{
|
|
lang,
|
|
theme: getTheme(),
|
|
disableAnimations,
|
|
session: session.isAuthenticated
|
|
? {
|
|
user: session.user,
|
|
session: session.session,
|
|
organisations: organisations || [],
|
|
}
|
|
: null,
|
|
publicEnv: createPublicEnv(),
|
|
},
|
|
{
|
|
headers: {
|
|
'Set-Cookie': await langCookie.serialize(lang),
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function Layout({ children }: { children: React.ReactNode }) {
|
|
const { theme } = useLoaderData<typeof loader>() || {};
|
|
|
|
return (
|
|
<ThemeProvider specifiedTheme={theme} themeAction="/v1/theme">
|
|
<LayoutContent>{children}</LayoutContent>
|
|
</ThemeProvider>
|
|
);
|
|
}
|
|
|
|
export function LayoutContent({ children }: { children: React.ReactNode }) {
|
|
const { publicEnv, session, lang, disableAnimations, ...data } =
|
|
useLoaderData<typeof loader>() || {};
|
|
|
|
const [theme] = useTheme();
|
|
|
|
return (
|
|
<html translate="no" lang={lang} data-theme={theme} className={theme ?? ''}>
|
|
<head>
|
|
<meta charSet="utf-8" />
|
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<link rel="manifest" href="/site.webmanifest" />
|
|
<meta name="google" content="notranslate" />
|
|
<Meta />
|
|
<Links />
|
|
<meta name="google" content="notranslate" />
|
|
<PreventFlashOnWrongTheme ssrTheme={Boolean(data.theme)} />
|
|
|
|
{disableAnimations && (
|
|
<style
|
|
dangerouslySetInnerHTML={{
|
|
__html: `*, *::before, *::after { animation: none !important; transition: none !important; }`,
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
{/* Fix: https://stackoverflow.com/questions/21147149/flash-of-unstyled-content-fouc-in-firefox-only-is-ff-slow-renderer */}
|
|
<script>0</script>
|
|
</head>
|
|
<body>
|
|
{/* Global license banner currently disabled. Need to wait until after a few releases. */}
|
|
{/* {licenseStatus === '?' && (
|
|
<div className="bg-destructive text-destructive-foreground">
|
|
<div className="mx-auto flex h-auto max-w-screen-xl items-center justify-center px-4 py-3 text-sm font-medium">
|
|
<div className="flex items-center">
|
|
<AlertTriangleIcon className="mr-2 h-4 w-4" />
|
|
<Trans>This is an expired license instance of Hanzo eSign</Trans>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)} */}
|
|
|
|
<SessionProvider initialSession={session}>
|
|
<TooltipProvider>
|
|
<ZapProvider>
|
|
{children}
|
|
|
|
<Toaster />
|
|
</ZapProvider>
|
|
</TooltipProvider>
|
|
</SessionProvider>
|
|
|
|
<ScrollRestoration />
|
|
<Scripts />
|
|
|
|
<script
|
|
dangerouslySetInnerHTML={{
|
|
__html: `window.__ENV__ = ${JSON.stringify(publicEnv)}`,
|
|
}}
|
|
/>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|
|
|
|
export default function App() {
|
|
return <Outlet />;
|
|
}
|
|
|
|
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
|
|
const errorCode = isRouteErrorResponse(error) ? error.status : 500;
|
|
|
|
if (errorCode !== 404) {
|
|
console.error('[RootErrorBoundary]', error);
|
|
}
|
|
|
|
return <GenericErrorLayout errorCode={errorCode} />;
|
|
}
|