/** * Platform detection shared by web and the Tauri mobile/native builds. * * - `IS_MOBILE_BUILD` is a *build-time* flag (set by `NEXT_PUBLIC_MOBILE=1`) * used to gate the static export, drop server API routes, and switch auth to * the native flow. It is statically analyzable so dead code is tree-shaken. * - `isTauriRuntime()` is a *runtime* check (the same static bundle can be * loaded by a browser during development, e.g. `pnpm dev`, even when built in * mobile mode). */ /** True when the bundle was built for the Tauri native shells. */ export const IS_MOBILE_BUILD = process.env.NEXT_PUBLIC_MOBILE === "1" /** * The per-app product this build targets (UltiMail, UltiDrive, …). Drives the * default start route, the deep-link scheme, and which sibling apps the suite * launcher tries to open. Defaults to `mail` (the pilot). */ export type SuiteApp = "mail" | "drive" | "agenda" | "meet" | "chat" | "contacts" export const SUITE_APP: SuiteApp = (process.env.NEXT_PUBLIC_SUITE_APP as SuiteApp | undefined) ?? "mail" /** Custom URL scheme for this app's deep links, e.g. `ultimail://`. */ export function appScheme(app: SuiteApp = SUITE_APP): string { return `ulti${app}` } /** Default start route for an app shell. */ export function appStartRoute(app: SuiteApp = SUITE_APP): string { switch (app) { case "mail": return "/mail" case "drive": return "/drive" case "agenda": return "/agenda" case "meet": return "/meet" case "chat": return "/chat" case "contacts": return "/contacts" default: return "/mail" } } /** * Map an in-app route to the suite app that "owns" it, so the launcher can open * the sibling native app instead of navigating in-webview. Returns null for * routes that should stay in the current app (settings, account, admin, …). */ export function suiteAppForRoute(route: string): SuiteApp | null { const path = route.split("?")[0] if (path.startsWith("/mail")) return "mail" if (path.startsWith("/drive")) return "drive" if (path.startsWith("/agenda")) return "agenda" if (path.startsWith("/meet")) return "agenda" // UltiCal+UltiMeet share one app if (path.startsWith("/chat")) return "chat" if (path.startsWith("/contacts")) return "contacts" return null } /** True only when actually executing inside a Tauri webview. */ export function isTauriRuntime(): boolean { if (typeof window === "undefined") return false return ( "__TAURI_INTERNALS__" in window || "__TAURI__" in window || // Tauri v2 exposes this in some configurations. (window as { isTauri?: boolean }).isTauri === true ) } /** * True when we should use the native auth + runtime-config code paths. * Mobile builds always use native paths; a mobile build opened in a plain * browser (dev) also uses them so the flow can be exercised without a device. */ export function useNativeRuntime(): boolean { return IS_MOBILE_BUILD || isTauriRuntime() }