diff --git a/apps/remix/app/entry.client.tsx b/apps/remix/app/entry.client.tsx index a60ac5063..42996b5ec 100644 --- a/apps/remix/app/entry.client.tsx +++ b/apps/remix/app/entry.client.tsx @@ -5,10 +5,17 @@ import { detect, fromHtmlTag } from '@lingui/detect-locale'; import { I18nProvider } from '@lingui/react'; import { hydrateRoot } from 'react-dom/client'; import { HydratedRouter } from 'react-router/dom'; +import { Theme, ThemeProvider } from 'remix-themes'; +import { match } from 'ts-pattern'; import { dynamicActivate } from '@documenso/lib/utils/i18n'; async function main() { + const theme = match(document.documentElement.getAttribute('data-theme')) + .with('dark', () => Theme.DARK) + .with('light', () => Theme.LIGHT) + .otherwise(() => null); + const locale = detect(fromHtmlTag('lang')) || 'en'; await dynamicActivate(locale); @@ -18,7 +25,9 @@ async function main() { document, - + + + , ); diff --git a/apps/remix/app/entry.server.tsx b/apps/remix/app/entry.server.tsx index 6caeba746..6250bff85 100644 --- a/apps/remix/app/entry.server.tsx +++ b/apps/remix/app/entry.server.tsx @@ -7,11 +7,13 @@ import type { RenderToPipeableStreamOptions } from 'react-dom/server'; import { renderToPipeableStream } from 'react-dom/server'; import type { AppLoadContext, EntryContext } from 'react-router'; import { ServerRouter } from 'react-router'; +import { ThemeProvider } from 'remix-themes'; import { APP_I18N_OPTIONS } from '@documenso/lib/constants/i18n'; import { dynamicActivate, extractLocaleData } from '@documenso/lib/utils/i18n'; import { langCookie } from './storage/lang-cookie.server'; +import { themeSessionResolver } from './storage/theme-session.server'; export const streamTimeout = 5_000; @@ -30,6 +32,10 @@ export default async function handleRequest( await dynamicActivate(language); + const { getTheme } = await themeSessionResolver(request); + + const theme = getTheme(); + return new Promise((resolve, reject) => { let shellRendered = false; const userAgent = request.headers.get('user-agent'); @@ -41,7 +47,9 @@ export default async function handleRequest( const { pipe, abort } = renderToPipeableStream( - + + + , { [readyOption]() { diff --git a/apps/remix/app/root.tsx b/apps/remix/app/root.tsx index 2f1fc07d2..a741ee00a 100644 --- a/apps/remix/app/root.tsx +++ b/apps/remix/app/root.tsx @@ -12,7 +12,7 @@ import { useLoaderData, useLocation, } from 'react-router'; -import { PreventFlashOnWrongTheme, ThemeProvider, useTheme } from 'remix-themes'; +import { PreventFlashOnWrongTheme, useTheme } from 'remix-themes'; import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session'; import { SessionProvider } from '@documenso/lib/client-only/providers/session'; @@ -105,13 +105,21 @@ export async function loader({ request }: Route.LoaderArgs) { ); } -export function App() { +export function Layout({ children }: { children: React.ReactNode }) { const { publicEnv, lang, session, ...data } = useLoaderData() || {}; const [theme] = useTheme(); + const location = useLocation(); + + useEffect(() => { + if (env('NODE_ENV') === 'production') { + trackPageview(); + } + }, [location.pathname]); + return ( - + @@ -136,7 +144,8 @@ export function App() { - + {children} + @@ -157,27 +166,8 @@ export function App() { ); } -/** - * We have this weird setup with: - * - No root layout - * - AppWithTheme - * - * To handle remix-themes. - */ -export default function AppWithTheme({ loaderData }: Route.ComponentProps) { - const location = useLocation(); - - useEffect(() => { - if (env('NODE_ENV') === 'production') { - trackPageview(); - } - }, [location.pathname]); - - return ( - - - - ); +export default function App() { + return ; } export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { diff --git a/packages/lib/utils/env.ts b/packages/lib/utils/env.ts index 0a9179c5a..26f90f0cc 100644 --- a/packages/lib/utils/env.ts +++ b/packages/lib/utils/env.ts @@ -13,7 +13,7 @@ export const env = (variable: EnvironmentVariable | (string & object)): string | return window.__ENV__[variable]; } - return process?.env?.[variable]; + return typeof process !== 'undefined' ? process?.env?.[variable] : undefined; }; export const createPublicEnv = () =>