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:
committed by
Facebook GitHub Bot
parent
db7aa9eeaf
commit
4df0ad4d35
@@ -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',
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
Id,
|
Id,
|
||||||
OnSelectNode,
|
OnSelectNode,
|
||||||
UINode,
|
UINode,
|
||||||
|
ViewMode,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import React, {
|
import React, {
|
||||||
ReactNode,
|
ReactNode,
|
||||||
@@ -52,6 +53,7 @@ import {
|
|||||||
FullscreenExitOutlined,
|
FullscreenExitOutlined,
|
||||||
FullscreenOutlined,
|
FullscreenOutlined,
|
||||||
SnippetsOutlined,
|
SnippetsOutlined,
|
||||||
|
TableOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
const {Text} = Typography;
|
const {Text} = Typography;
|
||||||
@@ -216,9 +218,11 @@ export function Tree2({nodes, rootId}: {nodes: Map<Id, UINode>; rootId: Id}) {
|
|||||||
text={searchTerm}
|
text={searchTerm}
|
||||||
highlightColor={theme.searchHighlightBackground.yellow}>
|
highlightColor={theme.searchHighlightBackground.yellow}>
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
|
frameworkEvents={instance.frameworkEvents}
|
||||||
focusedNodeId={focusedNode}
|
focusedNodeId={focusedNode}
|
||||||
hoveredNodeId={hoveredNode}
|
hoveredNodeId={hoveredNode}
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
|
onSetViewMode={instance.uiActions.onSetViewMode}
|
||||||
onContextMenuOpen={instance.uiActions.onContextMenuOpen}
|
onContextMenuOpen={instance.uiActions.onContextMenuOpen}
|
||||||
onFocusNode={instance.uiActions.onFocusNode}>
|
onFocusNode={instance.uiActions.onFocusNode}>
|
||||||
<div
|
<div
|
||||||
@@ -504,18 +508,22 @@ const DecorationImage = styled.img({
|
|||||||
const renderDepthOffset = 12;
|
const renderDepthOffset = 12;
|
||||||
|
|
||||||
const ContextMenu: React.FC<{
|
const ContextMenu: React.FC<{
|
||||||
|
frameworkEvents: DataSource<FrameworkEvent>;
|
||||||
nodes: Map<Id, UINode>;
|
nodes: Map<Id, UINode>;
|
||||||
hoveredNodeId?: Id;
|
hoveredNodeId?: Id;
|
||||||
focusedNodeId?: Id;
|
focusedNodeId?: Id;
|
||||||
onFocusNode: (id?: Id) => void;
|
onFocusNode: (id?: Id) => void;
|
||||||
onContextMenuOpen: (open: boolean) => void;
|
onContextMenuOpen: (open: boolean) => void;
|
||||||
|
onSetViewMode: (viewMode: ViewMode) => void;
|
||||||
}> = ({
|
}> = ({
|
||||||
nodes,
|
nodes,
|
||||||
|
frameworkEvents,
|
||||||
hoveredNodeId,
|
hoveredNodeId,
|
||||||
children,
|
children,
|
||||||
focusedNodeId,
|
focusedNodeId,
|
||||||
onFocusNode,
|
onFocusNode,
|
||||||
onContextMenuOpen,
|
onContextMenuOpen,
|
||||||
|
onSetViewMode,
|
||||||
}) => {
|
}) => {
|
||||||
const copyItems: ReactNode[] = [];
|
const copyItems: ReactNode[] = [];
|
||||||
const hoveredNode = nodes.get(hoveredNodeId ?? Number.MAX_SAFE_INTEGER);
|
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 (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
onVisibleChange={(visible) => {
|
onVisibleChange={(visible) => {
|
||||||
@@ -588,8 +615,12 @@ const ContextMenu: React.FC<{
|
|||||||
<Menu>
|
<Menu>
|
||||||
{focus}
|
{focus}
|
||||||
{removeFocus}
|
{removeFocus}
|
||||||
{(focus || removeFocus) && <Menu.Divider key="divider-focus" />}
|
{frameworkEventsTable}
|
||||||
|
{(focus || removeFocus || frameworkEventsTable) && (
|
||||||
|
<Menu.Divider key="divider-focus" />
|
||||||
|
)}
|
||||||
{copyItems}
|
{copyItems}
|
||||||
|
|
||||||
{hoveredNode && <IDEContextMenuItems key="ide" node={hoveredNode} />}
|
{hoveredNode && <IDEContextMenuItems key="ide" node={hoveredNode} />}
|
||||||
</Menu>
|
</Menu>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {QueryClientProvider} from 'react-query';
|
|||||||
import {Tree2} from './Tree';
|
import {Tree2} from './Tree';
|
||||||
import {StreamInterceptorErrorView} from './StreamInterceptorErrorView';
|
import {StreamInterceptorErrorView} from './StreamInterceptorErrorView';
|
||||||
import {queryClient} from '../reactQuery';
|
import {queryClient} from '../reactQuery';
|
||||||
|
import {FrameworkEventsTable} from './FrameworkEventsTable';
|
||||||
|
|
||||||
export function Component() {
|
export function Component() {
|
||||||
const instance = usePlugin(plugin);
|
const instance = usePlugin(plugin);
|
||||||
@@ -41,6 +42,7 @@ export function Component() {
|
|||||||
|
|
||||||
useHotkeys('ctrl+i', () => setShowPerfStats((show) => !show));
|
useHotkeys('ctrl+i', () => setShowPerfStats((show) => !show));
|
||||||
|
|
||||||
|
const viewMode = useValue(instance.uiState.viewMode);
|
||||||
const [bottomPanelComponent, setBottomPanelComponent] = useState<
|
const [bottomPanelComponent, setBottomPanelComponent] = useState<
|
||||||
ReactNode | undefined
|
ReactNode | undefined
|
||||||
>();
|
>();
|
||||||
@@ -96,7 +98,12 @@ export function Component() {
|
|||||||
<Spin data-testid="loading-indicator" />
|
<Spin data-testid="loading-indicator" />
|
||||||
</Centered>
|
</Centered>
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (viewMode.mode === 'frameworkEventsTable') {
|
||||||
|
return <FrameworkEventsTable rootTreeId={viewMode.treeRootId} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<Layout.Container grow padh="small" padv="medium">
|
<Layout.Container grow padh="small" padv="medium">
|
||||||
@@ -139,7 +146,6 @@ export function Component() {
|
|||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export function Centered(props: {children: React.ReactNode}) {
|
export function Centered(props: {children: React.ReactNode}) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
UIActions,
|
UIActions,
|
||||||
UINode,
|
UINode,
|
||||||
UIState,
|
UIState,
|
||||||
|
ViewMode,
|
||||||
} from './types';
|
} from './types';
|
||||||
import {Draft} from 'immer';
|
import {Draft} from 'immer';
|
||||||
import {tracker} from './tracker';
|
import {tracker} from './tracker';
|
||||||
@@ -202,6 +203,8 @@ export function plugin(client: PluginClient<Events>) {
|
|||||||
const uiState: UIState = {
|
const uiState: UIState = {
|
||||||
isConnected: createState(false),
|
isConnected: createState(false),
|
||||||
|
|
||||||
|
viewMode: createState({mode: 'default'}),
|
||||||
|
|
||||||
//used to disabled hover effects which cause rerenders and mess up the existing context menu
|
//used to disabled hover effects which cause rerenders and mess up the existing context menu
|
||||||
isContextMenuOpen: createState<boolean>(false),
|
isContextMenuOpen: createState<boolean>(false),
|
||||||
|
|
||||||
@@ -461,6 +464,10 @@ function uiActions(uiState: UIState, nodes: Atom<Map<Id, UINode>>): UIActions {
|
|||||||
uiState.filterMainThreadMonitoring.set(toggled);
|
uiState.filterMainThreadMonitoring.set(toggled);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onSetViewMode = (viewMode: ViewMode) => {
|
||||||
|
uiState.viewMode.set(viewMode);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onExpandNode,
|
onExpandNode,
|
||||||
onCollapseNode,
|
onCollapseNode,
|
||||||
@@ -470,6 +477,7 @@ function uiActions(uiState: UIState, nodes: Atom<Map<Id, UINode>>): UIActions {
|
|||||||
onFocusNode,
|
onFocusNode,
|
||||||
setVisualiserWidth,
|
setVisualiserWidth,
|
||||||
onSetFilterMainThreadMonitoring,
|
onSetFilterMainThreadMonitoring,
|
||||||
|
onSetViewMode,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
import {Atom} from 'flipper-plugin';
|
import {Atom} from 'flipper-plugin';
|
||||||
|
|
||||||
export type UIState = {
|
export type UIState = {
|
||||||
|
viewMode: Atom<ViewMode>;
|
||||||
isConnected: Atom<boolean>;
|
isConnected: Atom<boolean>;
|
||||||
isPaused: Atom<boolean>;
|
isPaused: Atom<boolean>;
|
||||||
streamState: Atom<StreamState>;
|
streamState: Atom<StreamState>;
|
||||||
@@ -25,6 +26,10 @@ export type UIState = {
|
|||||||
filterMainThreadMonitoring: Atom<boolean>;
|
filterMainThreadMonitoring: Atom<boolean>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ViewMode =
|
||||||
|
| {mode: 'default'}
|
||||||
|
| {mode: 'frameworkEventsTable'; treeRootId: Id};
|
||||||
|
|
||||||
export type NodeSelection = {
|
export type NodeSelection = {
|
||||||
id: Id;
|
id: Id;
|
||||||
source: SelectionSource;
|
source: SelectionSource;
|
||||||
@@ -44,7 +49,9 @@ export type UIActions = {
|
|||||||
onCollapseNode: (node: Id) => void;
|
onCollapseNode: (node: Id) => void;
|
||||||
setVisualiserWidth: (width: number) => void;
|
setVisualiserWidth: (width: number) => void;
|
||||||
onSetFilterMainThreadMonitoring: (toggled: boolean) => void;
|
onSetFilterMainThreadMonitoring: (toggled: boolean) => void;
|
||||||
|
onSetViewMode: (viewMode: ViewMode) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SelectionSource = 'visualiser' | 'tree' | 'keyboard';
|
export type SelectionSource = 'visualiser' | 'tree' | 'keyboard';
|
||||||
|
|
||||||
export type StreamState =
|
export type StreamState =
|
||||||
|
|||||||
Reference in New Issue
Block a user