"use client" import { Component, useEffect, useState, type ReactNode } from "react" import { ClientThemeShell } from "@/components/suite/client-theme-shell" import { runThemeInit } from "@/components/theme-init-script" import { FirstLaunchSplash } from "@/components/first-launch-splash" import { QueryProvider } from "@/lib/api/query-provider" import { AuthProvider } from "@/components/auth/auth-provider" import { SessionGuard } from "@/components/auth/session-guard" import { NativeBridgeProvider } from "@/components/mobile/native-bridge-provider" import { NativeAuthGate } from "@/components/mobile/native-auth-gate" import { NativeShellChrome } from "@/components/mobile/native-shell-chrome" import { MailToaster } from "@/components/gmail/mail-toaster" function MobileBootScreen({ hint }: { hint?: string }) { return (

UltiMail

{hint ? (

{hint}

) : null}
) } class MobileErrorBoundary extends Component< { children: ReactNode }, { error: Error | null } > { state = { error: null as Error | null } static getDerivedStateFromError(error: Error) { return { error } } render() { if (this.state.error) { return ( ) } return this.props.children } } /** * Tauri mobile root. SSR renders a static boot screen; the interactive app * mounts on the client so Android WebView never hydrates complex SSR trees. */ export function MobileLayoutRoot({ children }: { children: ReactNode }) { const [clientReady, setClientReady] = useState(false) const [bootHint, setBootHint] = useState() useEffect(() => { setClientReady(true) }, []) useEffect(() => { if (!clientReady) return runThemeInit() }, [clientReady]) useEffect(() => { if (clientReady) return const timer = window.setTimeout(() => { setBootHint("Chargement JavaScript…") }, 8_000) return () => window.clearTimeout(timer) }, [clientReady]) if (!clientReady) { return } return ( {children} ) }