// SPDX-License-Identifier: AGPL-3.0-or-later import errorFallbackStyles from '@app/features/app/components/ErrorFallback.module.css'; import {NativeTitlebar} from '@app/features/app/components/layout/NativeTitlebar'; import {useNativePlatform} from '@app/features/app/hooks/useNativePlatform'; import AppStorage, {PRESERVED_RESET_STORAGE_KEYS} from '@app/features/platform/state/PersistentStorage'; import {ensureLatestAssets} from '@app/features/platform/types/Versioning'; import {Logger} from '@app/features/platform/utils/AppLogger'; import {Button} from '@app/features/ui/button/Button'; import {copy as copyText} from '@app/features/ui/commands/TextCopyCommands'; import {FluxerIcon} from '@app/features/ui/components/icons/FluxerIcon'; import LayerManager from '@app/features/ui/state/LayerManager'; import {useNativeTitleBar} from '@app/features/window/hooks/useNativeTitleBar'; import {msg} from '@lingui/core/macro'; import {Trans, useLingui} from '@lingui/react/macro'; import type React from 'react'; import {useCallback, useEffect, useState} from 'react'; interface ErrorFallbackProps { error?: Error; reset?: () => void; } const logger = new Logger('ErrorFallback'); const UNKNOWN_ERROR_DESCRIPTOR = msg({ message: 'Unknown error', comment: 'Fallback diagnostic text on the crash screen when an Error object has no message.', }); function getStackTraceText(error: Error | undefined, unknownErrorLabel: string): string | null { if (!error) { return null; } const stack = error.stack?.trim(); if (stack) { return stack; } const name = error.name?.trim() || 'Error'; const message = error.message?.trim() || unknownErrorLabel; return `${name}: ${message}`; } async function cleanupRuntimeStateOnCrash(): Promise { LayerManager.closeAll(); const [{default: GatewayConnection}, {default: MediaEngine}] = await Promise.all([ import('@app/features/gateway/transport/GatewayConnection'), import('@app/features/voice/engine/MediaEngineFacade'), ]); GatewayConnection.logout(); MediaEngine.cleanup(); } export const ErrorFallback: React.FC = ({error}) => { const {i18n} = useLingui(); const {platform, isNative, isMacOS} = useNativePlatform(); const useSystemTitleBar = useNativeTitleBar(); const [updateAvailable, setUpdateAvailable] = useState(false); const [isUpdating, setIsUpdating] = useState(false); const [isCopyingStackTrace, setIsCopyingStackTrace] = useState(false); const [checkingForUpdates, setCheckingForUpdates] = useState(true); const stackTraceText = getStackTraceText(error, i18n._(UNKNOWN_ERROR_DESCRIPTOR)); useEffect(() => { void cleanupRuntimeStateOnCrash().catch((error) => { logger.error('Failed to clean up runtime state on crash screen', error); }); }, []); useEffect(() => { let isMounted = true; const run = async () => { try { const {updateFound} = await ensureLatestAssets({force: true}); if (isMounted) { setUpdateAvailable(updateFound); } } catch (error) { logger.error('Failed to check for updates:', error); } finally { if (isMounted) { setCheckingForUpdates(false); } } }; void run(); return () => { isMounted = false; }; }, []); const handleUpdate = useCallback(async () => { setIsUpdating(true); try { const {updateFound} = await ensureLatestAssets({force: true}); if (!updateFound) { setIsUpdating(false); window.location.reload(); } } catch (error) { logger.error('Failed to apply update:', error); setIsUpdating(false); } }, []); const handleCopyStackTrace = useCallback(async () => { if (!stackTraceText) { return; } setIsCopyingStackTrace(true); try { await copyText(i18n, stackTraceText); } finally { setIsCopyingStackTrace(false); } }, [stackTraceText]); return (
{isNative && !isMacOS && !useSystemTitleBar && ( )}

The app crashed

{checkingForUpdates ? ( The app has crashed. Checking for updates that might fix this issue… ) : updateAvailable ? ( Something went wrong and the app crashed. An update is available that may fix this issue. ) : ( Something went wrong and the app crashed. Try reloading or resetting the app. )}

{stackTraceText && ( )}
); };