import { joinClassNames, sleep } from '@decky/ui'; import { FunctionComponent, useEffect, useReducer, useState } from 'react'; import { disablePlugin, uninstallPlugin } from '../plugin'; import { VerInfo, doRestart, doShutdown } from '../updater'; import { ValveReactErrorInfo, getLikelyErrorSourceFromValveReactError } from '../utils/errors'; import { useSetting } from '../utils/hooks/useSetting'; import { UpdateBranch } from './settings/pages/general/BranchSelect'; interface DeckyErrorBoundaryProps { error: ValveReactErrorInfo; errorKey: string; identifier: string; reset: () => void; } declare global { interface Window { SystemNetworkStore?: any; } } const classes = { root: 'deckyErrorBoundary', likelyOccurred: 'likely-occured-msg', panel: 'panel-section', panelHeader: 'panel-header', trace: 'trace', rowList: 'row-list', rowItem: 'row-item', buttonDescRow: 'button-description-row', flexRowWGap: 'flex-row', marginBottom: 'margin-bottom', swipePrompt: 'swipe-prompt', }; const vars = { scrollBarwidth: '18px', rootMarginLeft: '15px', panelXPadding: '20px', }; export const startSSH = DeckyBackend.callable('utilities/start_ssh'); export const starrCEFForwarding = DeckyBackend.callable('utilities/allow_remote_debugging'); function ipToString(ip: number) { return [(ip >>> 24) & 255, (ip >>> 16) & 255, (ip >>> 8) & 255, (ip >>> 0) & 255].join('.'); } // Intentionally not localized since we can't really trust React here const DeckyErrorBoundary: FunctionComponent = ({ error, identifier, reset }) => { const [actionLog, addLogLine] = useReducer((log: string, line: string) => (log += '\n' + line), ''); const [actionsEnabled, setActionsEnabled] = useState(true); const [debugAllowed, setDebugAllowed] = useState(true); // Intentionally doesn't use DeckyState. const [versionInfo, setVersionInfo] = useState(); const [errorSource, wasCausedByPlugin, shouldReportToValve] = getLikelyErrorSourceFromValveReactError(error); useEffect(() => { if (!shouldReportToValve) DeckyPluginLoader.errorBoundaryHook.temporarilyDisableReporting(); DeckyPluginLoader.updateVersion().then(setVersionInfo); }, []); const [selectedBranch, setSelectedBranch] = useSetting('branch', UpdateBranch.Stable); const [isChecking, setIsChecking] = useState(false); const [updateProgress, setUpdateProgress] = useState(-1); const [versionToUpdateTo, setSetVersionToUpdateTo] = useState(''); useEffect(() => { const a = DeckyBackend.addEventListener('updater/update_download_percentage', (percentage) => { setUpdateProgress(percentage); }); const b = DeckyBackend.addEventListener('updater/finish_download', () => { setUpdateProgress(-2); }); return () => { DeckyBackend.removeEventListener('updater/update_download_percentage', a); DeckyBackend.removeEventListener('updater/finish_download', b); }; }, []); return ( <>
An error occurred while rendering this content.
          
            {identifier && `Error Reference: ${identifier}`}
            {versionInfo?.current && `\nDecky Version: ${versionInfo.current}`}
          
        
This error likely occurred in {errorSource}.
{actionLog?.length > 0 && (
            
              Running actions...
              {actionLog}
            
          
)} {actionsEnabled && (
Actions
Use the touch screen. Solutions are listed in the recommended order. If you are still experiencing issues, please post in the #loader-support channel at decky.xyz/discord.
Retry the action or restart
{wasCausedByPlugin && (
Disable or uninstall the suspected plugin
)}
Disable all plugins
{
{updateProgress > -1 ? 'Update in progress... ' + updateProgress + '%' : updateProgress == -2 ? 'Update complete. Restarting...' : 'Check for Decky updates'} {
{updateProgress == -1 && ( <> )}
}
}
Disable Decky until next boot
{debugAllowed && (
Enable remote debugging and SSH until next boot (for developers)
)}
)} {actionsEnabled && (
Swipe to scroll
)}
Trace
            
              {error.error.stack}
              {'\n\n'}
              Component Stack:
              {error.info.componentStack}
            
          
); }; export default DeckyErrorBoundary;