diff --git a/desktop/flipper-plugin/src/ui/Toolbar.tsx b/desktop/flipper-plugin/src/ui/Toolbar.tsx index 90304982c..e5f19be20 100644 --- a/desktop/flipper-plugin/src/ui/Toolbar.tsx +++ b/desktop/flipper-plugin/src/ui/Toolbar.tsx @@ -25,12 +25,17 @@ export function Toolbar({ children, style, wash, + right, }: { children?: React.ReactNode; position?: 'bottom' | 'top'; compact?: boolean; wash?: boolean; style?: React.CSSProperties; + /** + * Additional children that are always right-aligned + */ + right?: React.ReactNode; }) { return ( {children} + {right ? ( + <> +
+ {right} + + ) : null}
); } diff --git a/desktop/flipper-plugin/src/ui/data-table/DataSourceRenderer.tsx b/desktop/flipper-plugin/src/ui/data-table/DataSourceRenderer.tsx index e28138504..d0f9c9e11 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataSourceRenderer.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataSourceRenderer.tsx @@ -321,7 +321,7 @@ export const DataSourceRenderer: ( }) as any; const TableContainer = styled.div({ - overflowY: 'scroll', + overflowY: 'auto', overflowX: 'hidden', display: 'flex', flex: 1, diff --git a/desktop/flipper-plugin/src/ui/data-table/TableRow.tsx b/desktop/flipper-plugin/src/ui/data-table/TableRow.tsx index 804d5724d..59d43958f 100644 --- a/desktop/flipper-plugin/src/ui/data-table/TableRow.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/TableRow.tsx @@ -120,7 +120,7 @@ export const TableRow = memo(function TableRow({ .filter((col) => col.visible) .map((col) => { const value = col.onRender - ? (col as any).onRender(record, highlighted, itemIndex) // TODO: ever used? + ? (col as any).onRender(record, highlighted, itemIndex) : DataFormatter.format((record as any)[col.key], col.formatters); return ( diff --git a/desktop/plugins/public/crash_reporter/Crashes.tsx b/desktop/plugins/public/crash_reporter/Crashes.tsx new file mode 100644 index 000000000..e99a6b64d --- /dev/null +++ b/desktop/plugins/public/crash_reporter/Crashes.tsx @@ -0,0 +1,113 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import {Button, Typography} from 'antd'; +import {CoffeeOutlined, CopyOutlined, DeleteOutlined} from '@ant-design/icons'; +import { + usePlugin, + useValue, + DataList, + Layout, + CodeBlock, + Toolbar, +} from 'flipper-plugin'; +import {Crash, devicePlugin} from './index'; + +const {Text} = Typography; +export function Crashes() { + const plugin = usePlugin(devicePlugin); + const crashes = useValue(plugin.crashes); + const selectedCrashId = useValue(plugin.selectedCrash); + const selectedCrash = crashes.find( + (c) => c.notificationID === selectedCrashId, + ); + + return ( + + ({ + id: crash.notificationID, + title: crash.reason ?? crash.name, + description: `${crash.date.toLocaleString()} - ${crash.name}`, + }))} + selection={plugin.selectedCrash} + onRenderEmpty={null} + /> + {selectedCrash ? ( + + ) : ( + + + + + {crashes.length === 0 + ? 'No crashes detected so far!' + : 'No crash selected'} + + + + )} + + ); +} + +function CrashDetails({crash}: {crash: Crash}) { + const plugin = usePlugin(devicePlugin); + + return ( + + { + plugin.clearCrashes(); + }} + title="Clear all crashes" + danger> + + + }> + + {plugin.isFB ? ( + + ) : null} + + + + + {crash.name} +
+
+ {crash.reason} +
+
+ {crash.callstack} +
+
+
+ ); +} diff --git a/desktop/plugins/public/crash_reporter/__tests__/testCrashReporterPlugin.node.tsx b/desktop/plugins/public/crash_reporter/__tests__/testCrashReporterPlugin.node.tsx index 68cc7e3dd..b7eb2a089 100644 --- a/desktop/plugins/public/crash_reporter/__tests__/testCrashReporterPlugin.node.tsx +++ b/desktop/plugins/public/crash_reporter/__tests__/testCrashReporterPlugin.node.tsx @@ -12,8 +12,12 @@ import {Crash} from '../index'; import {TestUtils} from 'flipper-plugin'; import {getPluginKey} from 'flipper'; import * as CrashReporterPlugin from '../index'; -import {parseCrashLog} from '../crash-utils'; -import {parsePath, shouldShowiOSCrashNotification} from '../ios-crash-utils'; +import { + parseIosCrash, + parsePath, + shouldShowiOSCrashNotification, +} from '../ios-crash-utils'; +import {parseAndroidCrash} from '../android-crash-utils'; function getCrash( id: number, @@ -42,7 +46,7 @@ function assertCrash(crash: Crash, expectedCrash: Crash) { test('test the parsing of the date and crash info for the log which matches the predefined regex', () => { const log = 'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV \n Blaa Blaa \n Blaa Blaa Date/Time: 2019-03-21 12:07:00.861 +0000 \n Blaa balaaa'; - const crash = parseCrashLog(log, 'iOS', undefined); + const crash = parseIosCrash(log); expect(crash.callstack).toEqual(log); expect(crash.reason).toEqual('SIGSEGV'); expect(crash.name).toEqual('SIGSEGV'); @@ -52,16 +56,15 @@ test('test the parsing of the date and crash info for the log which matches the test('test the parsing of the reason for crash when log matches the crash regex, but there is no mention of date', () => { const log = 'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV \n Blaa Blaa \n Blaa Blaa'; - const crash = parseCrashLog(log, 'iOS', undefined); + const crash = parseIosCrash(log); expect(crash.callstack).toEqual(log); expect(crash.reason).toEqual('SIGSEGV'); expect(crash.name).toEqual('SIGSEGV'); - expect(crash.date).toBeUndefined(); }); test('test the parsing of the crash log when log does not match the predefined regex but is alphanumeric', () => { const log = 'Blaa Blaaa \n Blaa Blaaa \n Blaa Blaaa'; - const crash = parseCrashLog(log, 'iOS', undefined); + const crash = parseIosCrash(log); expect(crash.callstack).toEqual(log); expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.name).toEqual('Cannot figure out the cause'); @@ -70,25 +73,23 @@ test('test the parsing of the crash log when log does not match the predefined r test('test the parsing of the reason for crash when log does not match the predefined regex contains unicode character', () => { const log = 'Blaa Blaaa \n Blaa Blaaa \n Exception Type: 🍕🐬 \n Blaa Blaa \n Blaa Blaa'; - const crash = parseCrashLog(log, 'iOS', undefined); + const crash = parseIosCrash(log); expect(crash.callstack).toEqual(log); expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.name).toEqual('Cannot figure out the cause'); - expect(crash.date).toBeUndefined(); }); test('test the parsing of the reason for crash when log is empty', () => { const log = ''; - const crash = parseCrashLog(log, 'iOS', undefined); + const crash = parseIosCrash(log); expect(crash.callstack).toEqual(log); expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.name).toEqual('Cannot figure out the cause'); - expect(crash.date).toBeUndefined(); }); test('test the parsing of the Android crash log for the proper android crash format', () => { const log = 'FATAL EXCEPTION: main\nProcess: com.facebook.flipper.sample, PID: 27026\njava.lang.IndexOutOfBoundsException: Index: 190, Size: 0\n\tat java.util.ArrayList.get(ArrayList.java:437)\n\tat com.facebook.flipper.sample.RootComponentSpec.hitGetRequest(RootComponentSpec.java:72)\n\tat com.facebook.flipper.sample.RootComponent.hitGetRequest(RootComponent.java:46)\n'; const date = new Date(); - const crash = parseCrashLog(log, 'Android', date); + const crash = parseAndroidCrash(log, date); expect(crash.callstack).toEqual(log); expect(crash.reason).toEqual( 'java.lang.IndexOutOfBoundsException: Index: 190, Size: 0', @@ -98,7 +99,7 @@ test('test the parsing of the Android crash log for the proper android crash for }); test('test the parsing of the Android crash log for the unknown crash format and no date', () => { const log = 'Blaa Blaa Blaa'; - const crash = parseCrashLog(log, 'Android', undefined); + const crash = parseAndroidCrash(log, undefined); expect(crash.callstack).toEqual(log); expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.name).toEqual('Cannot figure out the cause'); @@ -106,7 +107,7 @@ test('test the parsing of the Android crash log for the unknown crash format and }); test('test the parsing of the Android crash log for the partial format matching the crash format', () => { const log = 'First Line Break \n Blaa Blaa \n Blaa Blaa '; - const crash = parseCrashLog(log, 'Android', undefined); + const crash = parseAndroidCrash(log, undefined); expect(crash.callstack).toEqual(log); expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.name).toEqual('First Line Break '); @@ -114,7 +115,7 @@ test('test the parsing of the Android crash log for the partial format matching test('test the parsing of the Android crash log with os being iOS', () => { const log = 'FATAL EXCEPTION: main\nProcess: com.facebook.flipper.sample, PID: 27026\njava.lang.IndexOutOfBoundsException: Index: 190, Size: 0\n\tat java.util.ArrayList.get(ArrayList.java:437)\n\tat com.facebook.flipper.sample.RootComponentSpec.hitGetRequest(RootComponentSpec.java:72)\n\tat com.facebook.flipper.sample.RootComponent.hitGetRequest(RootComponent.java:46)\n'; - const crash = parseCrashLog(log, 'iOS', undefined); + const crash = parseIosCrash(log); expect(crash.callstack).toEqual(log); expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.name).toEqual('Cannot figure out the cause'); @@ -165,7 +166,7 @@ test('test getNewPersistedStateFromCrashLog for non-empty defaultPersistedState const pluginStateCrash = getCrash(0, 'callstack', 'crash1', 'crash1'); plugin.instance.reportCrash(pluginStateCrash); const content = 'Blaa Blaaa \n Blaa Blaaa'; - plugin.instance.reportCrash(parseCrashLog(content, 'iOS', undefined)); + plugin.instance.reportCrash(parseIosCrash(content)); const crashes = plugin.instance.crashes.get(); expect(crashes.length).toEqual(2); assertCrash(crashes[0], pluginStateCrash); @@ -180,18 +181,6 @@ test('test getNewPersistedStateFromCrashLog for non-empty defaultPersistedState ); }); -test('test getNewPersistedStateFromCrashLog when os is undefined', () => { - const plugin = TestUtils.startDevicePlugin(CrashReporterPlugin); - const content = 'Blaa Blaaa \n Blaa Blaaa'; - expect(() => { - plugin.instance.reportCrash( - parseCrashLog(content, undefined as any, undefined), - ); - }).toThrowErrorMatchingInlineSnapshot(`"Unsupported OS"`); - const crashes = plugin.instance.crashes.get(); - expect(crashes.length).toEqual(0); -}); - test('test parsing of path when inputs are correct', () => { const content = 'Blaa Blaaa \n Blaa Blaaa \n Path: path/to/simulator/TH1S-15DEV1CE-1D/AppName.app/AppName \n Blaa Blaa \n Blaa Blaa'; diff --git a/desktop/plugins/public/crash_reporter/android-crash-utils.tsx b/desktop/plugins/public/crash_reporter/android-crash-utils.tsx index 4daa8934a..18ef07acd 100644 --- a/desktop/plugins/public/crash_reporter/android-crash-utils.tsx +++ b/desktop/plugins/public/crash_reporter/android-crash-utils.tsx @@ -7,17 +7,14 @@ * @format */ -import type {DeviceLogEntry} from 'flipper-plugin'; -import type {CrashLog} from './index'; +import type {DeviceLogEntry, DevicePluginClient} from 'flipper-plugin'; +import {UNKNOWN_CRASH_REASON} from './crash-utils'; +import type {Crash, CrashLog} from './index'; -export function parseAndroidCrash( - content: string, - fallbackReason: string, - logDate?: Date, -) { +export function parseAndroidCrash(content: string, logDate?: Date) { const regForName = /.*\n/; const nameRegArr = regForName.exec(content); - let name = nameRegArr ? nameRegArr[0] : fallbackReason; + let name = nameRegArr ? nameRegArr[0] : UNKNOWN_CRASH_REASON; const regForCallStack = /\tat[\w\s\n\.$&+,:;=?@#|'<>.^*()%!-]*$/; const callStackArray = regForCallStack.exec(content); const callStack = callStackArray ? callStackArray[0] : ''; @@ -29,8 +26,8 @@ export function parseAndroidCrash( const reasonText = remainingString.length > 0 ? remainingString.split('\n').pop() - : fallbackReason; - const reason = reasonText ? reasonText : fallbackReason; + : UNKNOWN_CRASH_REASON; + const reason = reasonText ? reasonText : UNKNOWN_CRASH_REASON; if (name[name.length - 1] === '\n') { name = name.slice(0, -1); } @@ -53,3 +50,34 @@ export function shouldParseAndroidLog( entry.type === 'fatal') ); } + +export function startAndroidCrashWatcher( + client: DevicePluginClient, + reportCrash: (payload: CrashLog | Crash) => void, +) { + const referenceDate = new Date(); + let androidLog: string = ''; + let androidLogUnderProcess = false; + let timer: null | NodeJS.Timeout = null; + client.device.onLogEntry((entry: DeviceLogEntry) => { + if (shouldParseAndroidLog(entry, referenceDate)) { + if (androidLogUnderProcess) { + androidLog += '\n' + entry.message; + androidLog = androidLog.trim(); + if (timer) { + clearTimeout(timer); + } + } else { + androidLog = entry.message; + androidLogUnderProcess = true; + } + timer = setTimeout(() => { + if (androidLog.length > 0) { + reportCrash(parseAndroidCrash(androidLog, entry.date)); + } + androidLogUnderProcess = false; + androidLog = ''; + }, 50); + } + }); +} diff --git a/desktop/plugins/public/crash_reporter/crash-utils.tsx b/desktop/plugins/public/crash_reporter/crash-utils.tsx index e5893531b..58560a3fd 100644 --- a/desktop/plugins/public/crash_reporter/crash-utils.tsx +++ b/desktop/plugins/public/crash_reporter/crash-utils.tsx @@ -8,32 +8,12 @@ */ import unicodeSubstring from 'unicode-substring'; -import type {CrashLog} from './index'; -import {parseAndroidCrash} from './android-crash-utils'; -import {parseIosCrash} from './ios-crash-utils'; +import type {Crash} from './index'; +import {DevicePluginClient} from 'flipper-plugin'; export const UNKNOWN_CRASH_REASON = 'Cannot figure out the cause'; -export function parseCrashLog( - content: string, - os: string, - logDate?: Date, -): CrashLog { - const fallbackReason = UNKNOWN_CRASH_REASON; - switch (os) { - case 'iOS': { - return parseIosCrash(content, fallbackReason, logDate); - } - case 'Android': { - return parseAndroidCrash(content, fallbackReason, logDate); - } - default: { - throw new Error('Unsupported OS'); - } - } -} - -export function truncate(baseString: string, numOfChars: number): string { +function truncate(baseString: string, numOfChars: number): string { if (baseString.length <= numOfChars) { return baseString; } @@ -41,8 +21,40 @@ export function truncate(baseString: string, numOfChars: number): string { return truncated_string + '\u2026'; } -export function trimCallStackIfPossible(callstack: string): string { +function trimCallStackIfPossible(callstack: string): string { const regex = /Application Specific Information:/; const query = regex.exec(callstack); return query ? callstack.substring(0, query.index) : callstack; } + +export function showCrashNotification( + client: DevicePluginClient, + crash: Crash, +) { + const ignore = !crash.name && !crash.reason; + const unknownCrashCause = crash.reason === UNKNOWN_CRASH_REASON; + if (ignore || unknownCrashCause) { + console.warn('Ignored the notification for the crash', crash); + return; + } + + let title: string = 'CRASH: ' + truncate(crash.name || crash.reason, 50); + title = `${ + crash.name == crash.reason + ? title + : title + 'Reason: ' + truncate(crash.reason, 50) + }`; + const callstack = crash.callstack + ? trimCallStackIfPossible(crash.callstack) + : 'No callstack available'; + const msg = `Callstack: ${truncate(callstack, 200)}`; + // TODO: fix client id + client.showNotification({ + id: crash.notificationID, + message: msg, + severity: 'error', + title: title, + action: crash.notificationID, + category: crash.reason || 'Unknown reason', + }); +} diff --git a/desktop/plugins/public/crash_reporter/index.tsx b/desktop/plugins/public/crash_reporter/index.tsx index d79118863..d4bf6e954 100644 --- a/desktop/plugins/public/crash_reporter/index.tsx +++ b/desktop/plugins/public/crash_reporter/index.tsx @@ -7,57 +7,11 @@ * @format */ -import { - View, - styled, - FlexColumn, - FlexRow, - ContextMenu, - clipboard, - Button, - Text, - colors, - Toolbar, - Spacer, - Select, -} from 'flipper'; -import React from 'react'; -import { - createState, - DeviceLogEntry, - DevicePluginClient, - usePlugin, - useValue, -} from 'flipper-plugin'; import type {FSWatcher} from 'fs'; -import { - parseCrashLog, - trimCallStackIfPossible, - truncate, - UNKNOWN_CRASH_REASON, -} from './crash-utils'; +import {createState, DevicePluginClient} from 'flipper-plugin'; +import {showCrashNotification} from './crash-utils'; import {addFileWatcherForiOSCrashLogs} from './ios-crash-utils'; -import {shouldParseAndroidLog} from './android-crash-utils'; - -type Maybe = T | null | undefined; - -type HeaderRowProps = { - title: string; - value: string; -}; -type openLogsCallbackType = () => void; - -type CrashReporterBarProps = { - openLogsCallback?: openLogsCallbackType; - crashSelector: CrashSelectorProps; -}; - -type CrashSelectorProps = { - crashes?: {[key: string]: string}; - orderedIDs?: Array; - selectedCrashID?: string; - onCrashChange: (name: Maybe) => void; -}; +import {startAndroidCrashWatcher} from './android-crash-utils'; export type Crash = { notificationID: string; @@ -71,235 +25,9 @@ export type CrashLog = { callstack: string; reason: string; name: string; - date: Maybe; + date?: Date | null; }; -const Padder = styled.div<{ - paddingLeft?: number; - paddingRight?: number; - paddingBottom?: number; - paddingTop?: number; -}>(({paddingLeft, paddingRight, paddingBottom, paddingTop}) => ({ - paddingLeft: paddingLeft || 0, - paddingRight: paddingRight || 0, - paddingBottom: paddingBottom || 0, - paddingTop: paddingTop || 0, -})); - -const Title = styled(Text)({ - fontWeight: 'bold', - color: colors.greyTint3, - height: 'auto', - width: 200, - textOverflow: 'ellipsis', -}); - -const Line = styled(View)({ - backgroundColor: colors.greyTint2, - height: 1, - width: 'auto', - marginTop: 2, - flexShrink: 0, -}); - -const Container = styled(FlexColumn)({ - overflow: 'auto', - flexShrink: 0, -}); - -const Value = styled(Text)({ - fontWeight: 'bold', - color: colors.greyTint3, - height: 'auto', - maxHeight: 200, - flexGrow: 1, - textOverflow: 'ellipsis', - whiteSpace: 'normal', - wordWrap: 'break-word', - lineHeight: 2, - marginLeft: 8, - marginRight: 8, - overflow: 'hidden', -}); - -const FlexGrowColumn = styled(FlexColumn)({ - flexGrow: 1, -}); - -const PluginRootContainer = styled(FlexColumn)({ - height: '100%', -}); - -const ScrollableColumn = styled(FlexGrowColumn)({ - overflow: 'auto', - height: 'auto', -}); - -const StyledFlexGrowColumn = styled(FlexColumn)({ - flexGrow: 1, -}); - -const StyledFlexRowColumn = styled(FlexRow)({ - aligItems: 'center', - justifyContent: 'center', - height: '100%', -}); - -const StyledFlexColumn = styled(StyledFlexGrowColumn)({ - justifyContent: 'center', - alignItems: 'center', -}); - -const MatchParentHeightComponent = styled(FlexRow)({ - height: '100%', -}); - -const ButtonGroupContainer = styled(FlexRow)({ - paddingLeft: 4, - paddingTop: 2, - paddingBottom: 2, - height: '100%', -}); - -const StyledSelectContainer = styled(FlexRow)({ - paddingLeft: 8, - paddingTop: 2, - paddingBottom: 2, - height: '100%', -}); - -const StyledSelect = styled(Select)({ - height: '100%', - maxWidth: 200, -}); - -const StackTraceContainer = styled(FlexColumn)({ - backgroundColor: colors.greyStackTraceTint, - flexShrink: 0, -}); - -class CrashSelector extends React.Component { - render() { - const {crashes, selectedCrashID, orderedIDs, onCrashChange} = this.props; - return ( - - - - - - ); - } -} - -class HeaderRow extends React.Component { - render() { - const {title, value} = this.props; - return ( - - - - {title} - { - clipboard.writeText(value); - }, - }, - ]}> - {value} - - - - - - ); - } -} - -type StackTraceComponentProps = { - stacktrace: string; -}; - -class StackTraceComponent extends React.Component { - render() { - const {stacktrace} = this.props; - return ( - - - {stacktrace} - - - - ); - } -} - export function devicePlugin(client: DevicePluginClient) { let notificationID = -1; let watcher: FSWatcher | undefined; @@ -326,70 +54,18 @@ export function devicePlugin(client: DevicePluginClient) { draft.push(crash); }); - // show notification? - const ignore = !crash.name && !crash.reason; - const unknownCrashCause = crash.reason === UNKNOWN_CRASH_REASON; - if (ignore || unknownCrashCause) { - console.warn('Ignored the notification for the crash', crash); - return; - } - - let title: string = 'CRASH: ' + truncate(crash.name || crash.reason, 50); - title = `${ - crash.name == crash.reason - ? title - : title + 'Reason: ' + truncate(crash.reason, 50) - }`; - const callstack = crash.callstack - ? trimCallStackIfPossible(crash.callstack) - : 'No callstack available'; - const msg = `Callstack: ${truncate(callstack, 200)}`; - client.showNotification({ - id: crash.notificationID, - message: msg, - severity: 'error', - title: title, - action: crash.notificationID, - category: crash.reason || 'Unknown reason', - }); + showCrashNotification(client, crash); } // Startup logic to establish log monitoring if (client.device.isConnected) { if (client.device.os.includes('iOS')) { watcher = addFileWatcherForiOSCrashLogs( - client.device.os, client.device.serial, reportCrash, ); } else { - const referenceDate = new Date(); - let androidLog: string = ''; - let androidLogUnderProcess = false; - let timer: Maybe = null; - client.device.onLogEntry((entry: DeviceLogEntry) => { - if (shouldParseAndroidLog(entry, referenceDate)) { - if (androidLogUnderProcess) { - androidLog += '\n' + entry.message; - androidLog = androidLog.trim(); - if (timer) { - clearTimeout(timer); - } - } else { - androidLog = entry.message; - androidLogUnderProcess = true; - } - timer = setTimeout(() => { - if (androidLog.length > 0) { - reportCrash( - parseCrashLog(androidLog, client.device.os, entry.date), - ); - } - androidLogUnderProcess = false; - androidLog = ''; - }, 50); - } - }); + startAndroidCrashWatcher(client, reportCrash); } } @@ -408,106 +84,15 @@ export function devicePlugin(client: DevicePluginClient) { copyCrashToClipboard(callstack: string) { client.writeTextToClipboard(callstack); }, + createPaste(callstack: string) { + client.createPaste(callstack); + }, + isFB: client.isFB, + clearCrashes() { + crashes.set([]); + selectedCrash.set(undefined); + }, }; } -export function Component() { - const plugin = usePlugin(devicePlugin); - const selectedCrash = useValue(plugin.selectedCrash); - const crashes = useValue(plugin.crashes); - const crash = - crashes.find((c) => c.notificationID === selectedCrash) ?? - crashes[crashes.length - 1] ?? - undefined; - - if (crash) { - const crashMap = crashes.reduce( - (acc: {[key: string]: string}, persistedCrash: Crash) => { - const {notificationID, date} = persistedCrash; - const name = 'Crash at ' + date.toLocaleString(); - acc[notificationID] = name; - return acc; - }, - {}, - ); - - const orderedIDs = crashes.map( - (persistedCrash) => persistedCrash.notificationID, - ); - const selectedCrashID = crash.notificationID; - const onCrashChange = (id: Maybe) => { - if (id) { - plugin.selectedCrash.set(id); - } - }; - - const callstackString = crash.callstack || ''; - const children = callstackString.split('\n').map((str) => { - return {message: str}; - }); - const crashSelector: CrashSelectorProps = { - crashes: crashMap, - orderedIDs, - selectedCrashID, - onCrashChange, - }; - const showReason = crash.reason !== UNKNOWN_CRASH_REASON; - return ( - - {plugin.os == 'Android' ? ( - { - if (crash.callstack) { - plugin.openInLogs(crash.callstack); - } - }} - /> - ) : ( - - )} - - - {showReason ? ( - - ) : null} - - Stacktrace - - { - plugin.copyCrashToClipboard(callstackString); - }, - }, - ]}> - - {children.map((child, index) => { - return ( - - ); - })} - - - - ); - } - const crashSelector = { - crashes: undefined, - orderedIDs: undefined, - selectedCrashID: undefined, - onCrashChange: () => void {}, - }; - return ( - - - - - No Crashes Logged - - - - ); -} +export {Crashes as Component} from './Crashes'; diff --git a/desktop/plugins/public/crash_reporter/ios-crash-utils.tsx b/desktop/plugins/public/crash_reporter/ios-crash-utils.tsx index 96da2d257..097f243a8 100644 --- a/desktop/plugins/public/crash_reporter/ios-crash-utils.tsx +++ b/desktop/plugins/public/crash_reporter/ios-crash-utils.tsx @@ -8,35 +8,28 @@ */ import type {Crash, CrashLog} from './index'; -import {parseCrashLog} from './crash-utils'; import fs from 'fs'; import os from 'os'; import path from 'path'; import {promisify} from 'util'; +import {UNKNOWN_CRASH_REASON} from './crash-utils'; -export function parseIosCrash( - content: string, - fallbackReason: string, - logDate?: Date, -) { +export function parseIosCrash(content: string) { const regex = /Exception Type: *\w*/; const arr = regex.exec(content); const exceptionString = arr ? arr[0] : ''; const exceptionRegex = /\w*$/; const tmp = exceptionRegex.exec(exceptionString); - const exception = tmp && tmp[0].length ? tmp[0] : fallbackReason; + const exception = tmp && tmp[0].length ? tmp[0] : UNKNOWN_CRASH_REASON; - let date = logDate; - if (!date) { - const dateRegex = /Date\/Time: *[\w\s\.:-]*/; - const dateArr = dateRegex.exec(content); - const dateString = dateArr ? dateArr[0] : ''; - const dateRegex2 = /[\w\s\.:-]*$/; - const tmp1 = dateRegex2.exec(dateString); - const extractedDateString: string | null = - tmp1 && tmp1[0].length ? tmp1[0] : null; - date = extractedDateString ? new Date(extractedDateString) : logDate; - } + const dateRegex = /Date\/Time: *[\w\s\.:-]*/; + const dateArr = dateRegex.exec(content); + const dateString = dateArr ? dateArr[0] : ''; + const dateRegex2 = /[\w\s\.:-]*$/; + const tmp1 = dateRegex2.exec(dateString); + const extractedDateString: string | null = + tmp1 && tmp1[0].length ? tmp1[0] : null; + const date = extractedDateString ? new Date(extractedDateString) : new Date(); const crash: CrashLog = { callstack: content, @@ -70,7 +63,6 @@ export function parsePath(content: string): string | null { } export function addFileWatcherForiOSCrashLogs( - deviceOs: string, serial: string, reportCrash: (payload: CrashLog | Crash) => void, ) { @@ -96,7 +88,7 @@ export function addFileWatcherForiOSCrashLogs( return; } if (shouldShowiOSCrashNotification(serial, data)) { - reportCrash(parseCrashLog(data, deviceOs, undefined)); + reportCrash(parseIosCrash(data)); } }); }); diff --git a/desktop/plugins/public/crash_reporter/package.json b/desktop/plugins/public/crash_reporter/package.json index a8beff645..abf349aac 100644 --- a/desktop/plugins/public/crash_reporter/package.json +++ b/desktop/plugins/public/crash_reporter/package.json @@ -35,6 +35,8 @@ "unicode-substring": "^1.0.0" }, "peerDependencies": { - "flipper-plugin": "0.0.0" + "@ant-design/icons": "*", + "ant-design": "*", + "flipper-plugin": "*" } }