Make flipper messages generally available, remove self inspection infra structure
Summary: Changelog: Flipper message debugging moved from a separate device to the console tab This makes message debugging easier accessible, and in production (recently requested at GH). Also it clears up a lot of infra that was created just to make flipper a self recursive inspection device + a separate plugin. While fun, a hardcoded setup is just a bit more simpler (no exception rules and better static verification) Reviewed By: nikoant Differential Revision: D29487811 fbshipit-source-id: b412adc3ef5bd831001333443b432b6c0f934a5e
This commit is contained in:
committed by
Facebook GitHub Bot
parent
8da7495a1a
commit
328ba9513c
@@ -31,10 +31,13 @@ import {
|
|||||||
_SandyPluginInstance,
|
_SandyPluginInstance,
|
||||||
_getFlipperLibImplementation,
|
_getFlipperLibImplementation,
|
||||||
} from 'flipper-plugin';
|
} from 'flipper-plugin';
|
||||||
import {flipperMessagesClientPlugin} from './utils/self-inspection/plugins/FlipperMessagesClientPlugin';
|
|
||||||
import {freeze} from 'immer';
|
import {freeze} from 'immer';
|
||||||
import GK from './fb-stubs/GK';
|
import GK from './fb-stubs/GK';
|
||||||
import {message} from 'antd';
|
import {message} from 'antd';
|
||||||
|
import {
|
||||||
|
isFlipperMessageDebuggingEnabled,
|
||||||
|
registerFlipperDebugMessage,
|
||||||
|
} from './chrome/FlipperMessages';
|
||||||
|
|
||||||
type Plugins = Set<string>;
|
type Plugins = Set<string>;
|
||||||
type PluginsArr = Array<string>;
|
type PluginsArr = Array<string>;
|
||||||
@@ -384,11 +387,8 @@ export default class Client extends EventEmitter {
|
|||||||
|
|
||||||
const {id, method} = data;
|
const {id, method} = data;
|
||||||
|
|
||||||
if (
|
if (isFlipperMessageDebuggingEnabled()) {
|
||||||
data.params?.api != 'flipper-messages' &&
|
registerFlipperDebugMessage({
|
||||||
flipperMessagesClientPlugin.isConnected()
|
|
||||||
) {
|
|
||||||
flipperMessagesClientPlugin.newMessage({
|
|
||||||
device: this.deviceSync?.displayTitle(),
|
device: this.deviceSync?.displayTitle(),
|
||||||
app: this.query.app,
|
app: this.query.app,
|
||||||
flipperInternalMethod: method,
|
flipperInternalMethod: method,
|
||||||
@@ -416,7 +416,7 @@ export default class Client extends EventEmitter {
|
|||||||
const params: Params = data.params;
|
const params: Params = data.params;
|
||||||
const bytes = msg.length * 2; // string lengths are measured in UTF-16 units (not characters), so 2 bytes per char
|
const bytes = msg.length * 2; // string lengths are measured in UTF-16 units (not characters), so 2 bytes per char
|
||||||
emitBytesReceived(params.api, bytes);
|
emitBytesReceived(params.api, bytes);
|
||||||
if (bytes > 5 * 1024 * 1024 && params.api !== 'flipper-messages') {
|
if (bytes > 5 * 1024 * 1024) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Plugin '${params.api}' received excessively large message for '${
|
`Plugin '${params.api}' received excessively large message for '${
|
||||||
params.method
|
params.method
|
||||||
@@ -462,12 +462,7 @@ export default class Client extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Flipper debug as full client is overkill, clean up
|
if (!handled && !isProduction()) {
|
||||||
if (
|
|
||||||
!handled &&
|
|
||||||
!isProduction() &&
|
|
||||||
params.api !== 'flipper-messages'
|
|
||||||
) {
|
|
||||||
console.warn(`Unhandled message ${params.api}.${params.method}`);
|
console.warn(`Unhandled message ${params.api}.${params.method}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -597,8 +592,8 @@ export default class Client extends EventEmitter {
|
|||||||
|
|
||||||
this.onResponse(response, resolve, reject);
|
this.onResponse(response, resolve, reject);
|
||||||
|
|
||||||
if (flipperMessagesClientPlugin.isConnected()) {
|
if (isFlipperMessageDebuggingEnabled()) {
|
||||||
flipperMessagesClientPlugin.newMessage({
|
registerFlipperDebugMessage({
|
||||||
device: this.deviceSync?.displayTitle(),
|
device: this.deviceSync?.displayTitle(),
|
||||||
app: this.query.app,
|
app: this.query.app,
|
||||||
flipperInternalMethod: method,
|
flipperInternalMethod: method,
|
||||||
@@ -625,8 +620,8 @@ export default class Client extends EventEmitter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flipperMessagesClientPlugin.isConnected()) {
|
if (isFlipperMessageDebuggingEnabled()) {
|
||||||
flipperMessagesClientPlugin.newMessage({
|
registerFlipperDebugMessage({
|
||||||
device: this.deviceSync?.displayTitle(),
|
device: this.deviceSync?.displayTitle(),
|
||||||
app: this.query.app,
|
app: this.query.app,
|
||||||
flipperInternalMethod: method,
|
flipperInternalMethod: method,
|
||||||
@@ -711,8 +706,8 @@ export default class Client extends EventEmitter {
|
|||||||
this.connection.fireAndForget({data: JSON.stringify(data)});
|
this.connection.fireAndForget({data: JSON.stringify(data)});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flipperMessagesClientPlugin.isConnected()) {
|
if (isFlipperMessageDebuggingEnabled()) {
|
||||||
flipperMessagesClientPlugin.newMessage({
|
registerFlipperDebugMessage({
|
||||||
device: this.deviceSync?.displayTitle(),
|
device: this.deviceSync?.displayTitle(),
|
||||||
app: this.query.app,
|
app: this.query.app,
|
||||||
flipperInternalMethod: method,
|
flipperInternalMethod: method,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {useMemo} from 'react';
|
import {useMemo} from 'react';
|
||||||
import {Button, ButtonGroup, Layout} from '../ui';
|
import {Button, Layout} from '../ui';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Console, Hook} from 'console-feed';
|
import {Console, Hook} from 'console-feed';
|
||||||
import type {Methods} from 'console-feed/lib/definitions/Methods';
|
import type {Methods} from 'console-feed/lib/definitions/Methods';
|
||||||
@@ -92,13 +92,11 @@ export function ConsoleLogs() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Top>
|
<Layout.Top>
|
||||||
<Toolbar>
|
<Toolbar wash>
|
||||||
<ButtonGroup>
|
|
||||||
<Button onClick={clearLogs} icon="trash">
|
<Button onClick={clearLogs} icon="trash">
|
||||||
Clear Logs
|
Clear Logs
|
||||||
</Button>
|
</Button>
|
||||||
<Button dropdown={dropdown}>Log Levels</Button>
|
<Button dropdown={dropdown}>Log Levels</Button>
|
||||||
</ButtonGroup>
|
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
<Layout.ScrollContainer vertical>
|
<Layout.ScrollContainer vertical>
|
||||||
<Console
|
<Console
|
||||||
|
|||||||
29
desktop/app/src/chrome/FlipperDevTools.tsx
Normal file
29
desktop/app/src/chrome/FlipperDevTools.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its 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 {Layout} from '../ui';
|
||||||
|
import React from 'react';
|
||||||
|
import {Tab, Tabs} from 'flipper-plugin';
|
||||||
|
import {ConsoleLogs} from './ConsoleLogs';
|
||||||
|
import {FlipperMessages} from './FlipperMessages';
|
||||||
|
|
||||||
|
export function FlipperDevTools() {
|
||||||
|
return (
|
||||||
|
<Layout.Container pad grow>
|
||||||
|
<Tabs grow>
|
||||||
|
<Tab tab="Console">
|
||||||
|
<ConsoleLogs />
|
||||||
|
</Tab>
|
||||||
|
<Tab tab="Messages">
|
||||||
|
<FlipperMessages />
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
</Layout.Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
209
desktop/app/src/chrome/FlipperMessages.tsx
Normal file
209
desktop/app/src/chrome/FlipperMessages.tsx
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its 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 {
|
||||||
|
DataInspector,
|
||||||
|
DataTable,
|
||||||
|
DataTableColumn,
|
||||||
|
Layout,
|
||||||
|
createState,
|
||||||
|
createDataSource,
|
||||||
|
DetailSidebar,
|
||||||
|
Panel,
|
||||||
|
theme,
|
||||||
|
styled,
|
||||||
|
useValue,
|
||||||
|
} from 'flipper-plugin';
|
||||||
|
import {Button} from 'antd';
|
||||||
|
import {
|
||||||
|
DeleteOutlined,
|
||||||
|
PauseCircleOutlined,
|
||||||
|
PlayCircleOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import React, {useCallback, useState} from 'react';
|
||||||
|
|
||||||
|
export type MessageInfo = {
|
||||||
|
time?: Date;
|
||||||
|
device?: string;
|
||||||
|
app: string;
|
||||||
|
flipperInternalMethod?: string;
|
||||||
|
plugin?: string;
|
||||||
|
pluginMethod?: string;
|
||||||
|
payload?: any;
|
||||||
|
direction:
|
||||||
|
| 'toClient:call'
|
||||||
|
| 'toClient:send'
|
||||||
|
| 'toFlipper:message'
|
||||||
|
| 'toFlipper:response';
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface MessageRow extends MessageInfo {
|
||||||
|
time: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Placeholder = styled(Layout.Container)({
|
||||||
|
center: true,
|
||||||
|
color: theme.textColorPlaceholder,
|
||||||
|
fontSize: 18,
|
||||||
|
});
|
||||||
|
|
||||||
|
function createRow(message: MessageInfo): MessageRow {
|
||||||
|
return {
|
||||||
|
...message,
|
||||||
|
time: message.time == null ? new Date() : message.time,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const COLUMN_CONFIG: DataTableColumn<MessageRow>[] = [
|
||||||
|
{
|
||||||
|
key: 'time',
|
||||||
|
title: 'Time',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'device',
|
||||||
|
title: 'Device',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'app',
|
||||||
|
title: 'App',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'flipperInternalMethod',
|
||||||
|
title: 'Flipper Internal Method',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'plugin',
|
||||||
|
title: 'Plugin',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'pluginMethod',
|
||||||
|
title: 'Method',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'direction',
|
||||||
|
title: 'Direction',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const flipperDebugMessages = createDataSource<MessageRow>([], {
|
||||||
|
limit: 1024 * 10,
|
||||||
|
persist: 'messages',
|
||||||
|
});
|
||||||
|
const flipperDebugMessagesEnabled = createState(false);
|
||||||
|
|
||||||
|
export function registerFlipperDebugMessage(message: MessageInfo) {
|
||||||
|
if (flipperDebugMessagesEnabled.get()) {
|
||||||
|
flipperDebugMessages.append(createRow(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFlipperMessageDebuggingEnabled(): boolean {
|
||||||
|
return flipperDebugMessagesEnabled.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// exposed for testing
|
||||||
|
export function setFlipperMessageDebuggingEnabled(value: boolean) {
|
||||||
|
flipperDebugMessagesEnabled.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// exposed for testing
|
||||||
|
export function clearFlipperDebugMessages() {
|
||||||
|
flipperDebugMessages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// exposed for testing ONLY!
|
||||||
|
export function getFlipperDebugMessages() {
|
||||||
|
return flipperDebugMessages.records();
|
||||||
|
}
|
||||||
|
|
||||||
|
function Sidebar({selection}: {selection: undefined | MessageRow}) {
|
||||||
|
const renderExtra = (extra: any) => (
|
||||||
|
<Panel title={'Payload'} collapsible={false}>
|
||||||
|
<DataInspector data={extra} expandRoot={false} />
|
||||||
|
</Panel>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DetailSidebar>
|
||||||
|
{selection != null ? (
|
||||||
|
renderExtra(selection.payload)
|
||||||
|
) : (
|
||||||
|
<Placeholder grow pad="large">
|
||||||
|
Select a message to view details
|
||||||
|
</Placeholder>
|
||||||
|
)}
|
||||||
|
</DetailSidebar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PauseResumeButton = () => {
|
||||||
|
const paused = !useValue(flipperDebugMessagesEnabled);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
title={`Click to enable tracing flipper messages`}
|
||||||
|
danger={!paused}
|
||||||
|
onClick={() => {
|
||||||
|
flipperDebugMessagesEnabled.update((v) => !v);
|
||||||
|
}}>
|
||||||
|
{paused ? <PlayCircleOutlined /> : <PauseCircleOutlined />}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function FlipperMessages() {
|
||||||
|
const [selection, setSelection] = useState<MessageRow | undefined>();
|
||||||
|
const paused = !useValue(flipperDebugMessagesEnabled);
|
||||||
|
|
||||||
|
const clearTableButton = (
|
||||||
|
<Button
|
||||||
|
title="Clear logs"
|
||||||
|
onClick={() => {
|
||||||
|
clearFlipperDebugMessages();
|
||||||
|
setSelection(undefined);
|
||||||
|
}}>
|
||||||
|
<DeleteOutlined />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderEmpty = useCallback(
|
||||||
|
() => (
|
||||||
|
<Layout.Container center pad gap style={{width: '100%', marginTop: 200}}>
|
||||||
|
{paused ? (
|
||||||
|
<>
|
||||||
|
Click to enable debugging Flipper messages between the Flipper
|
||||||
|
application and connected clients: <PauseResumeButton />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'Waiting for data...'
|
||||||
|
)}
|
||||||
|
</Layout.Container>
|
||||||
|
),
|
||||||
|
[paused],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.Container grow>
|
||||||
|
<DataTable<MessageRow>
|
||||||
|
dataSource={flipperDebugMessages}
|
||||||
|
columns={COLUMN_CONFIG}
|
||||||
|
onSelect={setSelection}
|
||||||
|
enableAutoScroll
|
||||||
|
onRenderEmpty={renderEmpty}
|
||||||
|
extraActions={
|
||||||
|
<>
|
||||||
|
<PauseResumeButton />
|
||||||
|
{clearTableButton}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Sidebar selection={selection} />
|
||||||
|
</Layout.Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,45 +7,67 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TestUtils} from 'flipper-plugin';
|
import * as React from 'react';
|
||||||
|
import {act, render} from '@testing-library/react';
|
||||||
|
|
||||||
import * as Plugin from '../';
|
import {
|
||||||
import {MessageRow} from '../';
|
clearFlipperDebugMessages,
|
||||||
|
FlipperMessages,
|
||||||
|
getFlipperDebugMessages,
|
||||||
|
MessageRow,
|
||||||
|
registerFlipperDebugMessage,
|
||||||
|
setFlipperMessageDebuggingEnabled,
|
||||||
|
} from '../FlipperMessages';
|
||||||
|
|
||||||
const fixRowTimestamps = (r: MessageRow): MessageRow => ({
|
const fixRowTimestamps = (r: MessageRow): MessageRow => ({
|
||||||
...r,
|
...r,
|
||||||
time: new Date(Date.UTC(0, 0, 0, 0, 0, 0)),
|
time: new Date(Date.UTC(0, 0, 0, 0, 0, 0)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
clearFlipperDebugMessages();
|
||||||
|
setFlipperMessageDebuggingEnabled(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
clearFlipperDebugMessages();
|
||||||
|
setFlipperMessageDebuggingEnabled(false);
|
||||||
|
});
|
||||||
|
|
||||||
test('It can store rows', () => {
|
test('It can store rows', () => {
|
||||||
const {instance, ...plugin} = TestUtils.startPlugin(Plugin);
|
registerFlipperDebugMessage({
|
||||||
|
|
||||||
expect(instance.rows.records()).toEqual([]);
|
|
||||||
expect(instance.highlightedRow.get()).toBeUndefined();
|
|
||||||
|
|
||||||
plugin.sendEvent('newMessage', {
|
|
||||||
app: 'Flipper',
|
app: 'Flipper',
|
||||||
direction: 'toFlipper',
|
direction: 'toFlipper:message',
|
||||||
});
|
});
|
||||||
|
|
||||||
plugin.sendEvent('newMessage', {
|
registerFlipperDebugMessage({
|
||||||
app: 'FB4A',
|
app: 'FB4A',
|
||||||
direction: 'toClient',
|
direction: 'toClient:call',
|
||||||
device: 'Android Phone',
|
device: 'Android Phone',
|
||||||
payload: {hello: 'world'},
|
payload: {hello: 'world'},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(instance.rows.records().map(fixRowTimestamps)).toMatchInlineSnapshot(`
|
setFlipperMessageDebuggingEnabled(false);
|
||||||
|
|
||||||
|
registerFlipperDebugMessage({
|
||||||
|
app: 'FB4A',
|
||||||
|
direction: 'toClient:call',
|
||||||
|
device: 'Android PhoneTEst',
|
||||||
|
payload: {hello: 'world'},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getFlipperDebugMessages().map(fixRowTimestamps))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
Array [
|
Array [
|
||||||
Object {
|
Object {
|
||||||
"app": "Flipper",
|
"app": "Flipper",
|
||||||
"direction": "toFlipper",
|
"direction": "toFlipper:message",
|
||||||
"time": 1899-12-31T00:00:00.000Z,
|
"time": 1899-12-31T00:00:00.000Z,
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"app": "FB4A",
|
"app": "FB4A",
|
||||||
"device": "Android Phone",
|
"device": "Android Phone",
|
||||||
"direction": "toClient",
|
"direction": "toClient:call",
|
||||||
"payload": Object {
|
"payload": Object {
|
||||||
"hello": "world",
|
"hello": "world",
|
||||||
},
|
},
|
||||||
@@ -56,62 +78,44 @@ test('It can store rows', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('It can clear', () => {
|
test('It can clear', () => {
|
||||||
const {instance, ...plugin} = TestUtils.startPlugin(Plugin);
|
registerFlipperDebugMessage({
|
||||||
|
|
||||||
expect(instance.rows.records()).toEqual([]);
|
|
||||||
expect(instance.highlightedRow.get()).toBeUndefined();
|
|
||||||
|
|
||||||
plugin.sendEvent('newMessage', {
|
|
||||||
app: 'Flipper',
|
app: 'Flipper',
|
||||||
direction: 'toFlipper',
|
direction: 'toFlipper:message',
|
||||||
});
|
});
|
||||||
|
|
||||||
instance.clear();
|
clearFlipperDebugMessages();
|
||||||
|
expect(getFlipperDebugMessages()).toEqual([]);
|
||||||
const newRows = instance.rows.records().map(fixRowTimestamps);
|
|
||||||
expect(newRows).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('It can highlight a row', () => {
|
|
||||||
const {instance, ...plugin} = TestUtils.startPlugin(Plugin);
|
|
||||||
|
|
||||||
plugin.sendEvent('newMessage', {
|
|
||||||
app: 'Flipper',
|
|
||||||
direction: 'toFlipper',
|
|
||||||
});
|
|
||||||
|
|
||||||
instance.setHighlightedRow(instance.rows.records()[0]);
|
|
||||||
|
|
||||||
expect(instance.rows.records()).toHaveLength(1);
|
|
||||||
expect(instance.highlightedRow.get()?.app).toEqual('Flipper');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('It can render empty', async () => {
|
test('It can render empty', async () => {
|
||||||
const {renderer} = TestUtils.renderPlugin(Plugin);
|
const renderer = render(<FlipperMessages />);
|
||||||
|
|
||||||
// Default message without any highlighted rows.
|
// Default message without any highlighted rows.
|
||||||
expect(
|
expect(
|
||||||
await renderer.findByText('Select a message to view details'),
|
await renderer.findByText('Select a message to view details'),
|
||||||
).not.toBeNull();
|
).not.toBeNull();
|
||||||
|
renderer.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('It can render rows', async () => {
|
test('It can render rows', async () => {
|
||||||
const {renderer, ...plugin} = TestUtils.renderPlugin(Plugin);
|
const renderer = render(<FlipperMessages />);
|
||||||
|
|
||||||
plugin.sendEvent('newMessage', {
|
act(() => {
|
||||||
|
registerFlipperDebugMessage({
|
||||||
time: new Date(0, 0, 0, 0, 0, 0),
|
time: new Date(0, 0, 0, 0, 0, 0),
|
||||||
app: 'Flipper',
|
app: 'Flipper',
|
||||||
direction: 'toFlipper',
|
direction: 'toFlipper:message',
|
||||||
});
|
});
|
||||||
|
|
||||||
plugin.sendEvent('newMessage', {
|
registerFlipperDebugMessage({
|
||||||
time: new Date(0, 0, 0, 0, 0, 0),
|
time: new Date(0, 0, 0, 0, 0, 0),
|
||||||
app: 'FB4A',
|
app: 'FB4A',
|
||||||
direction: 'toClient',
|
direction: 'toClient:send',
|
||||||
device: 'Android Phone',
|
device: 'Android Phone',
|
||||||
flipperInternalMethod: 'unique-string',
|
flipperInternalMethod: 'unique-string',
|
||||||
payload: {hello: 'world'},
|
payload: {hello: 'world'},
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
expect((await renderer.findByText('unique-string')).parentElement)
|
expect((await renderer.findByText('unique-string')).parentElement)
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
@@ -154,8 +158,10 @@ test('It can render rows', async () => {
|
|||||||
class="css-1vr131n-TableBodyColumnContainer e1luu51r0"
|
class="css-1vr131n-TableBodyColumnContainer e1luu51r0"
|
||||||
width="14%"
|
width="14%"
|
||||||
>
|
>
|
||||||
toClient
|
toClient:send
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
renderer.unmount();
|
||||||
});
|
});
|
||||||
@@ -16,7 +16,7 @@ import {Logger} from '../fb-interfaces/Logger';
|
|||||||
|
|
||||||
import {LeftRail} from './LeftRail';
|
import {LeftRail} from './LeftRail';
|
||||||
import {useStore, useDispatch} from '../utils/useStore';
|
import {useStore, useDispatch} from '../utils/useStore';
|
||||||
import {ConsoleLogs} from '../chrome/ConsoleLogs';
|
import {FlipperDevTools} from '../chrome/FlipperDevTools';
|
||||||
import {setStaticView} from '../reducers/connections';
|
import {setStaticView} from '../reducers/connections';
|
||||||
import {
|
import {
|
||||||
ACTIVE_SHEET_CHANGELOG_RECENT_ONLY,
|
ACTIVE_SHEET_CHANGELOG_RECENT_ONLY,
|
||||||
@@ -79,7 +79,7 @@ export function SandyApp() {
|
|||||||
}
|
}
|
||||||
switch (newSelection) {
|
switch (newSelection) {
|
||||||
case 'flipperlogs':
|
case 'flipperlogs':
|
||||||
dispatch(setStaticView(ConsoleLogs));
|
dispatch(setStaticView(FlipperDevTools));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import {WebsocketClientFlipperConnection} from './utils/js-client-server-utils/w
|
|||||||
import querystring from 'querystring';
|
import querystring from 'querystring';
|
||||||
import {IncomingMessage} from 'http';
|
import {IncomingMessage} from 'http';
|
||||||
import ws from 'ws';
|
import ws from 'ws';
|
||||||
import {initSelfInpector} from './utils/self-inspection/selfInspectionUtils';
|
|
||||||
import DummyDevice from './devices/DummyDevice';
|
import DummyDevice from './devices/DummyDevice';
|
||||||
import BaseDevice from './devices/BaseDevice';
|
import BaseDevice from './devices/BaseDevice';
|
||||||
import {sideEffect} from './utils/sideEffect';
|
import {sideEffect} from './utils/sideEffect';
|
||||||
@@ -106,10 +105,6 @@ class Server extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
initSelfInpector(this.store, this.logger, this, this.connections);
|
|
||||||
}
|
|
||||||
|
|
||||||
const {insecure, secure} = this.store.getState().application.serverPorts;
|
const {insecure, secure} = this.store.getState().application.serverPorts;
|
||||||
this.initialisePromise = this.certificateProvider
|
this.initialisePromise = this.certificateProvider
|
||||||
.loadSecureServerConfig()
|
.loadSecureServerConfig()
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import {notification, Typography} from 'antd';
|
import {notification, Typography} from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {ConsoleLogs} from '../chrome/ConsoleLogs';
|
import {FlipperDevTools} from '../chrome/FlipperDevTools';
|
||||||
import {setStaticView} from '../reducers/connections';
|
import {setStaticView} from '../reducers/connections';
|
||||||
import {getStore} from '../store';
|
import {getStore} from '../store';
|
||||||
import {Layout} from '../ui';
|
import {Layout} from '../ui';
|
||||||
@@ -29,7 +29,7 @@ export function showErrorNotification(message: string, description?: string) {
|
|||||||
See{' '}
|
See{' '}
|
||||||
<Link
|
<Link
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
getStore().dispatch(setStaticView(ConsoleLogs));
|
getStore().dispatch(setStaticView(FlipperDevTools));
|
||||||
notification.close(key);
|
notification.close(key);
|
||||||
}}>
|
}}>
|
||||||
logs
|
logs
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its 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 {FlipperConnection, FlipperPlugin} from 'flipper-client-sdk';
|
|
||||||
|
|
||||||
export type MessageInfo = {
|
|
||||||
device?: string;
|
|
||||||
app: string;
|
|
||||||
flipperInternalMethod?: string;
|
|
||||||
plugin?: string;
|
|
||||||
pluginMethod?: string;
|
|
||||||
payload?: any;
|
|
||||||
direction:
|
|
||||||
| 'toClient:call'
|
|
||||||
| 'toClient:send'
|
|
||||||
| 'toFlipper:message'
|
|
||||||
| 'toFlipper:response';
|
|
||||||
};
|
|
||||||
|
|
||||||
export class FlipperMessagesClientPlugin implements FlipperPlugin {
|
|
||||||
protected connection: FlipperConnection | null = null;
|
|
||||||
|
|
||||||
onConnect(connection: FlipperConnection): void {
|
|
||||||
this.connection = connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
onDisconnect(): void {
|
|
||||||
this.connection = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getId(): string {
|
|
||||||
return 'flipper-messages';
|
|
||||||
}
|
|
||||||
|
|
||||||
runInBackground(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
newMessage(message: MessageInfo) {
|
|
||||||
this.connection?.send('newMessage', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
isConnected() {
|
|
||||||
return this.connection != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const flipperMessagesClientPlugin = new FlipperMessagesClientPlugin();
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its 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 {FlipperClientConnection} from '../../Client';
|
|
||||||
import {Flowable, Single} from 'rsocket-flowable';
|
|
||||||
import {Payload, ConnectionStatus, ISubscriber} from 'rsocket-types';
|
|
||||||
|
|
||||||
import {FlipperClient} from 'flipper-client-sdk';
|
|
||||||
|
|
||||||
// somehow linter isn't happy with next import so type definitions are copied
|
|
||||||
// import {IFutureSubject} from 'rsocket-flowable/Single';
|
|
||||||
|
|
||||||
type CancelCallback = () => void;
|
|
||||||
|
|
||||||
interface IFutureSubject<T> {
|
|
||||||
onComplete: (value: T) => void;
|
|
||||||
onError: (error: Error) => void;
|
|
||||||
onSubscribe: (cancel: CancelCallback | null | undefined) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SelfInspectionFlipperClient<M>
|
|
||||||
extends FlipperClient
|
|
||||||
implements FlipperClientConnection<string, M>
|
|
||||||
{
|
|
||||||
connStatusSubscribers: Set<ISubscriber<ConnectionStatus>> = new Set();
|
|
||||||
connStatus: ConnectionStatus = {kind: 'CONNECTED'};
|
|
||||||
|
|
||||||
connectionStatus(): Flowable<ConnectionStatus> {
|
|
||||||
return new Flowable<ConnectionStatus>((subscriber) => {
|
|
||||||
subscriber.onSubscribe({
|
|
||||||
cancel: () => {
|
|
||||||
this.connStatusSubscribers.delete(subscriber);
|
|
||||||
},
|
|
||||||
request: (_) => {
|
|
||||||
this.connStatusSubscribers.add(subscriber);
|
|
||||||
subscriber.onNext(this.connStatus);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
|
||||||
this.connStatus = {kind: 'CLOSED'};
|
|
||||||
this.connStatusSubscribers.forEach((subscriber) => {
|
|
||||||
subscriber.onNext(this.connStatus);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fireAndForget(payload: Payload<string, M>): void {
|
|
||||||
if (payload.data == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const message = JSON.parse(payload.data) as {
|
|
||||||
method: string;
|
|
||||||
id: number;
|
|
||||||
params: any;
|
|
||||||
};
|
|
||||||
this.onMessageReceived(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
activeRequests = new Map<number, IFutureSubject<Payload<string, M>>>();
|
|
||||||
|
|
||||||
requestResponse(payload: Payload<string, M>): Single<Payload<string, M>> {
|
|
||||||
return new Single((subscriber) => {
|
|
||||||
subscriber.onSubscribe(() => {});
|
|
||||||
if (payload.data == null) {
|
|
||||||
subscriber.onError(new Error('empty payload'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const message = JSON.parse(payload.data) as {
|
|
||||||
method: string;
|
|
||||||
id: number;
|
|
||||||
params: any;
|
|
||||||
};
|
|
||||||
this.activeRequests.set(message.id, subscriber);
|
|
||||||
this.onMessageReceived(message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client methods
|
|
||||||
|
|
||||||
messagesHandler: ((message: any) => void) | undefined;
|
|
||||||
|
|
||||||
start(_appName: string): void {
|
|
||||||
this.onConnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
stop(): void {}
|
|
||||||
|
|
||||||
sendData(payload: any): void {
|
|
||||||
if (payload['success'] != null) {
|
|
||||||
const message = payload as {id: number; success: unknown};
|
|
||||||
const sub = this.activeRequests.get(message.id);
|
|
||||||
sub?.onComplete({data: JSON.stringify(message)});
|
|
||||||
this.activeRequests.delete(message.id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.messagesHandler && this.messagesHandler(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
isAvailable(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
subscibeForClientMessages(handler: (message: any) => void) {
|
|
||||||
this.messagesHandler = handler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const selfInspectionClient = new SelfInspectionFlipperClient();
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its 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 Client, {ClientQuery} from '../../Client';
|
|
||||||
import {FlipperClientConnection} from '../../Client';
|
|
||||||
import {Store} from '../../reducers';
|
|
||||||
import {Logger} from '../../fb-interfaces/Logger';
|
|
||||||
|
|
||||||
import Server from '../../server';
|
|
||||||
import {buildClientId} from '../clientUtils';
|
|
||||||
import {selfInspectionClient} from './selfInspectionClient';
|
|
||||||
import {flipperMessagesClientPlugin} from './plugins/FlipperMessagesClientPlugin';
|
|
||||||
import {destroyDevice} from '../../reducers/connections';
|
|
||||||
|
|
||||||
export function initSelfInpector(
|
|
||||||
store: Store,
|
|
||||||
logger: Logger,
|
|
||||||
flipperServer: Server,
|
|
||||||
flipperConnections: Map<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
connection: FlipperClientConnection<any, any> | null | undefined;
|
|
||||||
client: Client;
|
|
||||||
}
|
|
||||||
>,
|
|
||||||
) {
|
|
||||||
const appName = 'Flipper';
|
|
||||||
|
|
||||||
selfInspectionClient.addPlugin(flipperMessagesClientPlugin);
|
|
||||||
const hostDevice = store
|
|
||||||
.getState()
|
|
||||||
.connections.devices.find((d) => d.serial === '');
|
|
||||||
if (!hostDevice) {
|
|
||||||
console.error('Failed to find host device for self inspector');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const query: ClientQuery = {
|
|
||||||
app: appName,
|
|
||||||
os: 'MacOS',
|
|
||||||
device: 'emulator',
|
|
||||||
device_id: '',
|
|
||||||
sdk_version: 4,
|
|
||||||
};
|
|
||||||
const clientId = buildClientId(query);
|
|
||||||
|
|
||||||
const client = new Client(
|
|
||||||
clientId,
|
|
||||||
query,
|
|
||||||
selfInspectionClient,
|
|
||||||
logger,
|
|
||||||
store,
|
|
||||||
undefined,
|
|
||||||
hostDevice,
|
|
||||||
);
|
|
||||||
|
|
||||||
flipperConnections.set(clientId, {
|
|
||||||
connection: selfInspectionClient,
|
|
||||||
client: client,
|
|
||||||
});
|
|
||||||
|
|
||||||
selfInspectionClient.connectionStatus().subscribe({
|
|
||||||
onNext(payload) {
|
|
||||||
if (payload.kind == 'ERROR' || payload.kind == 'CLOSED') {
|
|
||||||
console.debug(`Device disconnected ${client.id}`, 'server');
|
|
||||||
flipperServer.removeConnection(client.id);
|
|
||||||
destroyDevice(store, logger, client.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSubscribe(subscription) {
|
|
||||||
subscription.request(Number.MAX_SAFE_INTEGER);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
client.init().then(() => {
|
|
||||||
flipperServer.emit('new-client', client);
|
|
||||||
flipperServer.emit('clients-change');
|
|
||||||
client.emit('plugins-change');
|
|
||||||
|
|
||||||
selfInspectionClient.subscibeForClientMessages((payload: any) => {
|
|
||||||
// let's break the possible recursion problems here
|
|
||||||
// for example we want to send init plugin message, but store state is being updated when we enable plugins
|
|
||||||
setImmediate(() => {
|
|
||||||
client.onMessage(JSON.stringify(payload));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -25,6 +25,13 @@ export function usePluginInstance():
|
|||||||
return pluginInstance;
|
return pluginInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function usePluginInstanceMaybe():
|
||||||
|
| SandyPluginInstance
|
||||||
|
| SandyDevicePluginInstance
|
||||||
|
| undefined {
|
||||||
|
return useContext(SandyPluginContext);
|
||||||
|
}
|
||||||
|
|
||||||
export function usePlugin<
|
export function usePlugin<
|
||||||
Factory extends PluginFactory<any, any> | DevicePluginFactory,
|
Factory extends PluginFactory<any, any> | DevicePluginFactory,
|
||||||
>(plugin: Factory): ReturnType<Factory> {
|
>(plugin: Factory): ReturnType<Factory> {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ import {Typography} from 'antd';
|
|||||||
import {CoffeeOutlined, SearchOutlined, PushpinFilled} from '@ant-design/icons';
|
import {CoffeeOutlined, SearchOutlined, PushpinFilled} from '@ant-design/icons';
|
||||||
import {useAssertStableRef} from '../../utils/useAssertStableRef';
|
import {useAssertStableRef} from '../../utils/useAssertStableRef';
|
||||||
import {Formatter} from '../DataFormatter';
|
import {Formatter} from '../DataFormatter';
|
||||||
import {usePluginInstance} from '../../plugin/PluginContext';
|
import {usePluginInstanceMaybe} from '../../plugin/PluginContext';
|
||||||
import {debounce} from 'lodash';
|
import {debounce} from 'lodash';
|
||||||
import {useInUnitTest} from '../../utils/useInUnitTest';
|
import {useInUnitTest} from '../../utils/useInUnitTest';
|
||||||
import {createDataSource} from 'flipper-plugin/src/state/createDataSource';
|
import {createDataSource} from 'flipper-plugin/src/state/createDataSource';
|
||||||
@@ -142,7 +142,7 @@ export function DataTable<T extends object>(
|
|||||||
const isUnitTest = useInUnitTest();
|
const isUnitTest = useInUnitTest();
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const scope = isUnitTest ? "" : usePluginInstance().pluginKey;
|
const scope = isUnitTest ? "" : usePluginInstanceMaybe()?.pluginKey ?? "";
|
||||||
const virtualizerRef = useRef<DataSourceVirtualizer | undefined>();
|
const virtualizerRef = useRef<DataSourceVirtualizer | undefined>();
|
||||||
const [tableState, dispatch] = useReducer(
|
const [tableState, dispatch] = useReducer(
|
||||||
dataTableManagerReducer as DataTableReducer<T>,
|
dataTableManagerReducer as DataTableReducer<T>,
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its 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 {
|
|
||||||
DataInspector,
|
|
||||||
DataTable,
|
|
||||||
DataTableColumn,
|
|
||||||
Layout,
|
|
||||||
createState,
|
|
||||||
PluginClient,
|
|
||||||
usePlugin,
|
|
||||||
useValue,
|
|
||||||
createDataSource,
|
|
||||||
DetailSidebar,
|
|
||||||
Panel,
|
|
||||||
theme,
|
|
||||||
styled,
|
|
||||||
} from 'flipper-plugin';
|
|
||||||
import {Button} from 'antd';
|
|
||||||
import {DeleteOutlined} from '@ant-design/icons';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export interface MessageInfo {
|
|
||||||
time?: Date;
|
|
||||||
device?: string;
|
|
||||||
app: string;
|
|
||||||
flipperInternalMethod?: string;
|
|
||||||
plugin?: string;
|
|
||||||
pluginMethod?: string;
|
|
||||||
payload?: any;
|
|
||||||
direction: 'toClient' | 'toFlipper';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MessageRow extends MessageInfo {
|
|
||||||
time: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Placeholder = styled(Layout.Container)({
|
|
||||||
center: true,
|
|
||||||
color: theme.textColorPlaceholder,
|
|
||||||
fontSize: 18,
|
|
||||||
});
|
|
||||||
|
|
||||||
function createRow(message: MessageInfo): MessageRow {
|
|
||||||
return {
|
|
||||||
...message,
|
|
||||||
time: message.time == null ? new Date() : message.time,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type Events = {
|
|
||||||
newMessage: MessageInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
const COLUMN_CONFIG: DataTableColumn<MessageRow>[] = [
|
|
||||||
{
|
|
||||||
key: 'time',
|
|
||||||
title: 'Time',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'device',
|
|
||||||
title: 'Device',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'app',
|
|
||||||
title: 'App',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'flipperInternalMethod',
|
|
||||||
title: 'Flipper Internal Method',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'plugin',
|
|
||||||
title: 'Plugin',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pluginMethod',
|
|
||||||
title: 'Method',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'direction',
|
|
||||||
title: 'Direction',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export function plugin(client: PluginClient<Events, {}>) {
|
|
||||||
const highlightedRow = createState<MessageRow>();
|
|
||||||
const rows = createDataSource<MessageRow>([], {
|
|
||||||
limit: 1024 * 10,
|
|
||||||
persist: 'messages',
|
|
||||||
});
|
|
||||||
|
|
||||||
const setHighlightedRow = (record: MessageRow) => {
|
|
||||||
highlightedRow.set(record);
|
|
||||||
};
|
|
||||||
|
|
||||||
const clear = () => {
|
|
||||||
highlightedRow.set(undefined);
|
|
||||||
rows.clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
client.onMessage('newMessage', (payload) => {
|
|
||||||
rows.append(createRow(payload));
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
rows,
|
|
||||||
highlightedRow,
|
|
||||||
setHighlightedRow,
|
|
||||||
clear,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function Sidebar() {
|
|
||||||
const instance = usePlugin(plugin);
|
|
||||||
const message = useValue(instance.highlightedRow);
|
|
||||||
|
|
||||||
const renderExtra = (extra: any) => (
|
|
||||||
<Panel title={'Payload'} collapsible={false}>
|
|
||||||
<DataInspector data={extra} expandRoot={false} />
|
|
||||||
</Panel>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DetailSidebar>
|
|
||||||
{message != null ? (
|
|
||||||
renderExtra(message.payload)
|
|
||||||
) : (
|
|
||||||
<Placeholder grow pad="large">
|
|
||||||
Select a message to view details
|
|
||||||
</Placeholder>
|
|
||||||
)}
|
|
||||||
</DetailSidebar>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Component() {
|
|
||||||
const instance = usePlugin(plugin);
|
|
||||||
|
|
||||||
const clearTableButton = (
|
|
||||||
<Button title="Clear logs" onClick={instance.clear}>
|
|
||||||
<DeleteOutlined />
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout.Container grow>
|
|
||||||
<DataTable<MessageRow>
|
|
||||||
dataSource={instance.rows}
|
|
||||||
columns={COLUMN_CONFIG}
|
|
||||||
onSelect={instance.setHighlightedRow}
|
|
||||||
extraActions={clearTableButton}
|
|
||||||
/>
|
|
||||||
<Sidebar />
|
|
||||||
</Layout.Container>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://fbflipper.com/schemas/plugin-package/v2.json",
|
|
||||||
"name": "flipper-plugin-flipper-messages",
|
|
||||||
"id": "flipper-messages",
|
|
||||||
"title": "Flipper Messages",
|
|
||||||
"icon": "bird",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"description": "Flipper self inspection: Messages to and from client",
|
|
||||||
"main": "dist/bundle.js",
|
|
||||||
"flipperBundlerEntry": "index.tsx",
|
|
||||||
"license": "MIT",
|
|
||||||
"keywords": [
|
|
||||||
"flipper-plugin"
|
|
||||||
],
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/facebook/flipper/issues"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"lint": "flipper-pkg lint",
|
|
||||||
"build": "flipper-pkg bundle",
|
|
||||||
"watch": "flipper-pkg bundle --watch",
|
|
||||||
"prepack": "flipper-pkg lint && flipper-pkg bundle --production"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"flipper": "*",
|
|
||||||
"flipper-pkg": "*",
|
|
||||||
"flipper-plugin": "*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user