import React from "react"; import { useRouteError, isRouteErrorResponse, Outlet, useNavigate, useLocation, } from "react-router"; import { useTranslation } from "react-i18next"; import { I18nKey } from "#/i18n/declaration"; import i18n from "#/i18n"; import { useGitHubAuthUrl } from "#/hooks/use-github-auth-url"; import { useIsAuthed } from "#/hooks/query/use-is-authed"; import { useConfig } from "#/hooks/query/use-config"; import { Sidebar } from "#/components/features/sidebar/sidebar"; import { AuthModal } from "#/components/features/waitlist/auth-modal"; import { ReauthModal } from "#/components/features/waitlist/reauth-modal"; import { AnalyticsConsentFormModal } from "#/components/features/analytics/analytics-consent-form-modal"; import { useSettings } from "#/hooks/query/use-settings"; import { useMigrateUserConsent } from "#/hooks/use-migrate-user-consent"; import { useBalance } from "#/hooks/query/use-balance"; import { SetupPaymentModal } from "#/components/features/payment/setup-payment-modal"; import { displaySuccessToast } from "#/utils/custom-toast-handlers"; import { useIsOnTosPage } from "#/hooks/use-is-on-tos-page"; import { useAutoLogin } from "#/hooks/use-auto-login"; import { useAuthCallback } from "#/hooks/use-auth-callback"; import { LOCAL_STORAGE_KEYS } from "#/utils/local-storage"; import { EmailVerificationGuard } from "#/components/features/guards/email-verification-guard"; export function ErrorBoundary() { const error = useRouteError(); const { t } = useTranslation(); if (isRouteErrorResponse(error)) { return (

{error.status}

{error.statusText}

          {error.data instanceof Object
            ? JSON.stringify(error.data)
            : error.data}
        
); } if (error instanceof Error) { return (

{t(I18nKey.ERROR$GENERIC)}

{error.message}
); } return (

{t(I18nKey.ERROR$UNKNOWN)}

); } export default function MainApp() { const navigate = useNavigate(); const { pathname } = useLocation(); const isOnTosPage = useIsOnTosPage(); const { data: settings } = useSettings(); const { error } = useBalance(); const { migrateUserConsent } = useMigrateUserConsent(); const { t } = useTranslation(); const config = useConfig(); const { data: isAuthed, isFetching: isFetchingAuth, isError: isAuthError, } = useIsAuthed(); // Always call the hook, but we'll only use the result when not on TOS page const gitHubAuthUrl = useGitHubAuthUrl({ appMode: config.data?.APP_MODE || null, gitHubClientId: config.data?.GITHUB_CLIENT_ID || null, }); // When on TOS page, we don't use the GitHub auth URL const effectiveGitHubAuthUrl = isOnTosPage ? null : gitHubAuthUrl; const [consentFormIsOpen, setConsentFormIsOpen] = React.useState(false); // Auto-login if login method is stored in local storage useAutoLogin(); // Handle authentication callback and set login method after successful authentication useAuthCallback(); React.useEffect(() => { // Don't change language when on TOS page if (!isOnTosPage && settings?.LANGUAGE) { i18n.changeLanguage(settings.LANGUAGE); } }, [settings?.LANGUAGE, isOnTosPage]); React.useEffect(() => { // Don't show consent form when on TOS page if (!isOnTosPage) { const consentFormModalIsOpen = settings?.USER_CONSENTS_TO_ANALYTICS === null; setConsentFormIsOpen(consentFormModalIsOpen); } }, [settings, isOnTosPage]); React.useEffect(() => { // Don't migrate user consent when on TOS page if (!isOnTosPage) { // Migrate user consent to the server if it was previously stored in localStorage migrateUserConsent({ handleAnalyticsWasPresentInLocalStorage: () => { setConsentFormIsOpen(false); }, }); } }, [isOnTosPage]); React.useEffect(() => { if (settings?.IS_NEW_USER && config.data?.APP_MODE === "saas") { displaySuccessToast(t(I18nKey.BILLING$YOURE_IN)); } }, [settings?.IS_NEW_USER, config.data?.APP_MODE]); React.useEffect(() => { // Don't do any redirects when on TOS page // Don't allow users to use the app if it 402s if (!isOnTosPage && error?.status === 402 && pathname !== "/") { navigate("/"); } }, [error?.status, pathname, isOnTosPage]); // Function to check if login method exists in local storage const checkLoginMethodExists = React.useCallback(() => { // Only check localStorage if we're in a browser environment if (typeof window !== "undefined" && window.localStorage) { return localStorage.getItem(LOCAL_STORAGE_KEYS.LOGIN_METHOD) !== null; } return false; }, []); // State to track if login method exists const [loginMethodExists, setLoginMethodExists] = React.useState( checkLoginMethodExists(), ); // Listen for storage events to update loginMethodExists when logout happens React.useEffect(() => { const handleStorageChange = (event: StorageEvent) => { if (event.key === LOCAL_STORAGE_KEYS.LOGIN_METHOD) { setLoginMethodExists(checkLoginMethodExists()); } }; // Also check on window focus, as logout might happen in another tab const handleWindowFocus = () => { setLoginMethodExists(checkLoginMethodExists()); }; window.addEventListener("storage", handleStorageChange); window.addEventListener("focus", handleWindowFocus); return () => { window.removeEventListener("storage", handleStorageChange); window.removeEventListener("focus", handleWindowFocus); }; }, [checkLoginMethodExists]); // Check login method status when auth status changes React.useEffect(() => { // When auth status changes (especially on logout), recheck login method setLoginMethodExists(checkLoginMethodExists()); }, [isAuthed, checkLoginMethodExists]); const renderAuthModal = !isAuthed && !isAuthError && !isFetchingAuth && !isOnTosPage && config.data?.APP_MODE === "saas" && !loginMethodExists; // Don't show auth modal if login method exists in local storage const renderReAuthModal = !isAuthed && !isAuthError && !isFetchingAuth && !isOnTosPage && config.data?.APP_MODE === "saas" && loginMethodExists; return (
{renderAuthModal && ( )} {renderReAuthModal && } {config.data?.APP_MODE === "oss" && consentFormIsOpen && ( { setConsentFormIsOpen(false); }} /> )} {config.data?.FEATURE_FLAGS.ENABLE_BILLING && config.data?.APP_MODE === "saas" && settings?.IS_NEW_USER && }
); }