/// import { createRootRouteWithContext, HeadContent, Link, Outlet, Scripts, useRouterState, } from '@tanstack/react-router' import { createServerFn } from '@tanstack/react-start' import { Compass } from 'lucide-react' import { useEffect, useRef, useState } from 'react' import { appMeta, DARK_THEME_COLOR, LIGHT_THEME_COLOR } from '#app/config/app-meta' import { MAIL_HOME_PATH } from '#app/config/route-paths' import { INITIAL_ROOT_CLASS_NAME } from '#app/config/theme' import type { OwnmailRouterContext } from '#app/query/query-provider' import { platform } from '#server/platform' import { DEFAULT_SITE_NAME, siteNameFromEnv } from '#server/site-config' import appCss from '../styles.css?url' const rootState = createServerFn({ method: 'GET' }).handler(async () => { const { env } = await platform() return { siteName: siteNameFromEnv(env) } }) export const Route = createRootRouteWithContext()({ loader: async () => rootState(), staleTime: Number.POSITIVE_INFINITY, head: (context) => { const siteName = context?.loaderData?.siteName ?? DEFAULT_SITE_NAME const { title, description } = appMeta(siteName) return { meta: [ { charSet: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1, viewport-fit=cover' }, { name: 'description', content: description }, { name: 'color-scheme', content: 'light dark' }, { name: 'mobile-web-app-capable', content: 'yes' }, { name: 'apple-mobile-web-app-capable', content: 'yes' }, { name: 'apple-mobile-web-app-title', content: siteName }, { name: 'apple-mobile-web-app-status-bar-style', content: 'default' }, { title }, ], links: [ { rel: 'stylesheet', href: appCss }, { rel: 'manifest', href: '/manifest.webmanifest' }, { rel: 'apple-touch-icon', href: '/apple-touch-icon.png' }, ], } }, component: RootComponent, errorComponent: AppError, notFoundComponent: NotFoundComponent, }) function AppError() { return ( We couldn’t load this page. Check your connection and try again. If it persists, sign in again. window.location.reload()} className="inline-flex min-h-11 items-center justify-center rounded-lg border border-border px-4 py-2 text-sm font-medium outline-none transition-colors hover:bg-muted focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring forced-colors:focus-visible:outline-2 forced-colors:focus-visible:outline-offset-2 forced-colors:focus-visible:outline-solid" > Retry Sign in again ) } function NotFoundComponent() { return ( Page not found The page you’re looking for doesn’t exist or has moved. Back to mail ) } function RootComponent() { return ( ) } function RouteAnnouncer() { const navigationPending = useRouterState({ select: (state) => state.isLoading }) const pathname = useRouterState({ select: (state) => state.location.pathname }) const settledPathname = useRef(pathname) const [announcement, setAnnouncement] = useState('') useEffect(() => { if (navigationPending || pathname === settledPathname.current) return settledPathname.current = pathname setAnnouncement(`${routeLabel(pathname)} loaded`) }, [navigationPending, pathname]) return ( {announcement} ) } function routeLabel(pathname: string): string { if (pathname.startsWith('/calendar')) return 'Calendar' if (pathname.startsWith('/contacts')) return 'Contacts' if (pathname.startsWith('/settings')) return 'Settings' return 'Mail' } function NavigationProgress() { const navigationPending = useRouterState({ select: (state) => state.isLoading }) const [visible, setVisible] = useState(false) const visibleSince = useRef(null) useEffect(() => { let timer: ReturnType | undefined if (navigationPending) { if (!visible) { timer = setTimeout(() => { visibleSince.current = Date.now() setVisible(true) }, 150) } } else if (visible) { // `visible` is only set in the same callback that records this timestamp. const elapsed = Date.now() - (visibleSince.current as number) timer = setTimeout( () => { visibleSince.current = null setVisible(false) }, Math.max(0, 300 - elapsed), ) } return () => { if (timer !== undefined) clearTimeout(timer) } }, [navigationPending, visible]) if (!visible) return null return ( ) }
We couldn’t load this page.
Check your connection and try again. If it persists, sign in again.
Page not found
The page you’re looking for doesn’t exist or has moved.