1/** 2 * Copyright (c) 650 Industries. 3 * Copyright (c) Meta Platforms, Inc. and affiliates. 4 * 5 * This source code is licensed under the MIT license found in the 6 * LICENSE file in the root directory of this source tree. 7 */ 8import React, { useCallback, useEffect, useState } from 'react'; 9import { Keyboard, ScrollView, View, StyleSheet } from 'react-native'; 10import * as LogBoxData from './Data/LogBoxData'; 11import { useLogs, useSelectedLog } from './Data/LogContext'; 12import * as LogBoxStyle from './UI/LogBoxStyle'; 13import { LogBoxInspectorCodeFrame } from './overlay/LogBoxInspectorCodeFrame'; 14import { LogBoxInspectorFooter as ErrorOverlayFooter } from './overlay/LogBoxInspectorFooter'; 15import { LogBoxInspectorHeader as ErrorOverlayHeader } from './overlay/LogBoxInspectorHeader'; 16import { LogBoxInspectorMessageHeader } from './overlay/LogBoxInspectorMessageHeader'; 17import { LogBoxInspectorStackFrames } from './overlay/LogBoxInspectorStackFrames'; 18const HEADER_TITLE_MAP = { 19 warn: 'Console Warning', 20 error: 'Console Error', 21 fatal: 'Uncaught Error', 22 syntax: 'Syntax Error', 23 static: 'Static Rendering Error (Node.js)', 24 component: 'Render Error', 25}; 26export function LogBoxInspectorContainer() { 27 const { selectedLogIndex, logs } = useLogs(); 28 const log = logs[selectedLogIndex]; 29 if (log == null) { 30 return null; 31 } 32 return React.createElement(LogBoxInspector, { log: log, selectedLogIndex: selectedLogIndex, logs: logs }); 33} 34export function LogBoxInspector({ log, selectedLogIndex, logs, }) { 35 const onDismiss = useCallback(() => { 36 // Here we handle the cases when the log is dismissed and it 37 // was either the last log, or when the current index 38 // is now outside the bounds of the log array. 39 const logsArray = Array.from(logs); 40 if (selectedLogIndex != null) { 41 if (logsArray.length - 1 <= 0) { 42 LogBoxData.setSelectedLog(-1); 43 } 44 else if (selectedLogIndex >= logsArray.length - 1) { 45 LogBoxData.setSelectedLog(selectedLogIndex - 1); 46 } 47 LogBoxData.dismiss(logsArray[selectedLogIndex]); 48 } 49 }, [selectedLogIndex]); 50 const onMinimize = useCallback(() => { 51 LogBoxData.setSelectedLog(-1); 52 }, []); 53 const onChangeSelectedIndex = useCallback((index) => { 54 LogBoxData.setSelectedLog(index); 55 }, []); 56 useEffect(() => { 57 if (log) { 58 LogBoxData.symbolicateLogNow('stack', log); 59 LogBoxData.symbolicateLogNow('component', log); 60 } 61 }, [log]); 62 useEffect(() => { 63 // Optimistically symbolicate the last and next logs. 64 if (logs.length > 1) { 65 const selected = selectedLogIndex; 66 const lastIndex = logs.length - 1; 67 const prevIndex = selected - 1 < 0 ? lastIndex : selected - 1; 68 const nextIndex = selected + 1 > lastIndex ? 0 : selected + 1; 69 for (const type of ['component', 'stack']) { 70 LogBoxData.symbolicateLogLazy(type, logs[prevIndex]); 71 LogBoxData.symbolicateLogLazy(type, logs[nextIndex]); 72 } 73 } 74 }, [logs, selectedLogIndex]); 75 useEffect(() => { 76 Keyboard.dismiss(); 77 }, []); 78 const _handleRetry = useCallback((type) => { 79 LogBoxData.retrySymbolicateLogNow(type, log); 80 }, [log]); 81 return (React.createElement(View, { style: styles.container }, 82 React.createElement(ErrorOverlayHeader, { onSelectIndex: onChangeSelectedIndex, level: log.level }), 83 React.createElement(ErrorOverlayBody, { onRetry: _handleRetry }), 84 React.createElement(ErrorOverlayFooter, { onDismiss: onDismiss, onMinimize: onMinimize }))); 85} 86export function ErrorOverlayBody({ onRetry }) { 87 const log = useSelectedLog(); 88 return React.createElement(ErrorOverlayBodyContents, { log: log, onRetry: onRetry }); 89} 90export function ErrorOverlayBodyContents({ log, onRetry, }) { 91 const [collapsed, setCollapsed] = useState(true); 92 useEffect(() => { 93 setCollapsed(true); 94 }, [log]); 95 const headerTitle = HEADER_TITLE_MAP[log.isComponentError ? 'component' : log.level] ?? log.type; 96 const header = (React.createElement(LogBoxInspectorMessageHeader, { collapsed: collapsed, onPress: () => setCollapsed(!collapsed), message: log.message, level: log.level, title: headerTitle })); 97 return (React.createElement(React.Fragment, null, 98 collapsed && header, 99 React.createElement(ScrollView, { style: styles.scrollBody }, 100 !collapsed && header, 101 React.createElement(LogBoxInspectorCodeFrame, { codeFrame: log.codeFrame }), 102 React.createElement(LogBoxInspectorStackFrames, { type: "stack", 103 // eslint-disable-next-line react/jsx-no-bind 104 onRetry: onRetry.bind(onRetry, 'stack') }), 105 !!log?.componentStack?.length && (React.createElement(LogBoxInspectorStackFrames, { type: "component", 106 // eslint-disable-next-line react/jsx-no-bind 107 onRetry: onRetry.bind(onRetry, 'component') }))))); 108} 109const styles = StyleSheet.create({ 110 scrollBody: { 111 backgroundColor: LogBoxStyle.getBackgroundColor(1), 112 flex: 1, 113 }, 114 container: { 115 top: 0, 116 left: 0, 117 bottom: 0, 118 right: 0, 119 zIndex: 999, 120 flex: 1, 121 // @ts-expect-error: fixed is not in the RN types but it works on web 122 position: 'fixed', 123 }, 124}); 125export default LogBoxData.withSubscription(LogBoxInspectorContainer); 126//# sourceMappingURL=ErrorOverlay.js.map