UID Refactor 3/n UINode -> ClientNode

Summary:
UINode has never been a good name, we have 3 versions of a node.
ClientNode Previously UINode (the raw data from the client)
NestedNode (for the visualiser)
TreeNode (extends ClientNode and adds stuff specific to the tree like indentation and expanded states)

Arguablely we dont need nested node but that is another story

Reviewed By: elboman

Differential Revision: D47547529

fbshipit-source-id: 9a3b119d1230ea7b6734e7a3270c28287b04faf1
This commit is contained in:
Luke De Feo
2023-07-21 07:17:31 -07:00
committed by Facebook GitHub Bot
parent f181551ce6
commit 87a1b657c3
14 changed files with 77 additions and 51 deletions

View File

@@ -18,7 +18,7 @@ export type Events = {
export type FrameScanEvent = { export type FrameScanEvent = {
frameTime: number; frameTime: number;
nodes: UINode[]; nodes: ClientNode[];
snapshot?: SnapshotInfo; snapshot?: SnapshotInfo;
frameworkEvents?: FrameworkEvent[]; frameworkEvents?: FrameworkEvent[];
}; };
@@ -30,7 +30,7 @@ export type FrameScanEvent = {
export type SubtreeUpdateEvent = { export type SubtreeUpdateEvent = {
txId: number; txId: number;
rootId: Id; rootId: Id;
nodes: UINode[]; nodes: ClientNode[];
snapshot: Snapshot; snapshot: Snapshot;
frameworkEvents?: FrameworkEvent[]; frameworkEvents?: FrameworkEvent[];
}; };
@@ -107,7 +107,7 @@ export type UpdateMetadataEvent = {
attributeMetadata: Record<MetadataId, Metadata>; attributeMetadata: Record<MetadataId, Metadata>;
}; };
export type UINode = { export type ClientNode = {
id: Id; id: Id;
parent?: Id; parent?: Id;
qualifiedName: string; //this is the name of the component plus qualification so myles has a chance of finding it. E.g com.facebook.MyView qualifiedName: string; //this is the name of the component plus qualification so myles has a chance of finding it. E.g com.facebook.MyView

View File

@@ -14,7 +14,7 @@ import {
Inspectable, Inspectable,
Bounds, Bounds,
Tag, Tag,
UINode, ClientNode,
Metadata, Metadata,
} from './ClientTypes'; } from './ClientTypes';
@@ -91,8 +91,8 @@ export type StreamState =
export interface StreamInterceptor { export interface StreamInterceptor {
transformNodes( transformNodes(
nodes: Map<Id, UINode>, nodes: Map<Id, ClientNode>,
): Promise<[Map<Id, UINode>, Metadata[]]>; ): Promise<[Map<Id, ClientNode>, Metadata[]]>;
transformMetadata(metadata: Metadata): Promise<Metadata>; transformMetadata(metadata: Metadata): Promise<Metadata>;
} }

View File

@@ -11,7 +11,7 @@ import {
PerformanceStatsEvent, PerformanceStatsEvent,
DynamicPerformanceStatsEvent, DynamicPerformanceStatsEvent,
Id, Id,
UINode, ClientNode,
FrameworkEvent, FrameworkEvent,
} from '../ClientTypes'; } from '../ClientTypes';
import {UIState} from '../DesktopTypes'; import {UIState} from '../DesktopTypes';
@@ -27,7 +27,7 @@ import {
export function PerfStats(props: { export function PerfStats(props: {
uiState: UIState; uiState: UIState;
nodes: Map<Id, UINode>; nodes: Map<Id, ClientNode>;
rootId?: Id; rootId?: Id;
events: DataSource<DynamicPerformanceStatsEvent, number>; events: DataSource<DynamicPerformanceStatsEvent, number>;
frameworkEvents: DataSource<FrameworkEvent>; frameworkEvents: DataSource<FrameworkEvent>;

View File

@@ -7,7 +7,12 @@
* @format * @format
*/ */
import {FrameworkEvent, FrameworkEventType, Id, UINode} from '../ClientTypes'; import {
FrameworkEvent,
FrameworkEventType,
Id,
ClientNode,
} from '../ClientTypes';
import {OnSelectNode, ViewMode} from '../DesktopTypes'; import {OnSelectNode, ViewMode} from '../DesktopTypes';
import React, { import React, {
ReactNode, ReactNode,
@@ -61,14 +66,20 @@ type NodeIndentGuide = {
addHorizontalMarker: boolean; addHorizontalMarker: boolean;
trimBottom: boolean; trimBottom: boolean;
}; };
export type TreeNode = UINode & { export type TreeNode = ClientNode & {
depth: number; depth: number;
idx: number; idx: number;
isExpanded: boolean; isExpanded: boolean;
indentGuide: NodeIndentGuide | null; indentGuide: NodeIndentGuide | null;
frameworkEvents: number | null; frameworkEvents: number | null;
}; };
export function Tree2({nodes, rootId}: {nodes: Map<Id, UINode>; rootId: Id}) { export function Tree2({
nodes,
rootId,
}: {
nodes: Map<Id, ClientNode>;
rootId: Id;
}) {
const instance = usePlugin(plugin); const instance = usePlugin(plugin);
const focusedNode = useValue(instance.uiState.focusedNode); const focusedNode = useValue(instance.uiState.focusedNode);
const expandedNodes = useValue(instance.uiState.expandedNodes); const expandedNodes = useValue(instance.uiState.expandedNodes);
@@ -476,7 +487,7 @@ function HighlightedText(props: {text: string}) {
return <span>{highlightManager.render(props.text)} </span>; return <span>{highlightManager.render(props.text)} </span>;
} }
function nodeIcon(node: UINode) { function nodeIcon(node: ClientNode) {
if (node.tags.includes('LithoMountable')) { if (node.tags.includes('LithoMountable')) {
return <DecorationImage src="icons/litho-logo-blue.png" />; return <DecorationImage src="icons/litho-logo-blue.png" />;
} else if (node.tags.includes('Litho')) { } else if (node.tags.includes('Litho')) {
@@ -503,7 +514,7 @@ const renderDepthOffset = 12;
const ContextMenu: React.FC<{ const ContextMenu: React.FC<{
frameworkEvents: DataSource<FrameworkEvent>; frameworkEvents: DataSource<FrameworkEvent>;
nodes: Map<Id, UINode>; nodes: Map<Id, ClientNode>;
hoveredNodeId?: Id; hoveredNodeId?: Id;
focusedNodeId?: Id; focusedNodeId?: Id;
onFocusNode: (id?: Id) => void; onFocusNode: (id?: Id) => void;
@@ -625,14 +636,14 @@ const ContextMenu: React.FC<{
}; };
type TreeListStackItem = { type TreeListStackItem = {
node: UINode; node: ClientNode;
depth: number; depth: number;
isChildOfSelectedNode: boolean; isChildOfSelectedNode: boolean;
selectedNodeDepth: number; selectedNodeDepth: number;
}; };
function toTreeNodes( function toTreeNodes(
nodes: Map<Id, UINode>, nodes: Map<Id, ClientNode>,
rootId: Id, rootId: Id,
expandedNodes: Set<Id>, expandedNodes: Set<Id>,
selectedNode: Id | undefined, selectedNode: Id | undefined,

View File

@@ -8,7 +8,7 @@
*/ */
import React, {useEffect, useMemo, useRef} from 'react'; import React, {useEffect, useMemo, useRef} from 'react';
import {Bounds, Coordinate, Id, UINode} from '../ClientTypes'; import {Bounds, Coordinate, Id, ClientNode} from '../ClientTypes';
import {NestedNode, OnSelectNode} from '../DesktopTypes'; import {NestedNode, OnSelectNode} from '../DesktopTypes';
import {produce, styled, theme, usePlugin, useValue} from 'flipper-plugin'; import {produce, styled, theme, usePlugin, useValue} from 'flipper-plugin';
@@ -21,7 +21,7 @@ import {useDelay} from '../hooks/useDelay';
export const Visualization2D: React.FC< export const Visualization2D: React.FC<
{ {
width: number; width: number;
nodes: Map<Id, UINode>; nodes: Map<Id, ClientNode>;
onSelectNode: OnSelectNode; onSelectNode: OnSelectNode;
} & React.HTMLAttributes<HTMLDivElement> } & React.HTMLAttributes<HTMLDivElement>
> = ({width, nodes, onSelectNode}) => { > = ({width, nodes, onSelectNode}) => {
@@ -255,7 +255,13 @@ function Visualization2DNode({
); );
} }
function HoveredOverlay({nodeId, nodes}: {nodeId: Id; nodes: Map<Id, UINode>}) { function HoveredOverlay({
nodeId,
nodes,
}: {
nodeId: Id;
nodes: Map<Id, ClientNode>;
}) {
const node = nodes.get(nodeId); const node = nodes.get(nodeId);
const isVisible = useDelay(longHoverDelay); const isVisible = useDelay(longHoverDelay);
@@ -279,7 +285,7 @@ function HoveredOverlay({nodeId, nodes}: {nodeId: Id; nodes: Map<Id, UINode>}) {
const OverlayBorder = styled.div<{ const OverlayBorder = styled.div<{
type: 'selected' | 'hovered'; type: 'selected' | 'hovered';
nodeId: Id; nodeId: Id;
nodes: Map<Id, UINode>; nodes: Map<Id, ClientNode>;
}>(({type, nodeId, nodes}) => { }>(({type, nodeId, nodes}) => {
const offset = getTotalOffset(nodeId, nodes); const offset = getTotalOffset(nodeId, nodes);
const node = nodes.get(nodeId); const node = nodes.get(nodeId);
@@ -305,7 +311,7 @@ const OverlayBorder = styled.div<{
* computes the x,y offset of a given node from the root of the visualization * computes the x,y offset of a given node from the root of the visualization
* in node coordinates * in node coordinates
*/ */
function getTotalOffset(id: Id, nodes: Map<Id, UINode>): Coordinate { function getTotalOffset(id: Id, nodes: Map<Id, ClientNode>): Coordinate {
const offset = {x: 0, y: 0}; const offset = {x: 0, y: 0};
let curId: Id | undefined = id; let curId: Id | undefined = id;
@@ -321,7 +327,7 @@ function getTotalOffset(id: Id, nodes: Map<Id, UINode>): Coordinate {
return offset; return offset;
} }
const ContextMenu: React.FC<{nodes: Map<Id, UINode>}> = ({children}) => { const ContextMenu: React.FC<{nodes: Map<Id, ClientNode>}> = ({children}) => {
const instance = usePlugin(plugin); const instance = usePlugin(plugin);
const focusedNodeId = useValue(instance.uiState.focusedNode); const focusedNodeId = useValue(instance.uiState.focusedNode);
@@ -392,9 +398,9 @@ function toPx(n: number) {
function toNestedNode( function toNestedNode(
rootId: Id, rootId: Id,
nodes: Map<Id, UINode>, nodes: Map<Id, ClientNode>,
): NestedNode | undefined { ): NestedNode | undefined {
function uiNodeToNestedNode(node: UINode): NestedNode { function uiNodeToNestedNode(node: ClientNode): NestedNode {
const nonNullChildren = node.children.filter( const nonNullChildren = node.children.filter(
(childId) => nodes.get(childId) != null, (childId) => nodes.get(childId) != null,
); );

View File

@@ -9,14 +9,14 @@
import React from 'react'; import React from 'react';
import {UINode} from '../../ClientTypes'; import {ClientNode} from '../../ClientTypes';
export async function prefetchSourceFileLocation(_: UINode) {} export async function prefetchSourceFileLocation(_: ClientNode) {}
export function IDEContextMenuItems(_: {node: UINode}) { export function IDEContextMenuItems(_: {node: ClientNode}) {
return <></>; return <></>;
} }
export function BigGrepContextMenuItems(_: {node: UINode}) { export function BigGrepContextMenuItems(_: {node: ClientNode}) {
return <></>; return <></>;
} }

View File

@@ -18,7 +18,7 @@ import {
theme, theme,
} from 'flipper-plugin'; } from 'flipper-plugin';
import {useHotkeys} from 'react-hotkeys-hook'; import {useHotkeys} from 'react-hotkeys-hook';
import {Id, Metadata, MetadataId, UINode} from '../ClientTypes'; import {Id, Metadata, MetadataId, ClientNode} from '../ClientTypes';
import {PerfStats} from './PerfStats'; import {PerfStats} from './PerfStats';
import {Visualization2D} from './Visualization2D'; import {Visualization2D} from './Visualization2D';
import {Inspector} from './sidebar/Inspector'; import {Inspector} from './sidebar/Inspector';
@@ -35,7 +35,7 @@ export function Component() {
const rootId = useValue(instance.rootId); const rootId = useValue(instance.rootId);
const streamState = useValue(instance.uiState.streamState); const streamState = useValue(instance.uiState.streamState);
const visualiserWidth = useValue(instance.uiState.visualiserWidth); const visualiserWidth = useValue(instance.uiState.visualiserWidth);
const nodes: Map<Id, UINode> = useValue(instance.nodes); const nodes: Map<Id, ClientNode> = useValue(instance.nodes);
const metadata: Map<MetadataId, Metadata> = useValue(instance.metadata); const metadata: Map<MetadataId, Metadata> = useValue(instance.metadata);
const [showPerfStats, setShowPerfStats] = useState(false); const [showPerfStats, setShowPerfStats] = useState(false);

View File

@@ -19,7 +19,7 @@ import {
usePlugin, usePlugin,
useValue, useValue,
} from 'flipper-plugin'; } from 'flipper-plugin';
import {Id, Metadata, MetadataId, UINode} from '../../ClientTypes'; import {Id, Metadata, MetadataId, ClientNode} from '../../ClientTypes';
import {IdentityInspector} from './inspector/IdentityInspector'; import {IdentityInspector} from './inspector/IdentityInspector';
import {AttributesInspector} from './inspector/AttributesInspector'; import {AttributesInspector} from './inspector/AttributesInspector';
import {Tooltip} from 'antd'; import {Tooltip} from 'antd';
@@ -29,7 +29,7 @@ import {FrameworkEventsInspector} from './inspector/FrameworkEventsInspector';
type Props = { type Props = {
os: DeviceOS; os: DeviceOS;
nodes: Map<Id, UINode>; nodes: Map<Id, ClientNode>;
metadata: Map<MetadataId, Metadata>; metadata: Map<MetadataId, Metadata>;
showExtra: (element: ReactNode) => void; showExtra: (element: ReactNode) => void;
}; };

View File

@@ -13,7 +13,7 @@ import {
InspectableObject, InspectableObject,
Metadata, Metadata,
MetadataId, MetadataId,
UINode, ClientNode,
} from '../../../ClientTypes'; } from '../../../ClientTypes';
import {DataInspector, Panel, styled} from 'flipper-plugin'; import {DataInspector, Panel, styled} from 'flipper-plugin';
import {Col, Row} from 'antd'; import {Col, Row} from 'antd';
@@ -244,7 +244,7 @@ function createSection(
type InspectorMode = 'layout' | 'attribute'; type InspectorMode = 'layout' | 'attribute';
type Props = { type Props = {
node: UINode; node: ClientNode;
metadata: Map<MetadataId, Metadata>; metadata: Map<MetadataId, Metadata>;
mode: InspectorMode; mode: InspectorMode;
rawEnabled?: boolean; rawEnabled?: boolean;

View File

@@ -8,10 +8,10 @@
*/ */
import React from 'react'; import React from 'react';
import {UINode} from '../../../ClientTypes'; import {ClientNode} from '../../../ClientTypes';
type Props = { type Props = {
node: UINode; node: ClientNode;
}; };
export const DocumentationInspector: React.FC<Props> = () => { export const DocumentationInspector: React.FC<Props> = () => {

View File

@@ -9,12 +9,12 @@
import {Button} from 'antd'; import {Button} from 'antd';
import {theme, TimelineDataDescription} from 'flipper-plugin'; import {theme, TimelineDataDescription} from 'flipper-plugin';
import {FrameworkEvent, UINode} from '../../../ClientTypes'; import {FrameworkEvent, ClientNode} from '../../../ClientTypes';
import React, {ReactNode, useState} from 'react'; import React, {ReactNode, useState} from 'react';
import {StackTraceInspector} from './StackTraceInspector'; import {StackTraceInspector} from './StackTraceInspector';
type Props = { type Props = {
node: UINode; node: ClientNode;
events: readonly FrameworkEvent[]; events: readonly FrameworkEvent[];
showExtra?: (element: ReactNode) => void; showExtra?: (element: ReactNode) => void;
}; };

View File

@@ -9,13 +9,13 @@
import React from 'react'; import React from 'react';
import {Col, Row} from 'antd'; import {Col, Row} from 'antd';
import {UINode} from '../../../ClientTypes'; import {ClientNode} from '../../../ClientTypes';
import {styled, theme} from 'flipper-plugin'; import {styled, theme} from 'flipper-plugin';
import {CodeInspector} from './fb-stubs/CodeInspector'; import {CodeInspector} from './fb-stubs/CodeInspector';
import {TopSpacedContainerStyle} from './Styles'; import {TopSpacedContainerStyle} from './Styles';
type Props = { type Props = {
node: UINode; node: ClientNode;
}; };
const IdentityKey = styled.div({ const IdentityKey = styled.div({

View File

@@ -8,7 +8,7 @@
*/ */
import {DeviceOS} from 'flipper-plugin'; import {DeviceOS} from 'flipper-plugin';
import {Id, Metadata, UINode} from '../ClientTypes'; import {Id, Metadata, ClientNode} from '../ClientTypes';
import {StreamInterceptor} from '../DesktopTypes'; import {StreamInterceptor} from '../DesktopTypes';
export function getStreamInterceptor(_: DeviceOS): StreamInterceptor { export function getStreamInterceptor(_: DeviceOS): StreamInterceptor {
@@ -21,8 +21,8 @@ class NoOpStreamInterceptor implements StreamInterceptor {
} }
async transformNodes( async transformNodes(
nodes: Map<Id, UINode>, nodes: Map<Id, ClientNode>,
): Promise<[Map<Id, UINode>, Metadata[]]> { ): Promise<[Map<Id, ClientNode>, Metadata[]]> {
return [nodes, []]; return [nodes, []];
} }

View File

@@ -24,7 +24,7 @@ import {
MetadataId, MetadataId,
PerformanceStatsEvent, PerformanceStatsEvent,
SnapshotInfo, SnapshotInfo,
UINode, ClientNode,
} from './ClientTypes'; } from './ClientTypes';
import { import {
UIState, UIState,
@@ -42,7 +42,7 @@ import {prefetchSourceFileLocation} from './components/fb-stubs/IDEContextMenu';
type LiveClientState = { type LiveClientState = {
snapshotInfo: SnapshotInfo | null; snapshotInfo: SnapshotInfo | null;
nodes: Map<Id, UINode>; nodes: Map<Id, ClientNode>;
}; };
type PendingData = { type PendingData = {
@@ -193,7 +193,7 @@ export function plugin(client: PluginClient<Events>) {
perfEvents.append(event); perfEvents.append(event);
}); });
const nodesAtom = createState<Map<Id, UINode>>(new Map()); const nodesAtom = createState<Map<Id, ClientNode>>(new Map());
const frameworkEvents = createDataSource<FrameworkEvent>([], { const frameworkEvents = createDataSource<FrameworkEvent>([], {
indices: [['nodeId']], indices: [['nodeId']],
limit: 10000, limit: 10000,
@@ -327,7 +327,7 @@ export function plugin(client: PluginClient<Events>) {
//todo deal with racecondition, where bloks screen is fetching, takes time then you go back get more recent frame then bloks screen comes and overrites it //todo deal with racecondition, where bloks screen is fetching, takes time then you go back get more recent frame then bloks screen comes and overrites it
function applyFrameData( function applyFrameData(
nodes: Map<Id, UINode>, nodes: Map<Id, ClientNode>,
snapshotInfo: SnapshotInfo | undefined, snapshotInfo: SnapshotInfo | undefined,
) { ) {
liveClientData = produce(liveClientData, (draft) => { liveClientData = produce(liveClientData, (draft) => {
@@ -390,7 +390,10 @@ export function plugin(client: PluginClient<Events>) {
}; };
} }
function uiActions(uiState: UIState, nodes: Atom<Map<Id, UINode>>): UIActions { function uiActions(
uiState: UIState,
nodes: Atom<Map<Id, ClientNode>>,
): UIActions {
const onExpandNode = (node: Id) => { const onExpandNode = (node: Id) => {
uiState.expandedNodes.update((draft) => { uiState.expandedNodes.update((draft) => {
draft.add(node); draft.add(node);
@@ -483,7 +486,10 @@ function uiActions(uiState: UIState, nodes: Atom<Map<Id, UINode>>): UIActions {
}; };
} }
function checkFocusedNodeStillActive(uiState: UIState, nodes: Map<Id, UINode>) { function checkFocusedNodeStillActive(
uiState: UIState,
nodes: Map<Id, ClientNode>,
) {
const focusedNodeId = uiState.focusedNode.get(); const focusedNodeId = uiState.focusedNode.get();
const focusedNode = focusedNodeId && nodes.get(focusedNodeId); const focusedNode = focusedNodeId && nodes.get(focusedNodeId);
if (!focusedNode || !isFocusedNodeAncestryAllActive(focusedNode, nodes)) { if (!focusedNode || !isFocusedNodeAncestryAllActive(focusedNode, nodes)) {
@@ -492,8 +498,8 @@ function checkFocusedNodeStillActive(uiState: UIState, nodes: Map<Id, UINode>) {
} }
function isFocusedNodeAncestryAllActive( function isFocusedNodeAncestryAllActive(
focused: UINode, focused: ClientNode,
nodes: Map<Id, UINode>, nodes: Map<Id, ClientNode>,
): boolean { ): boolean {
let node = focused; let node = focused;
@@ -519,7 +525,10 @@ function isFocusedNodeAncestryAllActive(
return false; return false;
} }
function collapseinActiveChildren(node: UINode, expandedNodes: Draft<Set<Id>>) { function collapseinActiveChildren(
node: ClientNode,
expandedNodes: Draft<Set<Id>>,
) {
if (node.activeChild) { if (node.activeChild) {
expandedNodes.add(node.activeChild); expandedNodes.add(node.activeChild);
for (const child of node.children) { for (const child of node.children) {