From d5814ea17c38cc759f6dc9f1adc62044c0b48117 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Mon, 21 Aug 2023 04:24:16 -0700 Subject: [PATCH] Use bottom panel as detail view for framework events Summary: Now when you click on an event the bottom bar appears automatically showing you every thing you need Reviewed By: lblasa Differential Revision: D48318694 fbshipit-source-id: 6505e439d949941dc0e091b9576d7d1321d8a05f --- .../public/ui-debugger/ClientTypes.tsx | 1 + .../public/ui-debugger/components/main.tsx | 50 ++++-- .../components/sidebar/Inspector.tsx | 2 +- .../inspector/FrameworkEventsInspector.tsx | 167 ++++++++++++------ 4 files changed, 150 insertions(+), 70 deletions(-) diff --git a/desktop/plugins/public/ui-debugger/ClientTypes.tsx b/desktop/plugins/public/ui-debugger/ClientTypes.tsx index 39348de8f..374e7bd6d 100644 --- a/desktop/plugins/public/ui-debugger/ClientTypes.tsx +++ b/desktop/plugins/public/ui-debugger/ClientTypes.tsx @@ -60,6 +60,7 @@ export type FrameworkEvent = { type: FrameworkEventType; timestamp: number; payload?: JSON; + duration?: number; attribution?: FrameworkEventAttribution; thread?: 'main' | string; }; diff --git a/desktop/plugins/public/ui-debugger/components/main.tsx b/desktop/plugins/public/ui-debugger/components/main.tsx index 632ae8a5e..22c778342 100644 --- a/desktop/plugins/public/ui-debugger/components/main.tsx +++ b/desktop/plugins/public/ui-debugger/components/main.tsx @@ -23,7 +23,7 @@ import {PerfStats} from './PerfStats'; import {Visualization2D} from './visualizer/Visualization2D'; import {Inspector} from './sidebar/Inspector'; import {TreeControls} from './tree/TreeControls'; -import {Button, Spin} from 'antd'; +import {Button, Spin, Typography} from 'antd'; import {QueryClientProvider} from 'react-query'; import {Tree2} from './tree/Tree'; import {StreamInterceptorErrorView} from './StreamInterceptorErrorView'; @@ -43,14 +43,14 @@ export function Component() { useHotkeys('ctrl+i', () => setShowPerfStats((show) => !show)); const viewMode = useValue(instance.uiState.viewMode); - const [bottomPanelComponent, setBottomPanelComponent] = useState< - ReactNode | undefined + const [bottomPanel, setBottomPanel] = useState< + {title: string; component: ReactNode} | undefined >(); - const openBottomPanelWithContent = (component: ReactNode) => { - setBottomPanelComponent(component); + const openBottomPanelWithContent = (title: string, component: ReactNode) => { + setBottomPanel({title, component}); }; const dismissBottomPanel = () => { - setBottomPanelComponent(undefined); + setBottomPanel(undefined); }; const [bottomPanelHeight, setBottomPanelHeight] = useState(400); @@ -124,7 +124,7 @@ export function Component() { - + - {bottomPanelComponent && ( + {bottomPanel && ( - {bottomPanelComponent} + {bottomPanel.component} )} @@ -179,12 +180,14 @@ export function Centered(props: {children: React.ReactNode}) { } type BottomPanelProps = { + title: string; dismiss: () => void; children: React.ReactNode; height: number; setHeight: (height: number) => void; }; export function BottomPanel({ + title, dismiss, children, height, @@ -198,7 +201,10 @@ export function BottomPanel({ bottomPanelRef.current && !bottomPanelRef.current.contains(event.target) ) { - dismiss(); + setTimeout(() => { + //push to back of event queue so that you can still select item in the tree + dismiss(); + }, 0); } }; // Add event listener when the component is mounted. @@ -222,12 +228,22 @@ export function BottomPanel({ height={height} onResize={(_, height) => setHeight(height)} gutter> - {children} -
- -
+ + + {title} + + + + {children} + + ); diff --git a/desktop/plugins/public/ui-debugger/components/sidebar/Inspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebar/Inspector.tsx index cd8298206..69353765b 100644 --- a/desktop/plugins/public/ui-debugger/components/sidebar/Inspector.tsx +++ b/desktop/plugins/public/ui-debugger/components/sidebar/Inspector.tsx @@ -31,7 +31,7 @@ type Props = { os: DeviceOS; nodes: Map; metadata: Map; - showExtra: (element: ReactNode) => void; + showExtra: (title: string, element: ReactNode) => void; }; export const Inspector: React.FC = ({ diff --git a/desktop/plugins/public/ui-debugger/components/sidebar/inspector/FrameworkEventsInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/FrameworkEventsInspector.tsx index c44318ccb..2305b3dea 100644 --- a/desktop/plugins/public/ui-debugger/components/sidebar/inspector/FrameworkEventsInspector.tsx +++ b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/FrameworkEventsInspector.tsx @@ -7,70 +7,133 @@ * @format */ -import {Button} from 'antd'; -import {theme, TimelineDataDescription} from 'flipper-plugin'; +import { + DataInspector, + Layout, + theme, + TimelineDataDescription, +} from 'flipper-plugin'; import {FrameworkEvent, ClientNode} from '../../../ClientTypes'; -import React, {ReactNode, useState} from 'react'; +import React, {ReactNode} from 'react'; import {StackTraceInspector} from './StackTraceInspector'; +import {Descriptions, Tag} from 'antd'; type Props = { node: ClientNode; events: readonly FrameworkEvent[]; - showExtra?: (element: ReactNode) => void; + showExtra?: (title: string, element: ReactNode) => void; }; export const FrameworkEventsInspector: React.FC = ({ node, events, showExtra, }) => { - const [selectedEvent, setSelectedEvent] = useState( - events[events.length - 1], - ); - - const showStacktrace = () => { - const attribution = selectedEvent.attribution; - if (attribution?.type === 'stacktrace') { - const stacktraceInspector = ( - - ); - showExtra?.(stacktraceInspector); - } - }; - - const hasStacktrace = (event: FrameworkEvent) => { - return event?.attribution?.type === 'stacktrace'; - }; - return ( - <> - { - const idx = parseInt(current, 10); - setSelectedEvent(events[idx]); - }} - timeline={{ - time: events.map((e, idx) => { - return { - moment: e.timestamp, - display: e.type.slice(e.type.lastIndexOf(':') + 1), - color: theme.primaryColor, - key: idx.toString(), - properties: e.payload as any, - }; - }), - current: (events.length - 1).toString(), - }} - /> - {hasStacktrace(selectedEvent) && ( - - )} - + { + const idx = parseInt(current, 10); + const event = events[idx]; + showExtra?.( + 'Event details', + , + ); + }} + timeline={{ + time: events.map((event, idx) => { + return { + moment: event.timestamp, + display: `${eventTypeToName(event.type)}`, + color: threadToColor(event.thread), + key: idx.toString(), + }; + }), + current: 'initialNone', + }} + /> ); }; + +function EventDetails({ + event, + node, +}: { + event: FrameworkEvent; + node: ClientNode; +}) { + const stackTrace = + event?.attribution?.type === 'stacktrace' ? ( + + ) : null; + + const details = ( + + + {event.type} + + {event.thread} + + + {formatTimestamp(event.timestamp)} + + {event.duration && ( + + {formatDuration(event.duration)} + + )} + {event.payload && Object.keys(event.payload).length > 0 && ( + + + + )} + + + ); + + return ( + + {details} + {stackTrace} + + ); +} + +const options: Intl.DateTimeFormatOptions = { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: false, +}; +function formatTimestamp(timestamp: number): string { + const date = new Date(timestamp); + + const formattedDate = new Intl.DateTimeFormat('en-US', options).format(date); + const milliseconds = date.getMilliseconds(); + + return `${formattedDate}.${milliseconds.toString().padStart(3, '0')}`; +} + +function formatDuration(nanoseconds: number): string { + if (nanoseconds < 1_000) { + return `${nanoseconds} nanoseconds`; + } else if (nanoseconds < 1_000_000) { + return `${(nanoseconds / 1_000).toFixed(2)} microseconds`; + } else if (nanoseconds < 1_000_000_000) { + return `${(nanoseconds / 1_000_000).toFixed(2)} milliseconds`; + } else if (nanoseconds < 1_000_000_000_000) { + return `${(nanoseconds / 1_000_000_000).toFixed(2)} seconds`; + } else { + return `${(nanoseconds / 1_000_000_000_000).toFixed(2)} minutes`; + } +} +function eventTypeToName(eventType: string) { + return eventType.slice(eventType.lastIndexOf('.') + 1); +} + +function threadToColor(thread?: string) { + return thread === 'main' ? theme.warningColor : theme.primaryColor; +}