Files
flipper/desktop/plugins/public/ui-debugger/components/main.tsx
Luke De Feo c76c993ce4 Introduced concept of active child
Summary: A node can have an active child, if present we assume all others are inactive and we don't traverse them. This means the activities not on top and view pager views not active will not be scanned. Additionally on the desktop we are automatically collapsing these views. The net result is a lot less work done on the main thread

Reviewed By: lblasa

Differential Revision: D39310126

fbshipit-source-id: ebd0c69d46f2d42fe42e678c8327fcdc73d08385
2022-09-12 03:48:43 -07:00

158 lines
3.7 KiB
TypeScript

/**
* 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 React, {useState} from 'react';
import {PerfStatsEvent, plugin} from '../index';
import {
DataInspector,
DataTable,
DataTableColumn,
DetailSidebar,
Layout,
usePlugin,
useValue,
} from 'flipper-plugin';
import {Tree, Typography} from 'antd';
import type {DataNode} from 'antd/es/tree';
import {DownOutlined} from '@ant-design/icons';
import {useHotkeys} from 'react-hotkeys-hook';
import {Id, UINode} from '../types';
function nodesToAntTree(root: Id, nodes: Map<Id, UINode>): [DataNode, Id[]] {
const inactive: Id[] = [];
function uiNodeToAntNode(id: Id): DataNode {
const node = nodes.get(id);
if (node?.activeChild) {
for (const child of node.children) {
if (child !== node?.activeChild) {
inactive.push(child);
}
}
}
return {
key: id,
title: node?.name,
children: node?.children.map((id) => uiNodeToAntNode(id)),
};
}
return [uiNodeToAntNode(root), inactive];
}
function formatDiff(start: number, end: number): string {
const ms = end - start;
return `${ms.toFixed(0)}ms`;
}
export const columns: DataTableColumn<PerfStatsEvent>[] = [
{
key: 'txId',
title: 'TXID',
},
{
key: 'nodesCount',
title: 'Total nodes',
},
{
key: 'start',
title: 'Start',
onRender: (row: PerfStatsEvent) => {
console.log(row.start);
return new Date(row.start).toISOString();
},
},
{
key: 'scanComplete',
title: 'Scan time',
onRender: (row: PerfStatsEvent) => {
return formatDiff(row.start, row.scanComplete);
},
},
{
key: 'serializationComplete',
title: 'Serialization time',
onRender: (row: PerfStatsEvent) => {
return formatDiff(row.scanComplete, row.serializationComplete);
},
},
{
key: 'socketComplete',
title: 'Socket send time',
onRender: (row: PerfStatsEvent) => {
return formatDiff(row.serializationComplete, row.socketComplete);
},
},
];
export function Component() {
const instance = usePlugin(plugin);
const rootId = useValue(instance.rootId);
const nodes = useValue(instance.nodes);
const [showPerfStats, setShowPerfStats] = useState(false);
const [selectedNode, setSelectedNode] = useState<string | undefined>(
undefined,
);
useHotkeys('ctrl+i', () => setShowPerfStats((show) => !show));
function renderAttributesInspector(node: UINode | undefined) {
if (!node) {
return;
}
return (
<>
<DetailSidebar>
<Layout.Container gap pad>
<Typography.Title level={2}>Attributes Inspector</Typography.Title>
<DataInspector data={node.attributes} expandRoot />
</Layout.Container>
</DetailSidebar>
</>
);
}
if (showPerfStats)
return (
<DataTable<PerfStatsEvent>
dataSource={instance.perfEvents}
columns={columns}
/>
);
if (rootId) {
const [antTree, inactive] = nodesToAntTree(rootId, nodes);
return (
<>
<Layout.ScrollContainer>
<Tree
showIcon
showLine
onSelect={(selected) => {
setSelectedNode(selected[0] as string);
}}
defaultExpandAll
expandedKeys={[...nodes.keys()].filter(
(key) => !inactive.includes(key),
)}
switcherIcon={<DownOutlined />}
treeData={[antTree]}
/>
</Layout.ScrollContainer>
{selectedNode && renderAttributesInspector(nodes.get(selectedNode))}
</>
);
}
return <div>Nothing yet</div>;
}