Files
flipper/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx
Sash Zats 1c3720fd5c Add ui-debugger modes
Summary:
## This diff: Add debugger mode infrastructure

We want to enable infrastructure allowing adding modes in the future without branching client and plugin code

This infra allows for the client to send a message informing flipper about the supported modes (following diffs)

Currently broadcasting mode change event over to the client

Reviewed By: lblasa

Differential Revision: D49385860

fbshipit-source-id: 2db6f65064d1ff7e1f57b2da93c3ed195400fb7f
2023-09-20 11:37:12 -07:00

216 lines
6.2 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 {plugin} from '../../index';
import {
Button,
Input,
Modal,
Tooltip,
Typography,
Space,
Switch,
Badge,
Dropdown,
} from 'antd';
import {
EyeOutlined,
PauseCircleOutlined,
AppstoreOutlined,
PlayCircleOutlined,
SearchOutlined,
} from '@ant-design/icons';
import {usePlugin, useValue, Layout} from 'flipper-plugin';
import {FrameworkEventMetadata, FrameworkEventType} from '../../ClientTypes';
import {
buildTreeSelectData,
FrameworkEventsTreeSelect,
} from '../shared/FrameworkEventsTreeSelect';
import {TraversalMode} from '../../DesktopTypes';
export const TreeControls: React.FC = () => {
const instance = usePlugin(plugin);
const searchTerm = useValue(instance.uiState.searchTerm);
const isPaused = useValue(instance.uiState.isPaused);
const filterMainThreadMonitoring = useValue(
instance.uiState.filterMainThreadMonitoring,
);
const frameworkEventMonitoring: Map<FrameworkEventType, boolean> = useValue(
instance.uiState.frameworkEventMonitoring,
);
const [showFrameworkEventsModal, setShowFrameworkEventsModal] =
useState(false);
const frameworkEventMetadata = useValue(instance.frameworkEventMetadata);
const currentTraversalMode = useValue(instance.uiState.currentTraversalMode);
const supportedTraversalModes = useValue(
instance.uiState.supportedTraversalModes,
);
const labelForMode = (mode: TraversalMode) => {
switch (mode) {
case 'view-hierarchy':
return 'UI Hierarchy';
case 'accessibility-hierarchy':
return 'Accessibility Hierarchy';
}
};
const menus = supportedTraversalModes.map((mode) => ({
key: mode,
label: labelForMode(mode),
}));
return (
<Layout.Horizontal gap="medium" pad="medium">
{supportedTraversalModes.length > 1 ? (
<Dropdown
menu={{
selectable: true,
selectedKeys: [currentTraversalMode],
items: menus,
onSelect: async (event) => {
const mode = event.selectedKeys[0] as TraversalMode;
instance.uiActions.setCurrentTraversalMode(mode); // update UI
await instance.onTraversalModeChange(mode); // update mobile client
},
}}>
<Tooltip title="Debugger Mode">
<Button shape="circle">
<AppstoreOutlined />
</Button>
</Tooltip>
</Dropdown>
) : null}
<Input
value={searchTerm}
onChange={(e) => {
instance.uiActions.onSearchTermUpdated(e.target.value);
}}
prefix={<SearchOutlined />}
placeholder="Search"
/>
<Button
type="default"
shape="circle"
onClick={instance.uiActions.onPlayPauseToggled}
icon={
<Tooltip
title={isPaused ? 'Resume live updates' : 'Pause incoming updates'}>
{isPaused ? <PlayCircleOutlined /> : <PauseCircleOutlined />}
</Tooltip>
}></Button>
{frameworkEventMonitoring.size > 0 && (
<>
<Badge
size="small"
count={
[...frameworkEventMonitoring.values()].filter(
(val) => val === true,
).length
}>
<Button
type="default"
shape="circle"
onClick={() => {
setShowFrameworkEventsModal(true);
}}
icon={
<Tooltip title="Framework event monitoring">
<EyeOutlined />
</Tooltip>
}></Button>
</Badge>
<FrameworkEventsMonitoringModal
metadata={frameworkEventMetadata}
filterMainThreadMonitoring={filterMainThreadMonitoring}
onSetFilterMainThreadMonitoring={
instance.uiActions.onSetFilterMainThreadMonitoring
}
frameworkEventTypes={[...frameworkEventMonitoring.entries()]}
onSetEventMonitored={
instance.uiActions.onSetFrameworkEventMonitored
}
visible={showFrameworkEventsModal}
onCancel={() => setShowFrameworkEventsModal(false)}
/>
</>
)}
</Layout.Horizontal>
);
};
function FrameworkEventsMonitoringModal({
visible,
onCancel,
onSetEventMonitored,
onSetFilterMainThreadMonitoring,
filterMainThreadMonitoring,
frameworkEventTypes,
metadata,
}: {
metadata: Map<FrameworkEventType, FrameworkEventMetadata>;
visible: boolean;
onCancel: () => void;
onSetEventMonitored: (
eventType: FrameworkEventType,
monitored: boolean,
) => void;
filterMainThreadMonitoring: boolean;
onSetFilterMainThreadMonitoring: (toggled: boolean) => void;
frameworkEventTypes: [FrameworkEventType, boolean][];
}) {
const selectedFrameworkEvents = frameworkEventTypes
.filter(([, selected]) => selected)
.map(([eventType]) => eventType);
const treeData = buildTreeSelectData(
frameworkEventTypes.map(([type]) => type),
metadata,
);
return (
<Modal
title="Framework event monitoring"
open={visible}
footer={null}
onCancel={onCancel}>
<Space direction="vertical" size="large">
<Typography.Text>
Monitoring an event will cause the relevant node in the visualizer and
tree to highlight briefly. Additionally counter will show the number
of matching events in the tree
</Typography.Text>
<FrameworkEventsTreeSelect
placeholder="Select node types to monitor"
onSetEventSelected={onSetEventMonitored}
selected={selectedFrameworkEvents}
treeData={treeData}
/>
<Layout.Horizontal gap="medium">
<Switch
checked={filterMainThreadMonitoring}
onChange={(event) => {
onSetFilterMainThreadMonitoring(event);
}}
/>
<Typography.Text>
Only highlight events that occured on the main thread
</Typography.Text>
</Layout.Horizontal>
</Space>
</Modal>
);
}