import { PropsWithChildren, useEffect, useState } from 'react'; import { ErrorBoundary, ErrorBoundaryPropsWithComponent, FallbackProps } from 'react-error-boundary'; import { useRouteError } from 'react-router'; import { useDebugState } from '@sd/client'; import { Button, Dialogs } from '@sd/ui'; import { showAlertDialog } from './components'; import { useOperatingSystem, useTheme } from './hooks'; import { usePlatform } from './util/Platform'; const sentryBrowserLazy = import('@sentry/browser'); const RENDERING_ERROR_LOCAL_STORAGE_KEY = 'was-rendering-error'; export function RouterErrorBoundary() { const error = useRouteError(); const reloadBtn = () => { location.reload(); localStorage.setItem(RENDERING_ERROR_LOCAL_STORAGE_KEY, 'true'); }; return ( { sentryBrowserLazy.then(({ captureException }) => captureException(error)); reloadBtn(); }} reloadBtn={reloadBtn} /> ); } export default ({ error, resetErrorBoundary }: FallbackProps) => ( { sentryBrowserLazy.then(({ captureException }) => captureException(error)); resetErrorBoundary(); }} reloadBtn={resetErrorBoundary} /> ); // This is sketchy but these are all edge cases that will only be encountered by developers if everything works as expected so it's probs fine const errorsThatRequireACoreReset = [ 'failed to initialize config', 'failed to initialize library manager: failed to run library migrations', 'failed to initialize config: We detected a Spacedrive config from a super early version of the app!', 'failed to initialize library manager: failed to run library migrations: YourAppIsOutdated - the config file is for a newer version of the app. Please update to the latest version to load it!' ]; export function ErrorPage({ reloadBtn, sendReportBtn, message, submessage }: { reloadBtn?: () => void; sendReportBtn?: () => void; message: string; submessage?: string; }) { useTheme(); const debug = useDebugState(); const os = useOperatingSystem(); const platform = usePlatform(); const isMacOS = os === 'macOS'; const [redirecting, _] = useState(() => localStorage.getItem(RENDERING_ERROR_LOCAL_STORAGE_KEY) ); // If the user is on a page and the user presses "Reset" on the error boundary, it may crash in rendering causing the user to get stuck on the error page. // If it crashes again, we redirect them instead of infinitely crashing. useEffect(() => { if (localStorage.getItem(RENDERING_ERROR_LOCAL_STORAGE_KEY) !== null) { localStorage.removeItem(RENDERING_ERROR_LOCAL_STORAGE_KEY); window.location.pathname = '/'; console.error( 'Hit error boundary after reloading. Redirecting to overview screen!', redirecting ); } }); if (redirecting) return null; // To stop flash of error boundary after `localStorage` is reset in the first render and the check above starts being `false` const resetHandler = () => { showAlertDialog({ title: 'Reset', value: 'Are you sure you want to reset Spacedrive? Your database will be deleted.', label: 'Confirm', cancelBtn: true, onSubmit: () => { localStorage.clear(); // @ts-expect-error window.__TAURI_INVOKE__('reset_spacedrive'); } }); }; if (!submessage && debug.enabled) submessage = 'Check the console (CMD/CTRL + OPTION/SHIFT + i) for stack trace.'; return (

APP CRASHED

We're past the event horizon...

				{message}
			
{submessage &&
{submessage}
}
{reloadBtn && ( )} {platform.openLogsDir && ( )} {(errorsThatRequireACoreReset.includes(message) || message.startsWith('NodeError::FailedToInitializeConfig') || message.startsWith('failed to initialize library manager')) && (

We detected you may have created your library with an older version of Spacedrive. Please reset it to continue using the app!

{' '} YOU WILL LOSE ANY EXISTING SPACEDRIVE DATA!

)}
); } export const BetterErrorBoundary = ({ children, FallbackComponent, ...props }: PropsWithChildren) => { useEffect(() => { const id = setTimeout( () => localStorage.removeItem(RENDERING_ERROR_LOCAL_STORAGE_KEY), 1000 ); return () => clearTimeout(id); }, []); return ( {children} ); };