Spaces:
Build error
Build error
| 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 ( | |
| <div> | |
| <h1>{error.status}</h1> | |
| <p>{error.statusText}</p> | |
| <pre> | |
| {error.data instanceof Object | |
| ? JSON.stringify(error.data) | |
| : error.data} | |
| </pre> | |
| </div> | |
| ); | |
| } | |
| if (error instanceof Error) { | |
| return ( | |
| <div> | |
| <h1>{t(I18nKey.ERROR$GENERIC)}</h1> | |
| <pre>{error.message}</pre> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div> | |
| <h1>{t(I18nKey.ERROR$UNKNOWN)}</h1> | |
| </div> | |
| ); | |
| } | |
| 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 ( | |
| <div | |
| data-testid="root-layout" | |
| className="bg-base p-3 h-screen md:min-w-[1024px] flex flex-col md:flex-row gap-3" | |
| > | |
| <Sidebar /> | |
| <div | |
| id="root-outlet" | |
| className="h-[calc(100%-50px)] md:h-full w-full relative overflow-auto" | |
| > | |
| <EmailVerificationGuard> | |
| <Outlet /> | |
| </EmailVerificationGuard> | |
| </div> | |
| {renderAuthModal && ( | |
| <AuthModal | |
| githubAuthUrl={effectiveGitHubAuthUrl} | |
| appMode={config.data?.APP_MODE} | |
| /> | |
| )} | |
| {renderReAuthModal && <ReauthModal />} | |
| {config.data?.APP_MODE === "oss" && consentFormIsOpen && ( | |
| <AnalyticsConsentFormModal | |
| onClose={() => { | |
| setConsentFormIsOpen(false); | |
| }} | |
| /> | |
| )} | |
| {config.data?.FEATURE_FLAGS.ENABLE_BILLING && | |
| config.data?.APP_MODE === "saas" && | |
| settings?.IS_NEW_USER && <SetupPaymentModal />} | |
| </div> | |
| ); | |
| } | |