Add Framework event table

Summary: Very basic framework events table, quite useful for debugging will add more to this soon

Reviewed By: lblasa

Differential Revision: D47520035

fbshipit-source-id: 10f4572dd4ed3529324f03a969773c7e91fde030
This commit is contained in:
Luke De Feo
2023-07-21 07:17:31 -07:00
committed by Facebook GitHub Bot
parent db7aa9eeaf
commit 4df0ad4d35
5 changed files with 166 additions and 43 deletions

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) Meta Platforms, Inc. and 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 {PartitionOutlined} from '@ant-design/icons';
import {
DataTable,
DataTableColumn,
DataTableManager,
Layout,
usePlugin,
} from 'flipper-plugin';
import React, {useEffect, useRef} from 'react';
import {FrameworkEvent, Id} from '../types';
import {plugin} from '../index';
import {Button, Tooltip} from 'antd';
export function FrameworkEventsTable({rootTreeId}: {rootTreeId?: Id}) {
const instance = usePlugin(plugin);
const managerRef = useRef<DataTableManager<FrameworkEvent> | null>(null);
useEffect(() => {
if (rootTreeId != null) {
managerRef.current?.resetFilters();
managerRef.current?.addColumnFilter('nodeId', rootTreeId as string);
}
}, [rootTreeId]);
return (
<Layout.Container grow>
<DataTable<FrameworkEvent>
dataSource={instance.frameworkEvents}
tableManagerRef={managerRef}
columns={columns}
extraActions={
<Tooltip title="Back to tree">
<Button
onClick={() => {
instance.uiActions.onSetViewMode({mode: 'default'});
}}
icon={<PartitionOutlined />}></Button>
</Tooltip>
}
/>
</Layout.Container>
);
}
const columns: DataTableColumn<FrameworkEvent>[] = [
{
key: 'timestamp',
onRender: (row: FrameworkEvent) => {
return new Date(row.timestamp).toLocaleTimeString();
},
},
{
key: 'nodeId',
},
{
key: 'type',
},
{
key: 'thread',
},
];

View File

