// SPDX-License-Identifier: AGPL-3.0-or-later import i18n, {initI18n} from '@app/app/I18n'; import styles from '@app/features/app/components/layout/AuthLayout.module.css'; import {CorruptedInstallationNagbar} from '@app/features/app/components/layout/app_layout/nagbars/CorruptedInstallationNagbar'; import {NativeDragRegion} from '@app/features/app/components/layout/NativeDragRegion'; import {NativeTitlebar} from '@app/features/app/components/layout/NativeTitlebar'; import {useNativePlatform} from '@app/features/app/hooks/useNativePlatform'; import {useSetLayoutVariant} from '@app/features/app/state/LayoutVariantContext'; import {AuthBackground} from '@app/features/auth/flow/AuthBackground'; import {AuthCardContainer} from '@app/features/auth/flow/AuthCardContainer'; import {useAuthBackground} from '@app/features/auth/hooks/useAuthBackground'; import {type AuthCardVariant, AuthLayoutContext} from '@app/features/auth/state/AuthLayoutContext'; import {AuthRegisterDraftContext, type AuthRegisterFormDraft} from '@app/features/auth/state/AuthRegisterDraftContext'; import {useLocation} from '@app/features/platform/components/router/RouterReact'; import {FluxerWordmark} from '@app/features/ui/components/icons/FluxerWordmark'; import {Scroller, type ScrollerHandle} from '@app/features/ui/components/Scroller'; import {isMobileExperienceEnabled} from '@app/features/ui/utils/MobileExperience'; import {hasUnavailableElectronNativeContext} from '@app/features/ui/utils/NativeUtils'; import {useNativeTitleBar} from '@app/features/window/hooks/useNativeTitleBar'; import foodPatternUrl from '@app/media/images/i-like-food.svg'; import type {GuildSplashCardAlignmentValue} from '@fluxer/constants/src/GuildConstants'; import {GuildSplashCardAlignment} from '@fluxer/constants/src/GuildConstants'; import {I18nProvider} from '@lingui/react'; import clsx from 'clsx'; import {observer} from 'mobx-react-lite'; import {type ReactNode, useCallback, useEffect, useMemo, useRef, useState} from 'react'; const AuthLayoutContent = observer(function AuthLayoutContent({children}: {children?: ReactNode}) { const [viewportHeight, setViewportHeight] = useState(() => window.innerHeight); const [viewportWidth, setViewportWidth] = useState(() => window.innerWidth); const [splashUrl, setSplashUrl] = useState(null); const [showLogoSide, setShowLogoSide] = useState(true); const [cardVariant, setCardVariant] = useState('default'); const [splashAlignment, setSplashAlignment] = useState( GuildSplashCardAlignment.CENTER, ); const {isNative, platform} = useNativePlatform(); const useSystemTitleBar = useNativeTitleBar(); const splashUrlRef = useRef(null); const registerFormDraftsRef = useRef>(new Map()); const scrollerRef = useRef(null); const location = useLocation(); const {patternReady, splashLoaded, splashDimensions} = useAuthBackground(splashUrl, foodPatternUrl); const handleSetSplashUrl = useCallback( (url: string | null) => { if (splashUrlRef.current === url) return; splashUrlRef.current = url; setSplashUrl(url); if (!url) { setSplashAlignment(GuildSplashCardAlignment.CENTER); } }, [setSplashAlignment], ); useEffect(() => { const handleResize = () => { setViewportWidth(window.innerWidth); setViewportHeight(window.innerHeight); }; handleResize(); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); useEffect(() => { document.documentElement.classList.add('auth-page'); return () => { document.documentElement.classList.remove('auth-page'); }; }, []); useEffect(() => { scrollerRef.current?.scrollToTop(); }, [location.pathname]); const splashScale = useMemo(() => { if (!splashDimensions) return null; const {width, height} = splashDimensions; if (width <= 0 || height <= 0) return null; const heightScale = viewportHeight / height; const widthScale = viewportWidth / width; return Math.max(heightScale, widthScale); }, [splashDimensions, viewportHeight, viewportWidth]); const getRegisterFormDraft = useCallback((draftKey: string): AuthRegisterFormDraft | undefined => { const draft = registerFormDraftsRef.current.get(draftKey); if (!draft) { return undefined; } return { ...draft, formValues: {...draft.formValues}, }; }, []); const setRegisterFormDraft = useCallback((draftKey: string, draft: AuthRegisterFormDraft) => { registerFormDraftsRef.current.set(draftKey, { ...draft, formValues: {...draft.formValues}, }); }, []); const clearRegisterFormDraft = useCallback((draftKey: string) => { registerFormDraftsRef.current.delete(draftKey); }, []); const authLayoutContextValue = useMemo( () => ({ setSplashUrl: handleSetSplashUrl, setShowLogoSide, setCardVariant, setSplashCardAlignment: setSplashAlignment, }), [handleSetSplashUrl], ); const authRegisterDraftContextValue = useMemo( () => ({ getRegisterFormDraft, setRegisterFormDraft, clearRegisterFormDraft, }), [clearRegisterFormDraft, getRegisterFormDraft, setRegisterFormDraft], ); const isMobileExperience = isMobileExperienceEnabled(); const showCorruptedInstallationNagbar = hasUnavailableElectronNativeContext(); if (isMobileExperience) { return ( {showCorruptedInstallationNagbar && (
)}
{children}
); } return ( {showCorruptedInstallationNagbar && (
)}
{isNative && !useSystemTitleBar && ( )}
{children}
); }); export const AuthLayout = observer(function AuthLayout({children}: {children?: ReactNode}) { const [isI18nInitialized, setIsI18nInitialized] = useState(false); const setLayoutVariant = useSetLayoutVariant(); useEffect(() => { setLayoutVariant('auth'); return () => { setLayoutVariant('app'); }; }, [setLayoutVariant]); useEffect(() => { initI18n().then(() => { setIsI18nInitialized(true); }); }, []); if (!isI18nInitialized) { return null; } return ( {children} ); });