"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}
)
}