@@ -13,6 +13,7 @@ import {
Id,
OnSelectNode,
UINode,
ViewMode,
} from '../types';
import React, {
ReactNode,
@@ -52,6 +53,7 @@ import {
FullscreenExitOutlined,
FullscreenOutlined,
SnippetsOutlined,
TableOutlined,
} from '@ant-design/icons';
const {Text} = Typography;
@@ -216,9 +218,11 @@ export function Tree2({nodes, rootId}: {nodes: Map<Id, UINode>; rootId: Id}) {
text={searchTerm}
highlightColor={theme.searchHighlightBackground.yellow}>
<ContextMenu
frameworkEvents={instance.frameworkEvents}
focusedNodeId={focusedNode}
hoveredNodeId={hoveredNode}
nodes={nodes}
onSetViewMode={instance.uiActions.onSetViewMode}
onContextMenuOpen={instance.uiActions.onContextMenuOpen}
onFocusNode={instance.uiActions.onFocusNode}>
<div
@@ -504,18 +508,22 @@ const DecorationImage = styled.img({
const renderDepthOffset = 12;
const ContextMenu: React.FC<{
frameworkEvents: DataSource<FrameworkEvent>;
nodes: Map<Id, UINode>;
hoveredNodeId?: Id;
focusedNodeId?: Id;
onFocusNode: (id?: Id) => void;
onContextMenuOpen: (open: boolean) => void;
onSetViewMode: (viewMode: ViewMode) => void;
}> = ({
nodes,
frameworkEvents,
hoveredNodeId,
children,
focusedNodeId,
onFocusNode,
onContextMenuOpen,
onSetViewMode,
}) => {
const copyItems: ReactNode[] = [];
const hoveredNode = nodes.get(hoveredNodeId ?? Number.MAX_SAFE_INTEGER);
@@ -579,6 +587,25 @@ const ContextMenu: React.FC<{
}}
/>
);
const matchingFrameworkEvents =
(hoveredNode &&
frameworkEvents.getAllRecordsByIndex({nodeId: hoveredNode.id})) ??
[];
const frameworkEventsTable = matchingFrameworkEvents.length > 0 && (
<UIDebuggerMenuItem
text="Explore events"
onClick={() => {
onSetViewMode({
mode: 'frameworkEventsTable',
treeRootId: hoveredNode?.id ?? '',
});
}}
icon={<TableOutlined />}
/>
);
return (
<Dropdown
onVisibleChange={(visible) => {
@@ -588,8 +615,12 @@ const ContextMenu: React.FC<{
<Menu>
{focus}
{removeFocus}
{(focus || removeFocus) && <Menu.Divider key="divider-focus" />}
{frameworkEventsTable}
{(focus || removeFocus || frameworkEventsTable) && (
<Menu.Divider key="divider-focus" />
)}
{copyItems}
{hoveredNode && <IDEContextMenuItems key="ide" node={hoveredNode} />}
</Menu>
)}

View File

@@ -28,6 +28,7 @@ import {QueryClientProvider} from 'react-query';
import {Tree2} from './Tree';
import {StreamInterceptorErrorView} from './StreamInterceptorErrorView';
import {queryClient} from '../reactQuery';
import {FrameworkEventsTable} from './FrameworkEventsTable';
export function Component() {
const instance = usePlugin(plugin);
@@ -41,6 +42,7 @@ export function Component() {
useHotkeys('ctrl+i', () => setShowPerfStats((show) => !show));
const viewMode = useValue(instance.uiState.viewMode);
const [bottomPanelComponent, setBottomPanelComponent] = useState<
ReactNode | undefined
>();
@@ -96,7 +98,12 @@ export function Component() {
<Spin data-testid="loading-indicator" />
</Centered>
);
} else {
}
if (viewMode.mode === 'frameworkEventsTable') {
return <FrameworkEventsTable rootTreeId={viewMode.treeRootId} />;
}
return (
<QueryClientProvider client={queryClient}>
<Layout.Container grow padh="small" padv="medium">
@@ -139,7 +146,6 @@ export function Component() {
</QueryClientProvider>
);
}
}
export function Centered(props: {children: React.ReactNode}) {
return (

View File

@@ -31,6 +31,7 @@ import {
UIActions,
UINode,
UIState,
ViewMode,
} from './types';
import {Draft} from 'immer';
import {tracker} from './tracker';
@@ -202,6 +203,8 @@ export function plugin(client: PluginClient<Events>) {
const uiState: UIState = {
isConnected: createState(false),
viewMode: createState({mode: 'default'}),
//used to disabled hover effects which cause rerenders and mess up the existing context menu
isContextMenuOpen: createState<boolean>(false),
@@ -461,6 +464,10 @@ function uiActions(uiState: UIState, nodes: Atom<Map<Id, UINode>>): UIActions {
uiState.filterMainThreadMonitoring.set(toggled);
};
const onSetViewMode = (viewMode: ViewMode) => {
uiState.viewMode.set(viewMode);
};
return {
onExpandNode,
onCollapseNode,
@@ -470,6 +477,7 @@ function uiActions(uiState: UIState, nodes: Atom<Map<Id, UINode>>): UIActions {
onFocusNode,
setVisualiserWidth,
onSetFilterMainThreadMonitoring,
onSetViewMode,
};
}

View File

@@ -10,6 +10,7 @@
import {Atom} from 'flipper-plugin';
export type UIState = {
viewMode: Atom<ViewMode>;
isConnected: Atom<boolean>;
isPaused: Atom<boolean>;
streamState: Atom<StreamState>;
@@ -25,6 +26,10 @@ export type UIState = {
filterMainThreadMonitoring: Atom<boolean>;
};
export type ViewMode =
| {mode: 'default'}
| {mode: 'frameworkEventsTable'; treeRootId: Id};
export type NodeSelection = {
id: Id;
source: SelectionSource;
@@ -44,7 +49,9 @@ export type UIActions = {
onCollapseNode: (node: Id) => void;
setVisualiserWidth: (width: number) => void;
onSetFilterMainThreadMonitoring: (toggled: boolean) => void;
onSetViewMode: (viewMode: ViewMode) => void;
};
export type SelectionSource = 'visualiser' | 'tree' | 'keyboard';
export type StreamState =