From 16154e1343a4c9b3abce4633333b643b0b3f7570 Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Wed, 30 Jun 2021 10:40:50 -0700 Subject: [PATCH] Remove classic plugin infra Summary: This removes all code duplication / old plugin infra that isn't needed anymore when all plugin run on the Sandy plugin infra structure. The diff is quite large, but the minimal one that passes tests and compiles. Existing tests are preserved by wrapping all remaining tests in `wrapSandy` for classic plugins where needed Reviewed By: passy Differential Revision: D29394738 fbshipit-source-id: 1315fabd9f048576aed15ed5f1cb6414d5fdbd40 --- desktop/app/src/Client.tsx | 12 +- desktop/app/src/PluginContainer.tsx | 146 +--- .../src/__tests__/PluginContainer.node.tsx | 316 +++++--- .../createMockFlipperWithPlugin.node.tsx.snap | 72 -- .../createMockFlipperWithPlugin.node.tsx | 20 - .../__tests__/ExportDataPluginSheet.node.tsx | 139 ---- .../ExportDataPluginSheet.node.tsx.snap | 252 ------- .../chrome/plugin-manager/PluginDebugger.tsx | 6 +- desktop/app/src/devices/BaseDevice.tsx | 23 +- desktop/app/src/dispatcher/notifications.tsx | 50 +- desktop/app/src/dispatcher/pluginManager.tsx | 19 +- desktop/app/src/index.tsx | 5 +- desktop/app/src/plugin.tsx | 22 +- .../reducers/__tests__/connections.node.tsx | 12 +- .../reducers/__tests__/pluginStates.node.tsx | 33 - .../src/reducers/__tests__/plugins.node.tsx | 7 +- desktop/app/src/reducers/index.tsx | 7 - desktop/app/src/reducers/pluginStates.tsx | 97 --- desktop/app/src/sandy-chrome/SandyApp.tsx | 2 +- desktop/app/src/selectors/connections.tsx | 3 +- .../createMockFlipperWithPlugin.tsx | 58 +- .../src/utils/__tests__/exportData.node.tsx | 213 +++--- .../src/utils/__tests__/messageQueue.node.tsx | 684 ------------------ .../__tests__/messageQueueSandy.node.tsx | 94 ++- .../src/utils/__tests__/pluginUtils.node.tsx | 26 +- .../src/utils/createSandyPluginWrapper.tsx | 3 +- desktop/app/src/utils/exportData.tsx | 338 +-------- desktop/app/src/utils/messageQueue.tsx | 137 +--- desktop/app/src/utils/pluginUtils.tsx | 86 +-- .../flipper-plugin/src/plugin/FlipperLib.tsx | 2 +- .../src/ui/elements-inspector/elements.tsx | 10 +- 31 files changed, 564 insertions(+), 2330 deletions(-) delete mode 100644 desktop/app/src/chrome/__tests__/ExportDataPluginSheet.node.tsx delete mode 100644 desktop/app/src/chrome/__tests__/__snapshots__/ExportDataPluginSheet.node.tsx.snap delete mode 100644 desktop/app/src/reducers/__tests__/pluginStates.node.tsx delete mode 100644 desktop/app/src/reducers/pluginStates.tsx delete mode 100644 desktop/app/src/utils/__tests__/messageQueue.node.tsx diff --git a/desktop/app/src/Client.tsx b/desktop/app/src/Client.tsx index 8ea15b399..c1a864023 100644 --- a/desktop/app/src/Client.tsx +++ b/desktop/app/src/Client.tsx @@ -7,7 +7,7 @@ * @format */ -import {PluginDefinition, FlipperPlugin, FlipperDevicePlugin} from './plugin'; +import {PluginDefinition} from './plugin'; import BaseDevice, {OS} from './devices/BaseDevice'; import {Logger} from './fb-interfaces/Logger'; import {Store} from './reducers/index'; @@ -21,7 +21,6 @@ import invariant from 'invariant'; import { getPluginKey, defaultEnabledBackgroundPlugins, - isSandyPlugin, } from './utils/pluginUtils'; import {processMessagesLater} from './utils/messageQueue'; import {emitBytesReceived} from './dispatcher/tracking'; @@ -118,10 +117,7 @@ export default class Client extends EventEmitter { messageBuffer: Record< string /*pluginKey*/, { - plugin: - | typeof FlipperPlugin - | typeof FlipperDevicePlugin - | _SandyPluginInstance; + plugin: _SandyPluginInstance; messages: Params[]; } > = {}; @@ -220,7 +216,7 @@ export default class Client extends EventEmitter { initFromImport(initialStates: Record>): this { this.plugins.forEach((pluginId) => { const plugin = this.getPlugin(pluginId); - if (isSandyPlugin(plugin)) { + if (plugin) { // TODO: needs to be wrapped in error tracking T68955280 this.sandyPluginStates.set( plugin.id, @@ -253,7 +249,7 @@ export default class Client extends EventEmitter { ) { // start a plugin on start if it is a SandyPlugin, which is enabled, and doesn't have persisted state yet if ( - isSandyPlugin(plugin) && + plugin && (isEnabled || defaultEnabledBackgroundPlugins.includes(plugin.id)) && !this.sandyPluginStates.has(plugin.id) ) { diff --git a/desktop/app/src/PluginContainer.tsx b/desktop/app/src/PluginContainer.tsx index 08b732290..ca8d71c20 100644 --- a/desktop/app/src/PluginContainer.tsx +++ b/desktop/app/src/PluginContainer.tsx @@ -7,14 +7,10 @@ * @format */ -import { - FlipperPlugin, - FlipperDevicePlugin, - Props as PluginProps, -} from './plugin'; +import {FlipperPlugin, FlipperDevicePlugin} from './plugin'; import {Logger} from './fb-interfaces/Logger'; import BaseDevice from './devices/BaseDevice'; -import {pluginKey as getPluginKey} from './reducers/pluginStates'; +import {pluginKey as getPluginKey} from './utils/pluginUtils'; import Client from './Client'; import { ErrorBoundary, @@ -31,8 +27,6 @@ import {StaticView, setStaticView} from './reducers/connections'; import {switchPlugin} from './reducers/pluginManager'; import React, {PureComponent} from 'react'; import {connect, ReactReduxContext} from 'react-redux'; -import {setPluginState} from './reducers/pluginStates'; -import {Settings} from './reducers/settings'; import {selectPlugin} from './reducers/connections'; import {State as Store, MiddlewareAPI} from './reducers/index'; import {activateMenuItems} from './MenuBar'; @@ -40,12 +34,11 @@ import {Message} from './reducers/pluginMessageQueue'; import {IdlerImpl} from './utils/Idler'; import {processMessageQueue} from './utils/messageQueue'; import {Layout} from './ui'; -import {theme, TrackingScope, _SandyPluginRenderer} from 'flipper-plugin'; +import {theme, _SandyPluginRenderer} from 'flipper-plugin'; import { ActivePluginListItem, isDevicePlugin, isDevicePluginDefinition, - isSandyPlugin, } from './utils/pluginUtils'; import {ContentContainer} from './sandy-chrome/ContentContainer'; import {Alert, Typography} from 'antd'; @@ -59,13 +52,6 @@ import {getActiveClient, getActivePlugin} from './selectors/connections'; const {Text, Link} = Typography; -const Container = styled(FlexColumn)({ - width: 0, - flexGrow: 1, - flexShrink: 1, - backgroundColor: colors.white, -}); - export const SidebarContainer = styled(FlexRow)({ backgroundColor: theme.backgroundWash, height: '100%', @@ -103,19 +89,14 @@ const ProgressBarBar = styled.div<{progress: number}>(({progress}) => ({ type OwnProps = { logger: Logger; - isSandy?: boolean; }; type StateFromProps = { - pluginState: Object; activePlugin: ActivePluginListItem | null; target: Client | BaseDevice | null; pluginKey: string | null; deepLinkPayload: unknown; - selectedApp: string | null; - isArchivedDevice: boolean; pendingMessages: Message[] | undefined; - settingsState: Settings; latestInstalledVersion: InstalledPluginDetails | undefined; }; @@ -125,7 +106,6 @@ type DispatchFromProps = { selectedApp?: string | null; deepLinkPayload: unknown; }) => any; - setPluginState: (payload: {pluginKey: string; state: any}) => void; setStaticView: (payload: StaticView) => void; enablePlugin: typeof switchPlugin; loadPlugin: typeof loadPlugin; @@ -228,17 +208,13 @@ class PluginContainer extends PureComponent { if ( target instanceof Client && activePlugin && - (isSandyPlugin(activePlugin.definition) || - activePlugin.definition.persistedStateReducer) && pluginKey && pendingMessages?.length ) { const start = Date.now(); this.idler = new IdlerImpl(); processMessageQueue( - isSandyPlugin(activePlugin.definition) - ? target.sandyPluginStates.get(activePlugin.definition.id)! - : activePlugin.definition, + target.sandyPluginStates.get(activePlugin.definition.id)!, pluginKey, this.store, (progress) => { @@ -360,18 +336,8 @@ class PluginContainer extends PureComponent { } renderPlugin() { - const { - pluginState, - setPluginState, - activePlugin, - pluginKey, - target, - isArchivedDevice, - selectedApp, - settingsState, - isSandy, - latestInstalledVersion, - } = this.props; + const {activePlugin, pluginKey, target, latestInstalledVersion} = + this.props; if ( !activePlugin || !target || @@ -381,7 +347,6 @@ class PluginContainer extends PureComponent { console.warn(`No selected plugin. Rendering empty!`); return this.renderNoPluginActive(); } - let pluginElement: null | React.ReactElement; const showUpdateAlert = latestInstalledVersion && activePlugin && @@ -392,71 +357,14 @@ class PluginContainer extends PureComponent { latestInstalledVersion.version, activePlugin.definition.version, ); - if (isSandyPlugin(activePlugin.definition)) { - // Make sure we throw away the container for different pluginKey! - const instance = target.sandyPluginStates.get(activePlugin.definition.id); - if (!instance) { - // happens if we selected a plugin that is not enabled on a specific app or not supported on a specific device. - return this.renderNoPluginActive(); - } - pluginElement = ( - <_SandyPluginRenderer key={pluginKey} plugin={instance} /> - ); - } else { - const props: PluginProps & { - key: string; - ref: ( - ref: - | FlipperPlugin - | FlipperDevicePlugin - | null - | undefined, - ) => void; - } = { - key: pluginKey, - logger: this.props.logger, - selectedApp, - persistedState: activePlugin.definition.defaultPersistedState - ? { - ...activePlugin.definition.defaultPersistedState, - ...pluginState, - } - : pluginState, - setStaticView: (payload: StaticView) => - this.props.setStaticView(payload), - setPersistedState: (state) => setPluginState({pluginKey, state}), - target, - deepLinkPayload: this.props.deepLinkPayload, - selectPlugin: (pluginID: string, deepLinkPayload: unknown) => { - const {target} = this.props; - // check if plugin will be available - if (target instanceof Client && target.plugins.has(pluginID)) { - this.props.selectPlugin({ - selectedPlugin: pluginID, - deepLinkPayload, - }); - return true; - } else if (target instanceof BaseDevice) { - this.props.selectPlugin({ - selectedPlugin: pluginID, - deepLinkPayload, - }); - return true; - } else { - return false; - } - }, - ref: this.refChanged, - isArchivedDevice, - settingsState, - }; - pluginElement = ( - - {React.createElement(activePlugin.definition, props)} - - ); + // Make sure we throw away the container for different pluginKey! + const instance = target.sandyPluginStates.get(activePlugin.definition.id); + if (!instance) { + // happens if we selected a plugin that is not enabled on a specific app or not supported on a specific device. + return this.renderNoPluginActive(); } - return isSandy ? ( + + return (
{showUpdateAlert && ( @@ -490,23 +398,13 @@ class PluginContainer extends PureComponent { heading={`Plugin "${ activePlugin.definition.title || 'Unknown' }" encountered an error during render`}> - {pluginElement} + + <_SandyPluginRenderer key={pluginKey} plugin={instance} /> + - ) : ( - - - - {pluginElement} - - - - ); } } @@ -516,11 +414,9 @@ export default connect( let pluginKey: string | null = null; let target: BaseDevice | Client | null = null; const { - connections: {selectedDevice, selectedApp, deepLinkPayload}, - pluginStates, + connections: {selectedDevice, deepLinkPayload}, plugins: {installedPlugins}, pluginMessageQueue, - settingsState, } = state; const selectedClient = getActiveClient(state); const activePlugin = getActivePlugin(state); @@ -536,24 +432,17 @@ export default connect( pluginKey = getPluginKey(selectedClient.id, activePlugin.details.id); } } - const isArchivedDevice = !selectedDevice - ? false - : selectedDevice.isArchived; const pendingMessages = pluginKey ? pluginMessageQueue[pluginKey] : undefined; const s: StateFromProps = { - pluginState: pluginStates[pluginKey as string], activePlugin, target, deepLinkPayload, pluginKey, - isArchivedDevice, - selectedApp: selectedApp || null, pendingMessages, - settingsState, latestInstalledVersion: installedPlugins.get( activePlugin?.details?.name ?? '', ), @@ -561,7 +450,6 @@ export default connect( return s; }, { - setPluginState, selectPlugin, setStaticView, enablePlugin: switchPlugin, diff --git a/desktop/app/src/__tests__/PluginContainer.node.tsx b/desktop/app/src/__tests__/PluginContainer.node.tsx index 32dae6310..356ed51db 100644 --- a/desktop/app/src/__tests__/PluginContainer.node.tsx +++ b/desktop/app/src/__tests__/PluginContainer.node.tsx @@ -72,22 +72,31 @@ test('Plugin container can render plugin and receive updates', async () => {
-

- Hello: - - +
+
- 0 - -

+

+ Hello: + + + 0 + +

+
+
+
-
`); @@ -164,17 +173,26 @@ test('PluginContainer can render Sandy plugins', async () => {
-
- Hello from Sandy - 0 +
+
+
+
+ Hello from Sandy + 0 +
+
+
-
`); @@ -195,17 +213,26 @@ test('PluginContainer can render Sandy plugins', async () => {
-
- Hello from Sandy - 2 +
+
+
+
+ Hello from Sandy + 2 +
+
+
-
`); @@ -262,17 +289,26 @@ test('PluginContainer can render Sandy plugins', async () => {
-
- Hello from Sandy - 9 +
+
+
+
+ Hello from Sandy + 9 +
+
+
-
`); @@ -501,17 +537,26 @@ test('PluginContainer + Sandy plugin supports deeplink', async () => {
-

- hello - world -

+
+
+
+

+ hello + world +

+
+
+
-
`); @@ -532,17 +577,26 @@ test('PluginContainer + Sandy plugin supports deeplink', async () => {
-

- hello - universe! -

+
+
+
+

+ hello + universe! +

+
+
+
-
`); @@ -655,16 +709,25 @@ test('PluginContainer can render Sandy device plugins', async () => {
-
- Hello from Sandy: +
+
+
+
+ Hello from Sandy: +
+
+
-
`); @@ -686,17 +749,26 @@ test('PluginContainer can render Sandy device plugins', async () => {
-
- Hello from Sandy: - helleuh +
+
+
+
+ Hello from Sandy: + helleuh +
+
+
-
`); @@ -778,17 +850,26 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
-

- hello - world -

+
+
+
+

+ hello + world +

+
+
+
-
`); @@ -809,17 +890,26 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
-

- hello - {"thisIs":"theUniverse"} -

+
+
+
+

+ hello + {"thisIs":"theUniverse"} +

+
+
+
-
`); @@ -1076,17 +1166,26 @@ test('PluginContainer can render Sandy plugins for archived devices', async () =
-
- Hello from Sandy - 0 +
+
+
+
+ Hello from Sandy + 0 +
+
+
-
`); @@ -1136,17 +1235,26 @@ test('PluginContainer can render Sandy plugins for archived devices', async () =
-
- Hello from Sandy - 0 +
+
+
+
+ Hello from Sandy + 0 +
+
+
-
`); diff --git a/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap b/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap index 1ffdedcf6..938ee03a8 100644 --- a/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap +++ b/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap @@ -1,77 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`can create a Fake flipper 1`] = ` -Object { - "androidEmulators": Array [], - "clients": Array [ - Object { - "id": "TestApp#Android#MockAndroidDevice#serial", - "query": Object { - "app": "TestApp", - "device": "MockAndroidDevice", - "device_id": "serial", - "os": "Android", - "sdk_version": 4, - }, - }, - ], - "deepLinkPayload": null, - "devices": Array [ - Object { - "deviceType": "physical", - "os": "Android", - "serial": "serial", - "title": "MockAndroidDevice", - }, - ], - "enabledDevicePlugins": Set { - "DeviceLogs", - "CrashReporter", - "MobileBuilds", - "Hermesdebuggerrn", - "React", - }, - "enabledPlugins": Object { - "TestApp": Array [ - "TestPlugin", - ], - }, - "selectedApp": "TestApp#Android#MockAndroidDevice#serial", - "selectedAppPluginListRevision": 0, - "selectedDevice": Object { - "deviceType": "physical", - "os": "Android", - "serial": "serial", - "title": "MockAndroidDevice", - }, - "selectedPlugin": "TestPlugin", - "staticView": null, - "uninitializedClients": Array [], - "userPreferredApp": "TestApp#Android#MockAndroidDevice#serial", - "userPreferredDevice": "MockAndroidDevice", - "userPreferredPlugin": "TestPlugin", -} -`; - -exports[`can create a Fake flipper 2`] = ` -Object { - "bundledPlugins": Map {}, - "clientPlugins": Map { - "TestPlugin" => [Function], - }, - "devicePlugins": Map {}, - "disabledPlugins": Array [], - "failedPlugins": Array [], - "gatekeepedPlugins": Array [], - "initialised": false, - "installedPlugins": Map {}, - "loadedPlugins": Map {}, - "marketplacePlugins": Array [], - "selectedPlugins": Array [], - "uninstalledPluginNames": Set {}, -} -`; - exports[`can create a Fake flipper with legacy wrapper 1`] = ` Object { "androidEmulators": Array [], diff --git a/desktop/app/src/__tests__/createMockFlipperWithPlugin.node.tsx b/desktop/app/src/__tests__/createMockFlipperWithPlugin.node.tsx index 02f935c59..332cc312c 100644 --- a/desktop/app/src/__tests__/createMockFlipperWithPlugin.node.tsx +++ b/desktop/app/src/__tests__/createMockFlipperWithPlugin.node.tsx @@ -40,26 +40,6 @@ class TestPlugin extends FlipperPlugin { } } -test('can create a Fake flipper', async () => { - const {client, device, store, sendMessage} = - await createMockFlipperWithPlugin(TestPlugin, {disableLegacyWrapper: true}); - expect(client).toBeTruthy(); - expect(device).toBeTruthy(); - expect(store).toBeTruthy(); - expect(sendMessage).toBeTruthy(); - expect(client.plugins.has(TestPlugin.id)).toBe(true); - expect(store.getState().connections).toMatchSnapshot(); - expect(store.getState().plugins).toMatchSnapshot(); - sendMessage('inc', {}); - expect(store.getState().pluginStates).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object { - "count": 1, - }, - } - `); -}); - const testIdler = new TestIdler(); function testOnStatusMessage() { diff --git a/desktop/app/src/chrome/__tests__/ExportDataPluginSheet.node.tsx b/desktop/app/src/chrome/__tests__/ExportDataPluginSheet.node.tsx deleted file mode 100644 index 92a6e1f53..000000000 --- a/desktop/app/src/chrome/__tests__/ExportDataPluginSheet.node.tsx +++ /dev/null @@ -1,139 +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 React from 'react'; -import {create, act, ReactTestRenderer} from 'react-test-renderer'; -import {Provider} from 'react-redux'; -import ExportDataPluginSheet from '../ExportDataPluginSheet'; -import {FlipperPlugin, FlipperDevicePlugin} from '../../plugin'; -import {getPluginKey} from '../../utils/pluginUtils'; -import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin'; -import {setPluginState} from '../../reducers/pluginStates'; -import {getExportablePlugins} from '../../selectors/connections'; - -class TestPlugin extends FlipperPlugin { - static details = { - title: 'TestPlugin', - id: 'TestPlugin', - } as any; -} -TestPlugin.title = 'TestPlugin'; -TestPlugin.id = 'TestPlugin'; -TestPlugin.defaultPersistedState = {msg: 'Test plugin'}; - -class TestDevicePlugin extends FlipperDevicePlugin { - static details = { - title: 'TestDevicePlugin', - id: 'TestDevicePlugin', - } as any; - - static supportsDevice() { - return true; - } -} -TestDevicePlugin.title = 'TestDevicePlugin'; -TestDevicePlugin.id = 'TestDevicePlugin'; -TestDevicePlugin.defaultPersistedState = {msg: 'TestDevicePlugin'}; - -test('SettingsSheet snapshot with nothing enabled', async () => { - let root: ReactTestRenderer; - const {store, togglePlugin, client, device, pluginKey} = - await createMockFlipperWithPlugin(TestPlugin, { - additionalPlugins: [TestDevicePlugin], - }); - - togglePlugin(); - - store.dispatch( - setPluginState({ - pluginKey, - state: {test: '1'}, - }), - ); - - expect(getExportablePlugins(store.getState())).toEqual([]); - - // makes device plugin visible - store.dispatch( - setPluginState({ - pluginKey: getPluginKey(undefined, device, 'TestDevicePlugin'), - state: {test: '1'}, - }), - ); - - expect(getExportablePlugins(store.getState())).toEqual([ - { - id: 'TestDevicePlugin', - label: 'TestDevicePlugin', - }, - ]); - - await act(async () => { - root = create( - - {}} /> - , - ); - }); - - expect(root!.toJSON()).toMatchSnapshot(); -}); - -test('SettingsSheet snapshot with one plugin enabled', async () => { - let root: ReactTestRenderer; - const {store, device, pluginKey} = await createMockFlipperWithPlugin( - TestPlugin, - { - additionalPlugins: [TestDevicePlugin], - }, - ); - - // enabled - // in Sandy wrapper, a plugin is either persistable or not, but it doesn't depend on the current state. - // So this plugin will show up, even though its state is still the default - expect(getExportablePlugins(store.getState())).toEqual([ - { - id: 'TestPlugin', - label: 'TestPlugin', - }, - ]); - - store.dispatch( - setPluginState({ - pluginKey, - state: {test: '1'}, - }), - ); - store.dispatch( - setPluginState({ - pluginKey: getPluginKey(undefined, device, 'TestDevicePlugin'), - state: {test: '1'}, - }), - ); - expect(getExportablePlugins(store.getState())).toEqual([ - { - id: 'TestDevicePlugin', - label: 'TestDevicePlugin', - }, - { - id: 'TestPlugin', - label: 'TestPlugin', - }, - ]); - - await act(async () => { - root = create( - - {}} /> - , - ); - }); - - expect(root!.toJSON()).toMatchSnapshot(); -}); diff --git a/desktop/app/src/chrome/__tests__/__snapshots__/ExportDataPluginSheet.node.tsx.snap b/desktop/app/src/chrome/__tests__/__snapshots__/ExportDataPluginSheet.node.tsx.snap deleted file mode 100644 index 54124ee09..000000000 --- a/desktop/app/src/chrome/__tests__/__snapshots__/ExportDataPluginSheet.node.tsx.snap +++ /dev/null @@ -1,252 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SettingsSheet snapshot with nothing enabled 1`] = ` -
-
-
- - Select the plugins for which you want to export the data - -
-
-
-
-
- - TestDevicePlugin - -
- -
-
-
-
-
-
-
-
-
-
-
- -
-
- -
-
-
-
-
-`; - -exports[`SettingsSheet snapshot with one plugin enabled 1`] = ` -
-
-
- - Select the plugins for which you want to export the data - -
-
-
-
-
- - TestDevicePlugin - -
- -
-
-
-
-
-
-
-
-
- - TestPlugin - -
- -
-
-
-
-
-
-
-
-
-
-
- -
-
- -
-
-
-
-
-`; diff --git a/desktop/app/src/chrome/plugin-manager/PluginDebugger.tsx b/desktop/app/src/chrome/plugin-manager/PluginDebugger.tsx index d9015c07b..881b17b30 100644 --- a/desktop/app/src/chrome/plugin-manager/PluginDebugger.tsx +++ b/desktop/app/src/chrome/plugin-manager/PluginDebugger.tsx @@ -15,7 +15,7 @@ import {connect} from 'react-redux'; import {Text, ManagedTable, styled, colors, Link, Bordered} from '../../ui'; import StatusIndicator from '../../ui/components/StatusIndicator'; import {State as Store} from '../../reducers'; -import {DevicePluginDefinition, ClientPluginDefinition} from '../../plugin'; +import {PluginDefinition} from '../../plugin'; const InfoText = styled(Text)({ lineHeight: '130%', @@ -43,8 +43,8 @@ type StateFromProps = { failedPlugins: Array<[PluginDetails, string]>; clients: Array; selectedDevice: string | null | undefined; - devicePlugins: DevicePluginDefinition[]; - clientPlugins: ClientPluginDefinition[]; + devicePlugins: PluginDefinition[]; + clientPlugins: PluginDefinition[]; }; type DispatchFromProps = {}; diff --git a/desktop/app/src/devices/BaseDevice.tsx b/desktop/app/src/devices/BaseDevice.tsx index 7bd14ebd7..aa3399479 100644 --- a/desktop/app/src/devices/BaseDevice.tsx +++ b/desktop/app/src/devices/BaseDevice.tsx @@ -18,11 +18,7 @@ import { createState, _getFlipperLibImplementation, } from 'flipper-plugin'; -import { - DevicePluginDefinition, - DevicePluginMap, - FlipperDevicePlugin, -} from '../plugin'; +import {PluginDefinition, DevicePluginMap} from '../plugin'; import {DeviceSpec, OS as PluginOS, PluginDetails} from 'flipper-plugin-lib'; export type DeviceShell = { @@ -191,9 +187,9 @@ export default class BaseDevice { return null; } - supportsPlugin(plugin: DevicePluginDefinition | PluginDetails) { + supportsPlugin(plugin: PluginDefinition | PluginDetails) { let pluginDetails: PluginDetails; - if (isDevicePluginDefinition(plugin)) { + if (plugin instanceof _SandyPluginDefinition) { pluginDetails = plugin.details; if (!pluginDetails.pluginType && !pluginDetails.supportedDevices) { // TODO T84453692: this branch is to support plugins defined with the legacy approach. Need to remove this branch after some transition period when @@ -205,7 +201,7 @@ export default class BaseDevice { false) ); } else { - return plugin.supportsDevice(this); + return (plugin as any).supportsDevice(this); } } } else { @@ -240,7 +236,7 @@ export default class BaseDevice { } } - loadDevicePlugin(plugin: DevicePluginDefinition, initialState?: any) { + loadDevicePlugin(plugin: PluginDefinition, initialState?: any) { if (!this.supportsPlugin(plugin)) { return; } @@ -286,12 +282,3 @@ export default class BaseDevice { this.sandyPluginStates.clear(); } } - -function isDevicePluginDefinition( - definition: any, -): definition is DevicePluginDefinition { - return ( - (definition as any).prototype instanceof FlipperDevicePlugin || - (definition instanceof _SandyPluginDefinition && definition.isDevicePlugin) - ); -} diff --git a/desktop/app/src/dispatcher/notifications.tsx b/desktop/app/src/dispatcher/notifications.tsx index bde82479c..2a6eadbd4 100644 --- a/desktop/app/src/dispatcher/notifications.tsx +++ b/desktop/app/src/dispatcher/notifications.tsx @@ -10,16 +10,13 @@ import {Store} from '../reducers/index'; import {Logger} from '../fb-interfaces/Logger'; import {PluginNotification} from '../reducers/notifications'; -import {PluginDefinition} from '../plugin'; import {ipcRenderer, IpcRendererEvent} from 'electron'; import { - setActiveNotifications, updatePluginBlocklist, updateCategoryBlocklist, } from '../reducers/notifications'; import {textContent} from '../utils/index'; -import {deconstructPluginKey} from '../utils/clientUtils'; -import {getPluginTitle, isSandyPlugin} from '../utils/pluginUtils'; +import {getPluginTitle} from '../utils/pluginUtils'; import {sideEffect} from '../utils/sideEffect'; import {openNotification} from '../sandy-chrome/notification/Notification'; @@ -28,7 +25,6 @@ const NOTIFICATION_THROTTLE = 5 * 1000; // in milliseconds export default (store: Store, logger: Logger) => { const knownNotifications: Set = new Set(); - const knownPluginStates: Map = new Map(); const lastNotificationTime: Map = new Map(); ipcRenderer.on( @@ -78,56 +74,16 @@ export default (store: Store, logger: Logger) => { sideEffect( store, {name: 'notifications', throttleMs: 500}, - ({notifications, pluginStates, plugins}) => ({ + ({notifications, plugins}) => ({ notifications, - pluginStates, devicePlugins: plugins.devicePlugins, clientPlugins: plugins.clientPlugins, }), - ({notifications, pluginStates, devicePlugins, clientPlugins}, store) => { + ({notifications, devicePlugins, clientPlugins}, store) => { function getPlugin(name: string) { return devicePlugins.get(name) ?? clientPlugins.get(name); } - Object.keys(pluginStates).forEach((key) => { - if (knownPluginStates.get(key) !== pluginStates[key]) { - knownPluginStates.set(key, pluginStates[key]); - const plugin = deconstructPluginKey(key); - const pluginName = plugin.pluginName; - const client = plugin.client; - - if (!pluginName) { - return; - } - - const persistingPlugin: undefined | PluginDefinition = - getPlugin(pluginName); - if ( - persistingPlugin && - !isSandyPlugin(persistingPlugin) && - persistingPlugin.getActiveNotifications - ) { - try { - const notifications = persistingPlugin.getActiveNotifications( - pluginStates[key], - ); - store.dispatch( - setActiveNotifications({ - notifications, - client, - pluginId: pluginName, - }), - ); - } catch (e) { - console.error( - 'Failed to compute notifications for plugin ' + pluginName, - e, - ); - } - } - } - }); - const {activeNotifications, blocklistedPlugins, blocklistedCategories} = notifications; diff --git a/desktop/app/src/dispatcher/pluginManager.tsx b/desktop/app/src/dispatcher/pluginManager.tsx index 36a7a73b7..6370b6e9c 100644 --- a/desktop/app/src/dispatcher/pluginManager.tsx +++ b/desktop/app/src/dispatcher/pluginManager.tsx @@ -8,7 +8,6 @@ */ import type {Store} from '../reducers/index'; -import {clearPluginState} from '../reducers/pluginStates'; import type {Logger} from '../fb-interfaces/Logger'; import { LoadPluginActionPayload, @@ -27,12 +26,7 @@ import { import {sideEffect} from '../utils/sideEffect'; import {requirePlugin} from './plugins'; import {showErrorNotification} from '../utils/notifications'; -import { - ClientPluginDefinition, - DevicePluginDefinition, - FlipperPlugin, - PluginDefinition, -} from '../plugin'; +import {PluginDefinition} from '../plugin'; import type Client from '../Client'; import {unloadModule} from '../utils/electronModuleCache'; import { @@ -148,7 +142,6 @@ function uninstallPlugin(store: Store, {plugin}: UninstallPluginActionPayload) { clients.forEach((client) => { stopPlugin(client, plugin.id); }); - store.dispatch(clearPluginState({pluginId: plugin.id})); if (!plugin.details.isBundled) { unloadPluginModule(plugin.details); } @@ -194,7 +187,7 @@ function switchPlugin( function switchClientPlugin( store: Store, - plugin: ClientPluginDefinition, + plugin: PluginDefinition, selectedApp: string | undefined, ) { selectedApp = selectedApp ?? getSelectedAppId(store); @@ -224,7 +217,7 @@ function switchClientPlugin( } } -function switchDevicePlugin(store: Store, plugin: DevicePluginDefinition) { +function switchDevicePlugin(store: Store, plugin: PluginDefinition) { const {connections} = store.getState(); const devicesWithPlugin = connections.devices.filter((d) => d.supportsPlugin(plugin.details), @@ -244,7 +237,7 @@ function switchDevicePlugin(store: Store, plugin: DevicePluginDefinition) { function updateClientPlugin( store: Store, - plugin: typeof FlipperPlugin, + plugin: PluginDefinition, enable: boolean, ) { const clients = store.getState().connections.clients; @@ -266,7 +259,6 @@ function updateClientPlugin( clientsWithEnabledPlugin.forEach((client) => { stopPlugin(client, plugin.id); }); - store.dispatch(clearPluginState({pluginId: plugin.id})); clientsWithEnabledPlugin.forEach((client) => { startPlugin(client, plugin, true); }); @@ -279,7 +271,7 @@ function updateClientPlugin( function updateDevicePlugin( store: Store, - plugin: DevicePluginDefinition, + plugin: PluginDefinition, enable: boolean, ) { if (enable) { @@ -292,7 +284,6 @@ function updateDevicePlugin( devicesWithEnabledPlugin.forEach((d) => { d.unloadDevicePlugin(plugin.id); }); - store.dispatch(clearPluginState({pluginId: plugin.id})); const previousVersion = store.getState().plugins.devicePlugins.get(plugin.id); if (previousVersion) { // unload previous version from Electron cache diff --git a/desktop/app/src/index.tsx b/desktop/app/src/index.tsx index a20424ebf..59f5f0414 100644 --- a/desktop/app/src/index.tsx +++ b/desktop/app/src/index.tsx @@ -39,8 +39,9 @@ export {default as constants} from './fb-stubs/constants'; export {connect} from 'react-redux'; export {selectPlugin, StaticView} from './reducers/connections'; export {writeBufferToFile, bufferToBlob} from './utils/screenshot'; -export {getPluginKey, getPersistedState} from './utils/pluginUtils'; -export {Idler, Notification} from 'flipper-plugin'; +export {getPluginKey} from './utils/pluginUtils'; +export {Notification, Idler} from 'flipper-plugin'; +export {IdlerImpl} from './utils/Idler'; export {Store, MiddlewareAPI, State as ReduxState} from './reducers/index'; export {default as BaseDevice} from './devices/BaseDevice'; export {DeviceLogEntry, LogLevel, DeviceLogListener} from 'flipper-plugin'; diff --git a/desktop/app/src/plugin.tsx b/desktop/app/src/plugin.tsx index f590db46c..be905da43 100644 --- a/desktop/app/src/plugin.tsx +++ b/desktop/app/src/plugin.tsx @@ -27,18 +27,10 @@ import { type Parameters = {[key: string]: any}; -export type PluginDefinition = ClientPluginDefinition | DevicePluginDefinition; +export type PluginDefinition = _SandyPluginDefinition; -export type DevicePluginDefinition = - | typeof FlipperDevicePlugin - | _SandyPluginDefinition; - -export type ClientPluginDefinition = - | typeof FlipperPlugin - | _SandyPluginDefinition; - -export type ClientPluginMap = Map; -export type DevicePluginMap = Map; +export type ClientPluginMap = Map; +export type DevicePluginMap = Map; // This function is intended to be called from outside of the plugin. // If you want to `call` from the plugin use, this.client.call @@ -211,6 +203,10 @@ export abstract class FlipperBasePlugin< } } +/** + * @deprecated Please use the newer "Sandy" plugin APIs! + * https://fbflipper.com/docs/extending/sandy-migration + */ export class FlipperDevicePlugin< S, A extends BaseAction, @@ -240,6 +236,10 @@ export class FlipperDevicePlugin< } } +/** + * @deprecated Please use the newer "Sandy" plugin APIs! + * https://fbflipper.com/docs/extending/sandy-migration + */ export class FlipperPlugin< S, A extends BaseAction, diff --git a/desktop/app/src/reducers/__tests__/connections.node.tsx b/desktop/app/src/reducers/__tests__/connections.node.tsx index 62909048e..d84ba0ad7 100644 --- a/desktop/app/src/reducers/__tests__/connections.node.tsx +++ b/desktop/app/src/reducers/__tests__/connections.node.tsx @@ -13,7 +13,17 @@ import BaseDevice from '../../devices/BaseDevice'; import MacDevice from '../../devices/MacDevice'; import {FlipperDevicePlugin} from '../../plugin'; import MetroDevice from '../../devices/MetroDevice'; -import {TestUtils} from 'flipper-plugin'; +import {TestUtils, _setFlipperLibImplementation} from 'flipper-plugin'; +import {wrapSandy} from '../../test-utils/createMockFlipperWithPlugin'; +import {createMockFlipperLib} from 'flipper-plugin/src/test-utils/test-utils'; + +beforeEach(() => { + _setFlipperLibImplementation(createMockFlipperLib()); +}); + +afterEach(() => { + _setFlipperLibImplementation(undefined); +}); test('doing a double REGISTER_DEVICE keeps the last', () => { const device1 = new BaseDevice('serial', 'physical', 'title', 'Android'); diff --git a/desktop/app/src/reducers/__tests__/pluginStates.node.tsx b/desktop/app/src/reducers/__tests__/pluginStates.node.tsx deleted file mode 100644 index cf9a4004c..000000000 --- a/desktop/app/src/reducers/__tests__/pluginStates.node.tsx +++ /dev/null @@ -1,33 +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 {default as reducer, setPluginState, Action} from '../pluginStates'; - -test('reduce setPluginState', () => { - const result = reducer( - {}, - setPluginState({pluginKey: 'myPlugin', state: {a: 1}}), - ); - expect(result).toEqual({myPlugin: {a: 1}}); -}); - -test('CLEAR_CLIENT_PLUGINS_STATE removes plugin state', () => { - const clientId = 'app1#device1'; - const pluginKey = 'app1#device1#plugin1'; - - const action: Action = { - type: 'CLEAR_CLIENT_PLUGINS_STATE', - payload: {clientId: clientId, devicePlugins: new Set()}, - }; - const result = reducer( - {[pluginKey]: {a: 1}, 'anotherPlugin#key': {b: 2}}, - action, - ); - expect(result).toEqual({'anotherPlugin#key': {b: 2}}); -}); diff --git a/desktop/app/src/reducers/__tests__/plugins.node.tsx b/desktop/app/src/reducers/__tests__/plugins.node.tsx index a256e0bf8..a5c290914 100644 --- a/desktop/app/src/reducers/__tests__/plugins.node.tsx +++ b/desktop/app/src/reducers/__tests__/plugins.node.tsx @@ -15,18 +15,21 @@ import { } from '../plugins'; import {FlipperPlugin, FlipperDevicePlugin, BaseAction} from '../../plugin'; import {InstalledPluginDetails} from 'flipper-plugin-lib'; +import {wrapSandy} from '../../test-utils/createMockFlipperWithPlugin'; -const testPlugin = class extends FlipperPlugin { +const testPluginOrig = class extends FlipperPlugin { static id = 'TestPlugin'; }; +const testPlugin = wrapSandy(testPluginOrig); -const testDevicePlugin = class extends FlipperDevicePlugin< +const testDevicePluginOrig = class extends FlipperDevicePlugin< any, BaseAction, any > { static id = 'TestDevicePlugin'; }; +const testDevicePlugin = wrapSandy(testDevicePluginOrig); test('add clientPlugin', () => { const res = reducer( diff --git a/desktop/app/src/reducers/index.tsx b/desktop/app/src/reducers/index.tsx index 4415f844c..e58945988 100644 --- a/desktop/app/src/reducers/index.tsx +++ b/desktop/app/src/reducers/index.tsx @@ -18,10 +18,6 @@ import connections, { persistMigrations as devicesPersistMigrations, persistVersion as devicesPersistVersion, } from './connections'; -import pluginStates, { - State as PluginStatesState, - Action as PluginStatesAction, -} from './pluginStates'; import pluginMessageQueue, { State as PluginMessageQueueState, Action as PluginMessageQueueAction, @@ -81,7 +77,6 @@ import {TransformConfig} from 'redux-persist/es/createTransform'; export type Actions = | ApplicationAction | DevicesAction - | PluginStatesAction | PluginMessageQueueAction | NotificationsAction | PluginsAction @@ -98,7 +93,6 @@ export type Actions = export type State = { application: ApplicationState; connections: DevicesState & PersistPartial; - pluginStates: PluginStatesState; pluginMessageQueue: PluginMessageQueueState; notifications: NotificationsState & PersistPartial; plugins: PluginsState & PersistPartial; @@ -158,7 +152,6 @@ export function createRootReducer() { }, connections, ), - pluginStates, pluginMessageQueue: pluginMessageQueue as any, notifications: persistReducer( { diff --git a/desktop/app/src/reducers/pluginStates.tsx b/desktop/app/src/reducers/pluginStates.tsx deleted file mode 100644 index ad8f00e4d..000000000 --- a/desktop/app/src/reducers/pluginStates.tsx +++ /dev/null @@ -1,97 +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 {produce} from 'immer'; -import {Actions} from '.'; -import {deconstructPluginKey} from '../utils/clientUtils'; - -export type State = { - [pluginKey: string]: any; -}; - -export const pluginKey = (serial: string, pluginName: string): string => { - return `${serial}#${pluginName}`; -}; - -export type Action = - | { - type: 'SET_PLUGIN_STATE'; - payload: { - pluginKey: string; - state: Object; - }; - } - | { - type: 'CLEAR_CLIENT_PLUGINS_STATE'; - payload: {clientId: string; devicePlugins: Set}; - } - | { - type: 'CLEAR_PLUGIN_STATE'; - payload: {pluginId: string}; - }; - -export default function reducer( - state: State | undefined = {}, - action: Actions, -): State { - if (action.type === 'SET_PLUGIN_STATE') { - const newPluginState = action.payload.state; - if (newPluginState && newPluginState !== state[action.payload.pluginKey]) { - return { - ...state, - [action.payload.pluginKey]: { - ...state[action.payload.pluginKey], - ...newPluginState, - }, - }; - } - return {...state}; - } else if (action.type === 'CLEAR_CLIENT_PLUGINS_STATE') { - const {payload} = action; - return Object.keys(state).reduce((newState: State, pluginKey) => { - // Only add the pluginState, if its from a plugin other than the one that - // was removed. pluginKeys are in the form of ${clientID}#${pluginID}. - const plugin = deconstructPluginKey(pluginKey); - const clientId = plugin.client; - const pluginId = plugin.pluginName; - if ( - clientId !== payload.clientId || - (pluginId && payload.devicePlugins.has(pluginId)) - ) { - newState[pluginKey] = state[pluginKey]; - } - return newState; - }, {}); - } else if (action.type === 'CLEAR_PLUGIN_STATE') { - const {pluginId} = action.payload; - return produce(state, (draft) => { - Object.keys(draft).forEach((pluginKey) => { - const pluginKeyParts = deconstructPluginKey(pluginKey); - if (pluginKeyParts.pluginName === pluginId) { - delete draft[pluginKey]; - } - }); - }); - } else { - return state; - } -} - -export const setPluginState = (payload: { - pluginKey: string; - state: Object; -}): Action => ({ - type: 'SET_PLUGIN_STATE', - payload, -}); - -export const clearPluginState = (payload: {pluginId: string}): Action => ({ - type: 'CLEAR_PLUGIN_STATE', - payload, -}); diff --git a/desktop/app/src/sandy-chrome/SandyApp.tsx b/desktop/app/src/sandy-chrome/SandyApp.tsx index 5f3a25320..0c1f1196a 100644 --- a/desktop/app/src/sandy-chrome/SandyApp.tsx +++ b/desktop/app/src/sandy-chrome/SandyApp.tsx @@ -169,7 +169,7 @@ export function SandyApp() { )} ) : ( - + )} {outOfContentsContainer} diff --git a/desktop/app/src/selectors/connections.tsx b/desktop/app/src/selectors/connections.tsx index 755b72586..0cf6b8997 100644 --- a/desktop/app/src/selectors/connections.tsx +++ b/desktop/app/src/selectors/connections.tsx @@ -108,10 +108,9 @@ export const getPluginLists = createSelector( ); export const getExportablePlugins = createSelector( - ({plugins, connections, pluginStates, pluginMessageQueue}: State) => ({ + ({plugins, connections, pluginMessageQueue}: State) => ({ plugins, connections, - pluginStates, pluginMessageQueue, }), getActiveDevice, diff --git a/desktop/app/src/test-utils/createMockFlipperWithPlugin.tsx b/desktop/app/src/test-utils/createMockFlipperWithPlugin.tsx index 59b50d5b5..131cdd381 100644 --- a/desktop/app/src/test-utils/createMockFlipperWithPlugin.tsx +++ b/desktop/app/src/test-utils/createMockFlipperWithPlugin.tsx @@ -27,7 +27,7 @@ import {Store} from '../reducers/index'; import Client, {ClientQuery} from '../Client'; import {Logger} from '../fb-interfaces/Logger'; -import {FlipperDevicePlugin, PluginDefinition} from '../plugin'; +import {FlipperDevicePlugin, FlipperPlugin, PluginDefinition} from '../plugin'; import PluginContainer from '../PluginContainer'; import {getPluginKey, isDevicePluginDefinition} from '../utils/pluginUtils'; import MockFlipper from './MockFlipper'; @@ -66,13 +66,12 @@ type MockOptions = Partial<{ * the base implementation will be used */ onSend?: (pluginId: string, method: string, params?: object) => any; - additionalPlugins?: PluginDefinition[]; + additionalPlugins?: (PluginDefinition | LegacyPluginDefinition)[]; dontEnableAdditionalPlugins?: true; asBackgroundPlugin?: true; supportedPlugins?: string[]; device?: BaseDevice; archivedDevice?: boolean; - disableLegacyWrapper?: boolean; }>; function isPluginEnabled( @@ -90,34 +89,38 @@ function isPluginEnabled( ); } +export type LegacyPluginDefinition = + | typeof FlipperDevicePlugin + | typeof FlipperPlugin; + +export function wrapSandy( + clazz: PluginDefinition | LegacyPluginDefinition, +): PluginDefinition { + return clazz instanceof _SandyPluginDefinition + ? clazz + : createSandyPluginFromClassicPlugin( + createMockActivatablePluginDetails({ + id: clazz.id, + title: clazz.title ?? clazz.id, + pluginType: + clazz.prototype instanceof FlipperDevicePlugin + ? 'device' + : 'client', + }), + clazz, + ); +} + export async function createMockFlipperWithPlugin( - pluginClazz: PluginDefinition, + pluginClazzOrig: PluginDefinition | LegacyPluginDefinition, options?: MockOptions, ): Promise { - function wrapSandy(clazz: PluginDefinition) { - return clazz instanceof _SandyPluginDefinition || - options?.disableLegacyWrapper - ? clazz - : createSandyPluginFromClassicPlugin( - createMockActivatablePluginDetails({ - id: clazz.id, - title: clazz.title ?? clazz.id, - pluginType: - clazz.prototype instanceof FlipperDevicePlugin - ? 'device' - : 'client', - }), - clazz, - ); - } + const pluginClazz = wrapSandy(pluginClazzOrig); + const additionalPlugins = options?.additionalPlugins?.map(wrapSandy) ?? []; - pluginClazz = wrapSandy(pluginClazz); const mockFlipper = new MockFlipper(); await mockFlipper.init({ - plugins: [ - pluginClazz, - ...(options?.additionalPlugins?.map(wrapSandy) ?? []), - ], + plugins: [pluginClazz, ...additionalPlugins], }); const logger = mockFlipper.logger; const store = mockFlipper.store; @@ -150,7 +153,7 @@ export async function createMockFlipperWithPlugin( } if (!options?.dontEnableAdditionalPlugins) { - options?.additionalPlugins?.forEach((plugin) => { + additionalPlugins.forEach((plugin) => { if (!isPluginEnabled(store, plugin, name)) { store.dispatch( switchPlugin({ @@ -246,7 +249,7 @@ export async function createMockFlipperWithPlugin( type Renderer = RenderResult; export async function renderMockFlipperWithPlugin( - pluginClazz: PluginDefinition, + pluginClazzOrig: PluginDefinition | LegacyPluginDefinition, options?: MockOptions, ): Promise< MockFlipperResult & { @@ -254,6 +257,7 @@ export async function renderMockFlipperWithPlugin( act: (cb: () => void) => void; } > { + const pluginClazz = wrapSandy(pluginClazzOrig); const args = await createMockFlipperWithPlugin(pluginClazz, options); function selectTestPlugin(store: Store, client: Client) { diff --git a/desktop/app/src/utils/__tests__/exportData.node.tsx b/desktop/app/src/utils/__tests__/exportData.node.tsx index 1ae5a803a..3ed1b311f 100644 --- a/desktop/app/src/utils/__tests__/exportData.node.tsx +++ b/desktop/app/src/utils/__tests__/exportData.node.tsx @@ -20,7 +20,10 @@ import { import {FlipperPlugin, FlipperDevicePlugin} from '../../plugin'; import {default as Client, ClientExport} from '../../Client'; import {selectedPlugins, State as PluginsState} from '../../reducers/plugins'; -import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin'; +import { + createMockFlipperWithPlugin, + wrapSandy, +} from '../../test-utils/createMockFlipperWithPlugin'; import { Notification, TestUtils, @@ -39,12 +42,16 @@ function testOnStatusMessage() { // emtpy stub } -class TestPlugin extends FlipperPlugin {} -TestPlugin.title = 'TestPlugin'; -TestPlugin.id = 'TestPlugin'; -class TestDevicePlugin extends FlipperDevicePlugin {} -TestDevicePlugin.title = 'TestDevicePlugin'; -TestDevicePlugin.id = 'TestDevicePlugin'; +class TestPluginOrig extends FlipperPlugin {} +TestPluginOrig.title = 'TestPlugin'; +TestPluginOrig.id = 'TestPlugin'; +const TestPlugin = wrapSandy(TestPluginOrig); + +class TestDevicePluginOrig extends FlipperDevicePlugin {} +TestDevicePluginOrig.title = 'TestDevicePlugin'; +TestDevicePluginOrig.id = 'TestDevicePlugin'; +const TestDevicePlugin = wrapSandy(TestDevicePluginOrig); + const logger = { track: () => {}, info: () => {}, @@ -193,7 +200,6 @@ test('test processStore function for empty state', async () => { processStore({ activeNotifications: [], device: null, - pluginStates: {}, clients: [], devicePlugins: new Map(), clientPlugins: new Map(), @@ -216,7 +222,6 @@ test('test processStore function for an iOS device connected', async () => { os: 'iOS', screenshotHandle: null, }), - pluginStates: {}, pluginStates2: {}, clients: [], devicePlugins: new Map(), @@ -238,8 +243,8 @@ test('test processStore function for an iOS device connected', async () => { expect(deviceType).toEqual('emulator'); expect(title).toEqual('TestiPhone'); expect(os).toEqual('iOS'); - const {pluginStates, activeNotifications} = json.store; - expect(pluginStates).toEqual({}); + const {activeNotifications} = json.store; + expect(json.pluginStates2).toEqual({}); expect(activeNotifications).toEqual([]); }); @@ -256,11 +261,11 @@ test('test processStore function for an iOS device connected with client plugin const json = await processStore({ activeNotifications: [], device, - pluginStates: { - [`${clientIdentifier}#TestPlugin`]: {msg: 'Test plugin'}, - }, pluginStates2: { - [`${clientIdentifier}`]: {TestPlugin2: [{msg: 'Test plugin2'}]}, + [clientIdentifier]: { + TestPlugin2: [{msg: 'Test plugin2'}], + TestPlugin: {msg: 'Test plugin'}, + }, }, clients: [client], devicePlugins: new Map(), @@ -271,25 +276,16 @@ test('test processStore function for an iOS device connected with client plugin if (!json) { fail('json is undefined'); } - const {pluginStates} = json.store; - const expectedPluginState = { - [`${generateClientIdentifierWithSalt( - clientIdentifier, - 'salt', - )}#TestPlugin`]: JSON.stringify({ - msg: 'Test plugin', - }), - }; const expectedPluginState2 = { - [`${generateClientIdentifierWithSalt(clientIdentifier, 'salt')}`]: { + [generateClientIdentifierWithSalt(clientIdentifier, 'salt')]: { TestPlugin2: [ { msg: 'Test plugin2', }, ], + TestPlugin: {msg: 'Test plugin'}, }, }; - expect(pluginStates).toEqual(expectedPluginState); expect(json.pluginStates2).toEqual(expectedPluginState2); }); @@ -324,15 +320,18 @@ test('test processStore function to have only the client for the selected device const json = await processStore({ activeNotifications: [], device: selectedDevice, - pluginStates: { - [unselectedDeviceClientIdentifier + '#TestDevicePlugin']: { - msg: 'Test plugin unselected device', + pluginStates2: { + [unselectedDeviceClientIdentifier]: { + TestDevicePlugin: { + msg: 'Test plugin unselected device', + }, }, - [selectedDeviceClientIdentifier + '#TestDevicePlugin']: { - msg: 'Test plugin selected device', + [selectedDeviceClientIdentifier]: { + TestDevicePlugin: { + msg: 'Test plugin selected device', + }, }, }, - pluginStates2: {}, clients: [ selectedDeviceClient, generateClientFromDevice(unselectedDevice, 'testapp'), @@ -347,17 +346,18 @@ test('test processStore function to have only the client for the selected device fail('json is undefined'); } const {clients} = json; - const {pluginStates} = json.store; const expectedPluginState = { - [generateClientIdentifierWithSalt(selectedDeviceClientIdentifier, 'salt') + - '#TestDevicePlugin']: JSON.stringify({ - msg: 'Test plugin selected device', - }), + [generateClientIdentifierWithSalt(selectedDeviceClientIdentifier, 'salt')]: + { + TestDevicePlugin: { + msg: 'Test plugin selected device', + }, + }, }; expect(clients).toEqual([ generateClientFromClientWithSalt(selectedDeviceClient, 'salt'), ]); - expect(pluginStates).toEqual(expectedPluginState); + expect(json.pluginStates2).toEqual(expectedPluginState); }); test('test processStore function to have multiple clients for the selected device', async () => { @@ -384,15 +384,18 @@ test('test processStore function to have multiple clients for the selected devic const json = await processStore({ activeNotifications: [], device: selectedDevice, - pluginStates: { - [clientIdentifierApp1 + '#TestPlugin']: { - msg: 'Test plugin App1', + pluginStates2: { + [clientIdentifierApp1]: { + TestPlugin: { + msg: 'Test plugin App1', + }, }, - [clientIdentifierApp2 + '#TestPlugin']: { - msg: 'Test plugin App2', + [clientIdentifierApp2]: { + TestPlugin: { + msg: 'Test plugin App2', + }, }, }, - pluginStates2: {}, clients: [ generateClientFromDevice(selectedDevice, 'testapp1'), generateClientFromDevice(selectedDevice, 'testapp2'), @@ -407,22 +410,23 @@ test('test processStore function to have multiple clients for the selected devic fail('json is undefined'); } const {clients} = json; - const {pluginStates} = json.store; const expectedPluginState = { - [generateClientIdentifierWithSalt(clientIdentifierApp1, 'salt') + - '#TestPlugin']: JSON.stringify({ - msg: 'Test plugin App1', - }), - [generateClientIdentifierWithSalt(clientIdentifierApp2, 'salt') + - '#TestPlugin']: JSON.stringify({ - msg: 'Test plugin App2', - }), + [generateClientIdentifierWithSalt(clientIdentifierApp1, 'salt')]: { + TestPlugin: { + msg: 'Test plugin App1', + }, + }, + [generateClientIdentifierWithSalt(clientIdentifierApp2, 'salt')]: { + TestPlugin: { + msg: 'Test plugin App2', + }, + }, }; expect(clients).toEqual([ generateClientFromClientWithSalt(client1, 'salt'), generateClientFromClientWithSalt(client2, 'salt'), ]); - expect(pluginStates).toEqual(expectedPluginState); + expect(json.pluginStates2).toEqual(expectedPluginState); }); test('test processStore function for device plugin state and no clients', async () => { @@ -437,12 +441,13 @@ test('test processStore function for device plugin state and no clients', async const json = await processStore({ activeNotifications: [], device: selectedDevice, - pluginStates: { - 'serial#TestDevicePlugin': { - msg: 'Test Device plugin', + pluginStates2: { + serial: { + TestDevicePlugin: { + msg: 'Test Device plugin', + }, }, }, - pluginStates2: {}, clients: [], devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]), clientPlugins: new Map(), @@ -453,12 +458,11 @@ test('test processStore function for device plugin state and no clients', async if (!json) { fail('json is undefined'); } - const {pluginStates} = json.store; const {clients} = json; const expectedPluginState = { - 'salt-serial#TestDevicePlugin': JSON.stringify({msg: 'Test Device plugin'}), + 'salt-serial': {TestDevicePlugin: {msg: 'Test Device plugin'}}, }; - expect(pluginStates).toEqual(expectedPluginState); + expect(json.pluginStates2).toEqual(expectedPluginState); expect(clients).toEqual([]); }); @@ -474,12 +478,13 @@ test('test processStore function for unselected device plugin state and no clien const json = await processStore({ activeNotifications: [], device: selectedDevice, - pluginStates: { - 'unselectedDeviceIdentifier#TestDevicePlugin': { - msg: 'Test Device plugin', + pluginStates2: { + unselectedDeviceIdentifier: { + TestDevicePlugin: { + msg: 'Test Device plugin', + }, }, }, - pluginStates2: {}, clients: [], devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]), clientPlugins: new Map(), @@ -489,9 +494,8 @@ test('test processStore function for unselected device plugin state and no clien if (!json) { fail('json is undefined'); } - const {pluginStates} = json.store; const {clients} = json; - expect(pluginStates).toEqual({}); + expect(json.pluginStates2).toEqual({}); expect(clients).toEqual([]); }); @@ -519,7 +523,6 @@ test('test processStore function for notifications for selected device', async ( const json = await processStore({ activeNotifications: [activeNotification], device: selectedDevice, - pluginStates: {}, pluginStates2: {}, clients: [client], devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]), @@ -531,9 +534,8 @@ test('test processStore function for notifications for selected device', async ( if (!json) { fail('json is undefined'); } - const {pluginStates} = json.store; const {clients} = json; - expect(pluginStates).toEqual({}); + expect(json.pluginStates2).toEqual({}); expect(clients).toEqual([generateClientFromClientWithSalt(client, 'salt')]); const {activeNotifications} = json.store; const expectedActiveNotification = { @@ -580,7 +582,6 @@ test('test processStore function for notifications for unselected device', async const json = await processStore({ activeNotifications: [activeNotification], device: selectedDevice, - pluginStates: {}, pluginStates2: {}, clients: [client, unselectedclient], devicePlugins: new Map(), @@ -591,9 +592,8 @@ test('test processStore function for notifications for unselected device', async if (!json) { fail('json is undefined'); } - const {pluginStates} = json.store; const {clients} = json; - expect(pluginStates).toEqual({}); + expect(json.pluginStates2).toEqual({}); expect(clients).toEqual([generateClientFromClientWithSalt(client, 'salt')]); const {activeNotifications} = json.store; expect(activeNotifications).toEqual([]); @@ -610,18 +610,21 @@ test('test processStore function for selected plugins', async () => { const client = generateClientFromDevice(selectedDevice, 'app'); const pluginstates = { - [generateClientIdentifier(selectedDevice, 'app') + '#TestDevicePlugin1']: { - msg: 'Test plugin1', + [generateClientIdentifier(selectedDevice, 'app')]: { + TestDevicePlugin1: { + msg: 'Test plugin1', + }, }, - [generateClientIdentifier(selectedDevice, 'app') + '#TestDevicePlugin2']: { - msg: 'Test plugin2', + [generateClientIdentifier(selectedDevice, 'app')]: { + TestDevicePlugin2: { + msg: 'Test plugin2', + }, }, }; const json = await processStore({ activeNotifications: [], device: selectedDevice, - pluginStates: pluginstates, - pluginStates2: {}, + pluginStates2: pluginstates as any, clients: [client], devicePlugins: new Map([ ['TestDevicePlugin1', TestDevicePlugin], @@ -634,15 +637,16 @@ test('test processStore function for selected plugins', async () => { if (!json) { fail('json is undefined'); } - const {pluginStates} = json.store; const {clients} = json; - expect(pluginStates).toEqual({ + expect(json.pluginStates2).toEqual({ [generateClientIdentifierWithSalt( generateClientIdentifier(selectedDevice, 'app'), 'salt', - ) + '#TestDevicePlugin2']: JSON.stringify({ - msg: 'Test plugin2', - }), + )]: { + TestDevicePlugin2: { + msg: 'Test plugin2', + }, + }, }); expect(clients).toEqual([generateClientFromClientWithSalt(client, 'salt')]); const {activeNotifications} = json.store; @@ -659,18 +663,19 @@ test('test processStore function for no selected plugins', async () => { }); const client = generateClientFromDevice(selectedDevice, 'app'); const pluginstates = { - [generateClientIdentifier(selectedDevice, 'app') + '#TestDevicePlugin1']: { - msg: 'Test plugin1', - }, - [generateClientIdentifier(selectedDevice, 'app') + '#TestDevicePlugin2']: { - msg: 'Test plugin2', + [generateClientIdentifier(selectedDevice, 'app')]: { + TestDevicePlugin1: { + msg: 'Test plugin1', + }, + TestDevicePlugin2: { + msg: 'Test plugin2', + }, }, }; const json = await processStore({ activeNotifications: [], device: selectedDevice, - pluginStates: pluginstates, - pluginStates2: {}, + pluginStates2: pluginstates as any, clients: [client], devicePlugins: new Map([ ['TestDevicePlugin1', TestDevicePlugin], @@ -684,21 +689,20 @@ test('test processStore function for no selected plugins', async () => { if (!json) { fail('json is undefined'); } - const {pluginStates} = json.store; const {clients} = json; - expect(pluginStates).toEqual({ + expect(json.pluginStates2).toEqual({ [generateClientIdentifierWithSalt( generateClientIdentifier(selectedDevice, 'app'), 'salt', - ) + '#TestDevicePlugin2']: JSON.stringify({ - msg: 'Test plugin2', - }), - [generateClientIdentifierWithSalt( - generateClientIdentifier(selectedDevice, 'app'), - 'salt', - ) + '#TestDevicePlugin1']: JSON.stringify({ - msg: 'Test plugin1', - }), + )]: { + TestDevicePlugin2: { + msg: 'Test plugin2', + }, + + TestDevicePlugin1: { + msg: 'Test plugin1', + }, + }, }); expect(clients).toEqual([generateClientFromClientWithSalt(client, 'salt')]); const {activeNotifications} = json.store; @@ -781,14 +785,12 @@ test('test determinePluginsToProcess for mutilple clients having plugins present pluginKey: `${client1.id}#TestPlugin`, pluginId: 'TestPlugin', pluginName: 'TestPlugin', - pluginClass: TestPlugin, client: client1, }, { pluginKey: `${client3.id}#TestPlugin`, pluginId: 'TestPlugin', pluginName: 'TestPlugin', - pluginClass: TestPlugin, client: client3, }, ]); @@ -905,7 +907,6 @@ test('test determinePluginsToProcess for multiple clients on same device', async pluginKey: `${client1.id}#TestPlugin`, pluginId: 'TestPlugin', pluginName: 'TestPlugin', - pluginClass: TestPlugin, client: client1, }, ]); @@ -999,7 +1000,6 @@ test('test determinePluginsToProcess for multiple clients on different device', pluginKey: `${client1Device2.id}#TestPlugin`, pluginId: 'TestPlugin', pluginName: 'TestPlugin', - pluginClass: TestPlugin, client: client1Device2, }, ]); @@ -1085,7 +1085,6 @@ test('test determinePluginsToProcess to ignore archived clients', async () => { pluginKey: `${client.id}#TestPlugin`, pluginId: 'TestPlugin', pluginName: 'TestPlugin', - pluginClass: TestPlugin, client: client, }, ]); diff --git a/desktop/app/src/utils/__tests__/messageQueue.node.tsx b/desktop/app/src/utils/__tests__/messageQueue.node.tsx deleted file mode 100644 index 329ae109a..000000000 --- a/desktop/app/src/utils/__tests__/messageQueue.node.tsx +++ /dev/null @@ -1,684 +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 {FlipperPlugin, FlipperDevicePlugin} from '../../plugin'; -import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin'; -import {Store, Client, sleep} from '../../'; -import { - selectPlugin, - selectClient, - selectDevice, -} from '../../reducers/connections'; -import {processMessageQueue} from '../messageQueue'; -import {getPluginKey} from '../pluginUtils'; -import {TestIdler} from '../Idler'; -import pluginMessageQueue, { - State, - queueMessages, -} from '../../reducers/pluginMessageQueue'; -import {registerPlugins} from '../../reducers/plugins'; -import {switchPlugin} from '../../reducers/pluginManager'; - -interface PersistedState { - count: 1; -} - -class TestPlugin extends FlipperPlugin { - static id = 'TestPlugin'; - - static defaultPersistedState = { - count: 0, - }; - - static persistedStateReducer( - persistedState: PersistedState, - method: string, - payload: {delta?: number}, - ) { - if (method === 'inc') { - return Object.assign({}, persistedState, { - count: persistedState.count + ((payload && payload?.delta) || 1), - }); - } - return persistedState; - } - - render() { - return null; - } -} - -function switchTestPlugin(store: Store, client: Client) { - store.dispatch( - switchPlugin({ - plugin: TestPlugin, - selectedApp: client.query.app, - }), - ); -} - -function selectDeviceLogs(store: Store) { - store.dispatch( - selectPlugin({ - selectedPlugin: 'DeviceLogs', - selectedApp: null, - deepLinkPayload: null, - selectedDevice: store.getState().connections.selectedDevice!, - }), - ); -} - -function selectTestPlugin(store: Store, client: Client) { - store.dispatch( - selectPlugin({ - selectedPlugin: TestPlugin.id, - selectedApp: client.query.app, - deepLinkPayload: null, - selectedDevice: store.getState().connections.selectedDevice!, - }), - ); -} - -test('queue - events are processed immediately if plugin is selected', async () => { - const {store, client, sendMessage} = await createMockFlipperWithPlugin( - TestPlugin, - { - disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx - }, - ); - expect(store.getState().connections.selectedPlugin).toBe('TestPlugin'); - sendMessage('noop', {}); - sendMessage('noop', {}); - sendMessage('inc', {}); - sendMessage('inc', {delta: 4}); - sendMessage('noop', {}); - client.flushMessageBuffer(); - expect(store.getState().pluginStates).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object { - "count": 5, - }, - } - `); - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot( - `Object {}`, - ); -}); - -test('queue - events are NOT processed immediately if plugin is NOT selected (but enabled)', async () => { - const {store, client, sendMessage, device} = - await createMockFlipperWithPlugin(TestPlugin, { - disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx - }); - selectDeviceLogs(store); - expect(store.getState().connections.selectedPlugin).not.toBe('TestPlugin'); - - sendMessage('inc', {}); - sendMessage('inc', {delta: 2}); - sendMessage('inc', {delta: 3}); - expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`); - // the first message is already visible cause of the leading debounce - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object {}, - }, - ], - } - `); - client.flushMessageBuffer(); - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object {}, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 2, - }, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 3, - }, - }, - ], - } - `); - - // process the message - const pluginKey = getPluginKey(client.id, device, TestPlugin.id); - await processMessageQueue(TestPlugin, pluginKey, store); - expect(store.getState().pluginStates).toEqual({ - [pluginKey]: { - count: 6, - }, - }); - - expect(store.getState().pluginMessageQueue).toEqual({ - [pluginKey]: [], - }); - - // disable, but, messages still arrives because selected - switchTestPlugin(store, client); - selectTestPlugin(store, client); - sendMessage('inc', {delta: 3}); - client.flushMessageBuffer(); - // active, immediately processed - expect(store.getState().pluginStates).toEqual({ - [pluginKey]: { - count: 9, - }, - }); - - // different plugin, and not enabled, message will never arrive - selectDeviceLogs(store); - sendMessage('inc', {delta: 4}); - client.flushMessageBuffer(); - expect(store.getState().pluginMessageQueue).toEqual({}); - - // star again, plugin still not selected, message is queued - switchTestPlugin(store, client); - sendMessage('inc', {delta: 5}); - client.flushMessageBuffer(); - - expect(store.getState().pluginMessageQueue).toEqual({ - [pluginKey]: [{api: 'TestPlugin', method: 'inc', params: {delta: 5}}], - }); -}); - -test('queue - events are queued for plugins that are favorite when app is not selected', async () => { - const {device, store, sendMessage, createClient} = - await createMockFlipperWithPlugin(TestPlugin, { - disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx - }); - selectDeviceLogs(store); - expect(store.getState().connections.selectedPlugin).not.toBe('TestPlugin'); - - const client2 = await createClient(device, 'TestApp2'); - store.dispatch(selectClient(client2.id)); - - // Now we send a message to the second client, it should arrive, - // as the plugin was enabled already on the first client as well - sendMessage('inc', {delta: 2}); - expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`); - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 2, - }, - }, - ], - } - `); -}); - -test('queue - events are queued for plugins that are favorite when app is selected on different device', async () => { - const {client, store, sendMessage, createDevice, createClient} = - await createMockFlipperWithPlugin(TestPlugin, { - disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx - }); - selectDeviceLogs(store); - expect(store.getState().connections.selectedPlugin).not.toBe('TestPlugin'); - - const device2 = createDevice('serial2'); - const client2 = await createClient(device2, client.query.app); // same app id - store.dispatch(selectDevice(device2)); - store.dispatch(selectClient(client2.id)); - - // Now we send a message to the first and second client, it should arrive, - // as the plugin was enabled already on the first client as well - sendMessage('inc', {delta: 2}); - sendMessage('inc', {delta: 3}, client2); - client.flushMessageBuffer(); - client2.flushMessageBuffer(); - expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`); - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 2, - }, - }, - ], - "TestApp#Android#MockAndroidDevice#serial2#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 3, - }, - }, - ], - } - `); -}); - -test('queue - events processing will be paused', async () => { - const {client, device, store, sendMessage} = - await createMockFlipperWithPlugin(TestPlugin, { - disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx - }); - selectDeviceLogs(store); - - sendMessage('inc', {}); - sendMessage('inc', {delta: 3}); - sendMessage('inc', {delta: 5}); - client.flushMessageBuffer(); - - // process the message - const pluginKey = getPluginKey(client.id, device, TestPlugin.id); - - // controlled idler will signal and and off that idling is needed - const idler = new TestIdler(); - - const p = processMessageQueue(TestPlugin, pluginKey, store, undefined, idler); - - expect(store.getState().pluginStates).toEqual({ - [pluginKey]: { - count: 4, - }, - }); - - expect(store.getState().pluginMessageQueue).toEqual({ - [pluginKey]: [{api: 'TestPlugin', method: 'inc', params: {delta: 5}}], - }); - - await idler.next(); - expect(store.getState().pluginStates).toEqual({ - [pluginKey]: { - count: 9, - }, - }); - - expect(store.getState().pluginMessageQueue).toEqual({ - [pluginKey]: [], - }); - - // don't idle anymore - idler.run(); - await p; -}); - -test('queue - messages that arrive during processing will be queued', async () => { - const {client, device, store, sendMessage} = - await createMockFlipperWithPlugin(TestPlugin, { - disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx - }); - selectDeviceLogs(store); - - sendMessage('inc', {}); - sendMessage('inc', {delta: 2}); - sendMessage('inc', {delta: 3}); - client.flushMessageBuffer(); - - // process the message - const pluginKey = getPluginKey(client.id, device, TestPlugin.id); - - const idler = new TestIdler(); - - const p = processMessageQueue(TestPlugin, pluginKey, store, undefined, idler); - - // first message is consumed - expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(1); - expect(store.getState().pluginStates[pluginKey].count).toBe(3); - - // Select the current plugin as active, still, messages should end up in the queue - store.dispatch( - selectPlugin({ - selectedPlugin: TestPlugin.id, - selectedApp: client.id, - deepLinkPayload: null, - selectedDevice: device, - }), - ); - expect(store.getState().connections.selectedPlugin).toBe('TestPlugin'); - - sendMessage('inc', {delta: 4}); - client.flushMessageBuffer(); - // should not be processed yet - expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(2); - expect(store.getState().pluginStates[pluginKey].count).toBe(3); - - await idler.next(); - expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(0); - expect(store.getState().pluginStates[pluginKey].count).toBe(10); - - idler.run(); - await p; -}); - -test('queue - processing can be cancelled', async () => { - const {client, device, store, sendMessage} = - await createMockFlipperWithPlugin(TestPlugin, { - disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx - }); - selectDeviceLogs(store); - - sendMessage('inc', {}); - sendMessage('inc', {delta: 2}); - sendMessage('inc', {delta: 3}); - sendMessage('inc', {delta: 4}); - sendMessage('inc', {delta: 5}); - client.flushMessageBuffer(); - - // process the message - const pluginKey = getPluginKey(client.id, device, TestPlugin.id); - - const idler = new TestIdler(); - - const p = processMessageQueue(TestPlugin, pluginKey, store, undefined, idler); - - // first message is consumed - await idler.next(); - expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(1); - expect(store.getState().pluginStates[pluginKey].count).toBe(10); - - idler.cancel(); - - expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(1); - expect(store.getState().pluginStates[pluginKey].count).toBe(10); - await p; -}); - -test('queue - make sure resetting plugin state clears the message queue', async () => { - const {client, device, store, sendMessage} = - await createMockFlipperWithPlugin(TestPlugin, { - disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx - }); - selectDeviceLogs(store); - - sendMessage('inc', {}); - sendMessage('inc', {delta: 2}); - client.flushMessageBuffer(); - - const pluginKey = getPluginKey(client.id, device, TestPlugin.id); - - expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(2); - - store.dispatch({ - type: 'CLEAR_CLIENT_PLUGINS_STATE', - payload: {clientId: client.id, devicePlugins: new Set()}, - }); - - expect(store.getState().pluginMessageQueue[pluginKey]).toBe(undefined); -}); - -test('queue will be cleaned up when it exceeds maximum size', () => { - let state: State = {}; - const pluginKey = 'test'; - const queueSize = 5000; - let i = 0; - for (i = 0; i < queueSize; i++) { - state = pluginMessageQueue( - state, - queueMessages(pluginKey, [{method: 'test', params: {i}}], queueSize), - ); - } - // almost full - expect(state[pluginKey][0]).toEqual({method: 'test', params: {i: 0}}); - expect(state[pluginKey].length).toBe(queueSize); // ~5000 - expect(state[pluginKey][queueSize - 1]).toEqual({ - method: 'test', - params: {i: queueSize - 1}, // ~4999 - }); - - state = pluginMessageQueue( - state, - queueMessages(pluginKey, [{method: 'test', params: {i: ++i}}], queueSize), - ); - - const newLength = Math.ceil(0.9 * queueSize) + 1; // ~4500 - expect(state[pluginKey].length).toBe(newLength); - expect(state[pluginKey][0]).toEqual({ - method: 'test', - params: {i: queueSize - newLength + 1}, // ~500 - }); - expect(state[pluginKey][newLength - 1]).toEqual({ - method: 'test', - params: {i: i}, // ~50001 - }); -}); - -test('client - incoming messages are buffered and flushed together', async () => { - class StubDeviceLogs extends FlipperDevicePlugin { - static id = 'DevicePlugin'; - - static supportsDevice() { - return true; - } - - static persistedStateReducer = jest.fn(); - } - - const {client, store, device, sendMessage} = - await createMockFlipperWithPlugin(TestPlugin, { - disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx - }); - selectDeviceLogs(store); - - store.dispatch(registerPlugins([StubDeviceLogs])); - sendMessage('inc', {}); - sendMessage('inc', {delta: 2}); - sendMessage('inc', {delta: 3}); - - // send a message to device logs - client.onMessage( - JSON.stringify({ - method: 'execute', - params: { - api: 'DevicePlugin', - method: 'log', - params: {line: 'suff'}, - }, - }), - ); - - expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`); - // the first message is already visible cause of the leading debounce - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object {}, - }, - ], - } - `); - expect(client.messageBuffer).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Object { - "messages": Array [ - Object { - "api": "DevicePlugin", - "method": "log", - "params": Object { - "line": "suff", - }, - }, - ], - "plugin": [Function], - }, - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object { - "messages": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 2, - }, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 3, - }, - }, - ], - "plugin": [Function], - }, - } - `); - - await sleep(500); - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Array [ - Object { - "api": "DevicePlugin", - "method": "log", - "params": Object { - "line": "suff", - }, - }, - ], - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object {}, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 2, - }, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 3, - }, - }, - ], - } - `); - expect(client.messageBuffer).toMatchInlineSnapshot(`Object {}`); - expect(StubDeviceLogs.persistedStateReducer.mock.calls).toMatchInlineSnapshot( - `Array []`, - ); - - // tigger processing the queue - const pluginKey = getPluginKey(client.id, device, StubDeviceLogs.id); - await processMessageQueue(StubDeviceLogs, pluginKey, store); - - expect(StubDeviceLogs.persistedStateReducer.mock.calls) - .toMatchInlineSnapshot(` - Array [ - Array [ - Object {}, - "log", - Object { - "line": "suff", - }, - ], - ] - `); - - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Array [], - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object {}, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 2, - }, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 3, - }, - }, - ], - } - `); -}); - -test('queue - messages that have not yet flushed be lost when disabling the plugin', async () => { - const {client, store, sendMessage, pluginKey} = - await createMockFlipperWithPlugin(TestPlugin, { - disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx - }); - selectDeviceLogs(store); - - sendMessage('inc', {}); - sendMessage('inc', {delta: 2}); - - expect(client.messageBuffer).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object { - "messages": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 2, - }, - }, - ], - "plugin": [Function], - }, - } - `); - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object {}, - }, - ], - } - `); - - // disable - switchTestPlugin(store, client); - expect(client.messageBuffer).toMatchInlineSnapshot(`Object {}`); - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot( - `Object {}`, - ); - - // re-enable, no messages arrive - switchTestPlugin(store, client); - client.flushMessageBuffer(); - processMessageQueue(TestPlugin, pluginKey, store); - expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`); -}); diff --git a/desktop/app/src/utils/__tests__/messageQueueSandy.node.tsx b/desktop/app/src/utils/__tests__/messageQueueSandy.node.tsx index cc49e2c1d..609a5ab9a 100644 --- a/desktop/app/src/utils/__tests__/messageQueueSandy.node.tsx +++ b/desktop/app/src/utils/__tests__/messageQueueSandy.node.tsx @@ -7,8 +7,11 @@ * @format */ -import {FlipperDevicePlugin} from '../../plugin'; -import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin'; +import {FlipperPlugin} from '../../plugin'; +import { + createMockFlipperWithPlugin, + wrapSandy, +} from '../../test-utils/createMockFlipperWithPlugin'; import {Store, Client, sleep} from '../../'; import { selectPlugin, @@ -26,6 +29,10 @@ import { _SandyPluginInstance, } from 'flipper-plugin'; import {switchPlugin} from '../../reducers/pluginManager'; +import pluginMessageQueue, { + State, + queueMessages, +} from '../../reducers/pluginMessageQueue'; type Events = { inc: { @@ -124,7 +131,6 @@ test('queue - events are NOT processed immediately if plugin is NOT selected (bu sendMessage('inc', {}); sendMessage('inc', {delta: 2}); sendMessage('inc', {delta: 3}); - expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`); expect(getTestPluginState(client).count).toBe(0); // the first message is already visible cause of the leading debounce expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` @@ -482,21 +488,21 @@ test('queue - make sure resetting plugin state clears the message queue', async }); test('client - incoming messages are buffered and flushed together', async () => { - class StubDeviceLogs extends FlipperDevicePlugin { - static id = 'DevicePlugin'; - - static supportsDevice() { - return true; - } + class StubPlugin extends FlipperPlugin { + static id = 'StubPlugin'; static persistedStateReducer = jest.fn(); } + const StubPluginWrapped = wrapSandy(StubPlugin); + const {client, store, device, sendMessage, pluginKey} = - await createMockFlipperWithPlugin(TestPlugin); + await createMockFlipperWithPlugin(TestPlugin, { + additionalPlugins: [StubPluginWrapped], + }); selectDeviceLogs(store); - store.dispatch(registerPlugins([StubDeviceLogs])); + store.dispatch(registerPlugins([StubPluginWrapped])); sendMessage('inc', {}); sendMessage('inc', {delta: 2}); sendMessage('inc', {delta: 3}); @@ -506,14 +512,13 @@ test('client - incoming messages are buffered and flushed together', async () => JSON.stringify({ method: 'execute', params: { - api: 'DevicePlugin', + api: 'StubPlugin', method: 'log', params: {line: 'suff'}, }, }), ); - expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`); expect(getTestPluginState(client).count).toBe(0); // the first message is already visible cause of the leading debounce expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` @@ -529,17 +534,17 @@ test('client - incoming messages are buffered and flushed together', async () => `); expect(client.messageBuffer).toMatchInlineSnapshot(` Object { - "TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Object { + "TestApp#Android#MockAndroidDevice#serial#StubPlugin": Object { "messages": Array [ Object { - "api": "DevicePlugin", + "api": "StubPlugin", "method": "log", "params": Object { "line": "suff", }, }, ], - "plugin": [Function], + "plugin": "[SandyPluginInstance]", }, "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object { "messages": Array [ @@ -569,9 +574,9 @@ test('client - incoming messages are buffered and flushed together', async () => await sleep(500); expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` Object { - "TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Array [ + "TestApp#Android#MockAndroidDevice#serial#StubPlugin": Array [ Object { - "api": "DevicePlugin", + "api": "StubPlugin", "method": "log", "params": Object { "line": "suff", @@ -602,19 +607,22 @@ test('client - incoming messages are buffered and flushed together', async () => } `); expect(client.messageBuffer).toMatchInlineSnapshot(`Object {}`); - expect(StubDeviceLogs.persistedStateReducer.mock.calls).toMatchInlineSnapshot( + expect(StubPlugin.persistedStateReducer.mock.calls).toMatchInlineSnapshot( `Array []`, ); // tigger processing the queue - const pluginKeyDevice = getPluginKey(client.id, device, StubDeviceLogs.id); - await processMessageQueue(StubDeviceLogs, pluginKeyDevice, store); + const pluginKeyDevice = getPluginKey(client.id, device, StubPlugin.id); + await processMessageQueue( + client.sandyPluginStates.get(StubPlugin.id)!, + pluginKeyDevice, + store, + ); - expect(StubDeviceLogs.persistedStateReducer.mock.calls) - .toMatchInlineSnapshot(` + expect(StubPlugin.persistedStateReducer.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - Object {}, + undefined, "log", Object { "line": "suff", @@ -625,7 +633,7 @@ test('client - incoming messages are buffered and flushed together', async () => expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` Object { - "TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Array [], + "TestApp#Android#MockAndroidDevice#serial#StubPlugin": Array [], "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ Object { "api": "TestPlugin", @@ -704,3 +712,39 @@ test('queue - messages that have not yet flushed be lost when disabling the plug ); expect(getTestPluginState(client)).toEqual({count: 0}); }); + +test('queue will be cleaned up when it exceeds maximum size', () => { + let state: State = {}; + const pluginKey = 'test'; + const queueSize = 5000; + let i = 0; + for (i = 0; i < queueSize; i++) { + state = pluginMessageQueue( + state, + queueMessages(pluginKey, [{method: 'test', params: {i}}], queueSize), + ); + } + // almost full + expect(state[pluginKey][0]).toEqual({method: 'test', params: {i: 0}}); + expect(state[pluginKey].length).toBe(queueSize); // ~5000 + expect(state[pluginKey][queueSize - 1]).toEqual({ + method: 'test', + params: {i: queueSize - 1}, // ~4999 + }); + + state = pluginMessageQueue( + state, + queueMessages(pluginKey, [{method: 'test', params: {i: ++i}}], queueSize), + ); + + const newLength = Math.ceil(0.9 * queueSize) + 1; // ~4500 + expect(state[pluginKey].length).toBe(newLength); + expect(state[pluginKey][0]).toEqual({ + method: 'test', + params: {i: queueSize - newLength + 1}, // ~500 + }); + expect(state[pluginKey][newLength - 1]).toEqual({ + method: 'test', + params: {i: i}, // ~50001 + }); +}); diff --git a/desktop/app/src/utils/__tests__/pluginUtils.node.tsx b/desktop/app/src/utils/__tests__/pluginUtils.node.tsx index 780bd7286..7167aaf20 100644 --- a/desktop/app/src/utils/__tests__/pluginUtils.node.tsx +++ b/desktop/app/src/utils/__tests__/pluginUtils.node.tsx @@ -72,7 +72,7 @@ function createMockFlipperPluginWithNoPersistedState(id: string) { } test('getActivePersistentPlugins, where the non persistent plugins getting excluded', async () => { - const {store, device, client} = await createMockFlipperWithPlugin( + const {store} = await createMockFlipperWithPlugin( createMockFlipperPluginWithDefaultPersistedState('ClientPlugin1'), { additionalPlugins: [ @@ -85,11 +85,6 @@ test('getActivePersistentPlugins, where the non persistent plugins getting exclu ); const state = store.getState(); - state.pluginStates = { - [getPluginKey(client.id, device, 'ClientPlugin1')]: {msg: 'DevicePlugin1'}, - [getPluginKey(client.id, device, 'ClientPlugin4')]: {msg: 'ClientPlugin2'}, - }; - const list = getExportablePlugins(state); expect(list).toEqual([ { @@ -97,23 +92,23 @@ test('getActivePersistentPlugins, where the non persistent plugins getting exclu label: 'ClientPlugin1', }, { - id: 'ClientPlugin4', - label: 'ClientPlugin4', + id: 'ClientPlugin2', + label: 'ClientPlugin2', + }, + { + id: 'ClientPlugin5', + label: 'ClientPlugin5', }, - // { Never activated, and no data received - // id: 'ClientPlugin5', - // label: 'ClientPlugin5', - // }, ]); }); -test('getActivePersistentPlugins, where the plugins not in pluginState or queue gets excluded', async () => { +test('getActivePersistentPlugins, with message queue', async () => { const {store, device, client} = await createMockFlipperWithPlugin( createMockFlipperPluginWithDefaultPersistedState('Plugin1'), { additionalPlugins: [ createMockDeviceFlipperPlugin('DevicePlugin2'), - createMockFlipperPluginWithDefaultPersistedState('ClientPlugin1'), + createMockFlipperPluginWithNoPersistedState('ClientPlugin1'), createMockFlipperPluginWithDefaultPersistedState('ClientPlugin2'), createMockFlipperPluginWithDefaultPersistedState('ClientPlugin3'), ], @@ -122,9 +117,6 @@ test('getActivePersistentPlugins, where the plugins not in pluginState or queue const state = store.getState(); - state.pluginStates = { - [getPluginKey(client.id, device, 'ClientPlugin2')]: {msg: 'ClientPlugin2'}, - }; state.pluginMessageQueue = { [getPluginKey(client.id, device, 'ClientPlugin3')]: [ {method: 'msg', params: {msg: 'ClientPlugin3'}}, diff --git a/desktop/app/src/utils/createSandyPluginWrapper.tsx b/desktop/app/src/utils/createSandyPluginWrapper.tsx index 0001452e0..8de134de9 100644 --- a/desktop/app/src/utils/createSandyPluginWrapper.tsx +++ b/desktop/app/src/utils/createSandyPluginWrapper.tsx @@ -70,8 +70,7 @@ export function createSandyPluginWrapper( if ( Plugin.persistedStateReducer || Plugin.exportPersistedState || - Plugin.defaultPersistedState || - Plugin.serializePersistedState + Plugin.defaultPersistedState ) { client.onExport(async (idler, onStatusMessage) => { const state = Plugin.exportPersistedState diff --git a/desktop/app/src/utils/exportData.tsx b/desktop/app/src/utils/exportData.tsx index 1f503db2e..29c39a33f 100644 --- a/desktop/app/src/utils/exportData.tsx +++ b/desktop/app/src/utils/exportData.tsx @@ -11,21 +11,14 @@ import os from 'os'; import path from 'path'; import electron from 'electron'; import {getInstance as getLogger} from '../fb-stubs/Logger'; -import {Store, State as ReduxState, MiddlewareAPI} from '../reducers'; +import {Store, MiddlewareAPI} from '../reducers'; import {DeviceExport} from '../devices/BaseDevice'; -import {State as PluginStatesState} from '../reducers/pluginStates'; import {State as PluginsState} from '../reducers/plugins'; import {PluginNotification} from '../reducers/notifications'; import Client, {ClientExport, ClientQuery} from '../Client'; import {getAppVersion} from './info'; -import {pluginKey} from '../reducers/pluginStates'; -import { - callClient, - supportsMethod, - PluginDefinition, - DevicePluginMap, - ClientPluginMap, -} from '../plugin'; +import {pluginKey} from '../utils/pluginUtils'; +import {DevicePluginMap, ClientPluginMap} from '../plugin'; import {default as BaseDevice} from '../devices/BaseDevice'; import {default as ArchivedDevice} from '../devices/ArchivedDevice'; import fs from 'fs'; @@ -34,7 +27,6 @@ import {remote, OpenDialogOptions} from 'electron'; import {readCurrentRevision} from './packageMetadata'; import {tryCatchReportPlatformFailures} from './metrics'; import {promisify} from 'util'; -import promiseTimeout from './promiseTimeout'; import {TestIdler} from './Idler'; import {setStaticView} from '../reducers/connections'; import { @@ -42,10 +34,10 @@ import { SupportFormRequestDetailsState, } from '../reducers/supportForm'; import {setSelectPluginsToExportActiveSheet} from '../reducers/application'; -import {deconstructClientId, deconstructPluginKey} from '../utils/clientUtils'; +import {deconstructClientId} from '../utils/clientUtils'; import {performance} from 'perf_hooks'; import {processMessageQueue} from './messageQueue'; -import {getPluginTitle, isSandyPlugin} from './pluginUtils'; +import {getPluginTitle} from './pluginUtils'; import {capture} from './screenshot'; import {uploadFlipperMedia} from '../fb-stubs/user'; import {Idler} from 'flipper-plugin'; @@ -71,7 +63,6 @@ export type ExportType = { device: DeviceExport | null; deviceScreenshot: string | null; store: { - pluginStates: PluginStatesExportState; activeNotifications: Array; }; // The GraphQL plugin relies on this format for generating @@ -80,15 +71,6 @@ export type ExportType = { supportRequestDetails?: SupportFormRequestDetailsState; }; -type ProcessPluginStatesOptions = { - clients: Array; - serial: string; - allPluginStates: PluginStatesState; - devicePlugins: DevicePluginMap; - selectedPlugins: Array; - statusUpdate?: (msg: string) => void; -}; - type ProcessNotificationStatesOptions = { clients: Array; serial: string; @@ -101,7 +83,6 @@ type PluginsToProcess = { pluginKey: string; pluginId: string; pluginName: string; - pluginClass: PluginDefinition; client: Client; }[]; @@ -110,7 +91,6 @@ type AddSaltToDeviceSerialOptions = { device: BaseDevice; deviceScreenshot: string | null; clients: Array; - pluginStates: PluginStatesExportState; pluginStates2: SandyPluginStates; devicePluginStates: Record; pluginNotification: Array; @@ -152,51 +132,6 @@ export function processClients( return filteredClients; } -export function processPluginStates( - options: ProcessPluginStatesOptions, -): PluginStatesState { - const { - clients, - serial, - allPluginStates, - devicePlugins, - selectedPlugins, - statusUpdate, - } = options; - - let pluginStates: PluginStatesState = {}; - statusUpdate && - statusUpdate('Filtering the plugin states for the filtered Clients...'); - for (const key in allPluginStates) { - const plugin = deconstructPluginKey(key); - - const pluginName = plugin.pluginName; - if ( - pluginName && - selectedPlugins.length > 0 && - !selectedPlugins.includes(pluginName) - ) { - continue; - } - if (plugin.type === 'client') { - if (!clients.some((c) => c.id.includes(plugin.client))) { - continue; - } - } - if (plugin.type === 'device') { - if ( - !pluginName || - !devicePlugins.has(pluginName) || - serial !== plugin.client - ) { - continue; - } - } - pluginStates = {...pluginStates, [key]: allPluginStates[key]}; - } - return pluginStates; -} - export function processNotificationStates( options: ProcessNotificationStatesOptions, ): Array { @@ -216,41 +151,6 @@ export function processNotificationStates( return activeNotifications; } -const serializePluginStates = async ( - pluginStates: PluginStatesState, - clientPlugins: ClientPluginMap, - devicePlugins: DevicePluginMap, - statusUpdate?: (msg: string) => void, - idler?: Idler, -): Promise => { - const pluginsMap = new Map([ - ...clientPlugins.entries(), - ...devicePlugins.entries(), - ]); - const pluginExportState: PluginStatesExportState = {}; - for (const key in pluginStates) { - const pluginName = deconstructPluginKey(key).pluginName; - statusUpdate && statusUpdate(`Serialising ${pluginName}...`); - const serializationMarker = `${EXPORT_FLIPPER_TRACE_EVENT}:serialization-per-plugin`; - performance.mark(serializationMarker); - const pluginClass = pluginName ? pluginsMap.get(pluginName) : null; - if (isSandyPlugin(pluginClass)) { - continue; // Those are already processed by `exportSandyPluginStates` - } else if (pluginClass) { - pluginExportState[key] = await pluginClass.serializePersistedState( - pluginStates[key], - statusUpdate, - idler, - pluginName, - ); - getLogger().trackTimeSince(serializationMarker, serializationMarker, { - plugin: pluginName, - }); - } - } - return pluginExportState; -}; - async function exportSandyPluginStates( pluginsToProcess: PluginsToProcess, idler: Idler, @@ -258,8 +158,8 @@ async function exportSandyPluginStates( ): Promise { const res: SandyPluginStates = {}; for (const key in pluginsToProcess) { - const {pluginId, client, pluginClass} = pluginsToProcess[key]; - if (isSandyPlugin(pluginClass) && client.sandyPluginStates.has(pluginId)) { + const {pluginId, client} = pluginsToProcess[key]; + if (client.sandyPluginStates.has(pluginId)) { if (!res[client.id]) { res[client.id] = {}; } @@ -276,33 +176,6 @@ async function exportSandyPluginStates( return res; } -const deserializePluginStates = ( - pluginStatesExportState: PluginStatesExportState, - clientPlugins: ClientPluginMap, - devicePlugins: DevicePluginMap, -): PluginStatesState => { - const pluginsMap = new Map([ - ...clientPlugins.entries(), - ...devicePlugins.entries(), - ]); - const pluginsState: PluginStatesState = {}; - for (const key in pluginStatesExportState) { - const pluginName = deconstructPluginKey(key).pluginName; - if (!pluginName || !pluginsMap.get(pluginName)) { - continue; - } - const pluginClass = pluginsMap.get(pluginName); - if (isSandyPlugin(pluginClass)) { - pluginsState[key] = pluginStatesExportState[key]; - } else if (pluginClass) { - pluginsState[key] = pluginClass.deserializePersistedState( - pluginStatesExportState[key], - ); - } - } - return pluginsState; -}; - function replaceSerialsInKeys>( collection: T, baseSerial: string, @@ -311,9 +184,7 @@ function replaceSerialsInKeys>( const result: Record = {}; for (const key in collection) { if (!key.includes(baseSerial)) { - throw new Error( - `Error while exporting, plugin state (${key}) does not have ${baseSerial} in its key`, - ); + continue; } result[key.replace(baseSerial, newSerial)] = collection[key]; } @@ -325,7 +196,6 @@ async function addSaltToDeviceSerial({ device, deviceScreenshot, clients, - pluginStates, pluginNotification, statusUpdate, pluginStates2, @@ -354,11 +224,6 @@ async function addSaltToDeviceSerial({ statusUpdate( 'Adding salt to the selected device id in the plugin states...', ); - const updatedPluginStates = replaceSerialsInKeys( - pluginStates, - serial, - newSerial, - ); const updatedPluginStates2 = replaceSerialsInKeys( pluginStates2, serial, @@ -385,7 +250,6 @@ async function addSaltToDeviceSerial({ device: {...newDevice.toJSON(), pluginStates: devicePluginStates}, deviceScreenshot: deviceScreenshot, store: { - pluginStates: updatedPluginStates, activeNotifications: updatedPluginNotifications, }, pluginStates2: updatedPluginStates2, @@ -395,7 +259,6 @@ async function addSaltToDeviceSerial({ type ProcessStoreOptions = { activeNotifications: Array; device: BaseDevice | null; - pluginStates: PluginStatesState; pluginStates2: SandyPluginStates; clients: Array; devicePlugins: DevicePluginMap; @@ -409,11 +272,9 @@ export async function processStore( { activeNotifications, device, - pluginStates, pluginStates2, clients, devicePlugins, - clientPlugins, salt, selectedPlugins, statusUpdate, @@ -435,14 +296,7 @@ export async function processStore( }) : null; const processedClients = processClients(clients, serial, statusUpdate); - const processedPluginStates = processPluginStates({ - clients: processedClients, - serial, - allPluginStates: pluginStates, - devicePlugins, - selectedPlugins, - statusUpdate, - }); + const processedActiveNotifications = processNotificationStates({ clients: processedClients, serial, @@ -451,14 +305,6 @@ export async function processStore( statusUpdate, }); - const exportPluginState = await serializePluginStates( - processedPluginStates, - clientPlugins, - devicePlugins, - statusUpdate, - idler, - ); - const devicePluginStates = await device.exportState( idler, statusUpdate, @@ -478,7 +324,6 @@ export async function processStore( device, deviceScreenshot: deviceScreenshotLink, clients: processedClients, - pluginStates: exportPluginState, pluginNotification: processedActiveNotifications, statusUpdate, selectedPlugins, @@ -492,116 +337,35 @@ export async function processStore( throw new Error('Selected device is null, please select a device'); } -export async function fetchMetadata( - pluginsToProcess: PluginsToProcess, - pluginStates: PluginStatesState, - state: ReduxState, - statusUpdate: (msg: string) => void, - idler: Idler, -): Promise<{ - pluginStates: PluginStatesState; - errors: {[plugin: string]: Error} | null; -}> { - const newPluginState = {...pluginStates}; - let errorObject: {[plugin: string]: Error} | null = null; - - for (const { - pluginName, - pluginId, - pluginClass, - client, - pluginKey, - } of pluginsToProcess) { - const exportState = - pluginClass && !isSandyPlugin(pluginClass) - ? pluginClass.exportPersistedState - : null; - if (exportState) { - const fetchMetaDataMarker = `${EXPORT_FLIPPER_TRACE_EVENT}:fetch-meta-data-per-plugin`; - const isConnected = client.connected.get(); - performance.mark(fetchMetaDataMarker); - try { - statusUpdate && - statusUpdate(`Fetching metadata for plugin ${pluginName}...`); - const data = await promiseTimeout( - 240000, // Fetching MobileConfig data takes ~ 3 mins, thus keeping timeout at 4 mins. - exportState( - isConnected ? callClient(client, pluginId) : undefined, - newPluginState[pluginKey], - state, - idler, - statusUpdate, - isConnected - ? supportsMethod(client, pluginId) - : () => Promise.resolve(false), - ), - `Timed out while collecting data for ${pluginName}`, - ); - if (!data) { - throw new Error( - `Metadata returned by the ${pluginName} is undefined`, - ); - } - getLogger().trackTimeSince(fetchMetaDataMarker, fetchMetaDataMarker, { - pluginId, - }); - newPluginState[pluginKey] = data; - } catch (e) { - if (!errorObject) { - errorObject = {}; - } - errorObject[pluginName] = e; - getLogger().trackTimeSince(fetchMetaDataMarker, fetchMetaDataMarker, { - pluginId, - error: e, - }); - continue; - } - } - } - - return {pluginStates: newPluginState, errors: errorObject}; -} - async function processQueues( store: MiddlewareAPI, pluginsToProcess: PluginsToProcess, statusUpdate?: (msg: string) => void, idler?: Idler, ) { - for (const { - pluginName, - pluginId, - pluginKey, - pluginClass, - client, - } of pluginsToProcess) { - if (isSandyPlugin(pluginClass) || pluginClass.persistedStateReducer) { - client.flushMessageBuffer(); - const processQueueMarker = `${EXPORT_FLIPPER_TRACE_EVENT}:process-queue-per-plugin`; - performance.mark(processQueueMarker); - const plugin = isSandyPlugin(pluginClass) - ? client.sandyPluginStates.get(pluginId) - : pluginClass; - if (!plugin) continue; - await processMessageQueue( - plugin, - pluginKey, - store, - ({current, total}) => { - statusUpdate?.( - `Processing event ${current} / ${total} (${Math.round( - (current / total) * 100, - )}%) for plugin ${pluginName}`, - ); - }, - idler, - ); + for (const {pluginName, pluginId, pluginKey, client} of pluginsToProcess) { + client.flushMessageBuffer(); + const processQueueMarker = `${EXPORT_FLIPPER_TRACE_EVENT}:process-queue-per-plugin`; + performance.mark(processQueueMarker); + const plugin = client.sandyPluginStates.get(pluginId); + if (!plugin) continue; + await processMessageQueue( + plugin, + pluginKey, + store, + ({current, total}) => { + statusUpdate?.( + `Processing event ${current} / ${total} (${Math.round( + (current / total) * 100, + )}%) for plugin ${pluginName}`, + ); + }, + idler, + ); - getLogger().trackTimeSince(processQueueMarker, processQueueMarker, { - pluginId, - }); - } + getLogger().trackTimeSince(processQueueMarker, processQueueMarker, { + pluginId, + }); } } @@ -638,7 +402,6 @@ export function determinePluginsToProcess( client, pluginId: plugin, pluginName: getPluginTitle(pluginClass), - pluginClass, }); } } @@ -671,14 +434,6 @@ async function getStoreExport( performance.mark(fetchMetaDataMarker); const client = clients.find((client) => client.id === selectedApp); - const metadata = await fetchMetadata( - pluginsToProcess, - state.pluginStates, - state, - statusUpdate, - idler, - ); - const newPluginState = metadata.pluginStates; const pluginStates2 = pluginsToProcess ? await exportSandyPluginStates(pluginsToProcess, idler, statusUpdate) @@ -687,7 +442,6 @@ async function getStoreExport( getLogger().trackTimeSince(fetchMetaDataMarker, fetchMetaDataMarker, { plugins: state.plugins.selectedPlugins, }); - const {errors} = metadata; const {activeNotifications} = state.notifications; const {devicePlugins, clientPlugins} = state.plugins; @@ -695,7 +449,6 @@ async function getStoreExport( { activeNotifications, device: selectedDevice, - pluginStates: newPluginState, pluginStates2, clients: client ? [client.toJSON()] : [], devicePlugins, @@ -706,7 +459,7 @@ async function getStoreExport( }, idler, ); - return {exportData, fetchMetaDataErrors: errors}; + return {exportData, fetchMetaDataErrors: null}; } export async function exportStore( @@ -810,34 +563,9 @@ export function importDataToStore(source: string, data: string, store: Store) { payload: archivedDevice, }); - const {pluginStates} = json.store; - const processedPluginStates: PluginStatesState = deserializePluginStates( - pluginStates, - store.getState().plugins.clientPlugins, - store.getState().plugins.devicePlugins, - ); - const keys = Object.keys(processedPluginStates); - keys.forEach((key) => { - store.dispatch({ - type: 'SET_PLUGIN_STATE', - payload: { - pluginKey: key, - state: processedPluginStates[key], - }, - }); - }); - clients.forEach((client: {id: string; query: ClientQuery}) => { const sandyPluginStates = json.pluginStates2[client.id] || {}; - const clientPlugins: Set = new Set([ - ...keys - .filter((key) => { - const plugin = deconstructPluginKey(key); - return plugin.type === 'client' && client.id === plugin.client; - }) - .map((pluginKey) => deconstructPluginKey(pluginKey).pluginName), - ...Object.keys(sandyPluginStates), - ]); + const clientPlugins = new Set(Object.keys(sandyPluginStates)); store.dispatch({ type: 'NEW_CLIENT', payload: new Client( diff --git a/desktop/app/src/utils/messageQueue.tsx b/desktop/app/src/utils/messageQueue.tsx index dd857e3b0..c14569bd6 100644 --- a/desktop/app/src/utils/messageQueue.tsx +++ b/desktop/app/src/utils/messageQueue.tsx @@ -7,9 +7,8 @@ * @format */ -import {PersistedStateReducer, FlipperDevicePlugin} from '../plugin'; -import type {State, MiddlewareAPI} from '../reducers/index'; -import {setPluginState} from '../reducers/pluginStates'; +import {FlipperDevicePlugin} from '../plugin'; +import type {MiddlewareAPI} from '../reducers/index'; import { clearMessageQueue, queueMessages, @@ -23,31 +22,7 @@ import {defaultEnabledBackgroundPlugins} from './pluginUtils'; import {batch, Idler, _SandyPluginInstance} from 'flipper-plugin'; import {addBackgroundStat} from './pluginStats'; -function processMessageClassic( - state: State, - pluginKey: string, - plugin: { - id: string; - persistedStateReducer: PersistedStateReducer | null; - }, - message: Message, -): State { - const reducerStartTime = Date.now(); - try { - const newPluginState = plugin.persistedStateReducer!( - state, - message.method, - message.params, - ); - addBackgroundStat(plugin.id, Date.now() - reducerStartTime); - return newPluginState; - } catch (e) { - console.error(`Failed to process event for plugin ${plugin.id}`, e); - return state; - } -} - -function processMessagesSandy( +function processMessagesImmediately( plugin: _SandyPluginInstance, messages: Message[], ) { @@ -63,60 +38,20 @@ function processMessagesSandy( } } -export function processMessagesImmediately( - store: MiddlewareAPI, - pluginKey: string, - plugin: - | { - defaultPersistedState: any; - id: string; - persistedStateReducer: PersistedStateReducer | null; - } - | _SandyPluginInstance, - messages: Message[], -) { - if (plugin instanceof _SandyPluginInstance) { - processMessagesSandy(plugin, messages); - } else { - const persistedState = getCurrentPluginState(store, plugin, pluginKey); - const newPluginState = messages.reduce( - (state, message) => - processMessageClassic(state, pluginKey, plugin, message), - persistedState, - ); - if (persistedState !== newPluginState) { - store.dispatch( - setPluginState({ - pluginKey, - state: newPluginState, - }), - ); - } - } -} - export function processMessagesLater( store: MiddlewareAPI, pluginKey: string, - plugin: - | { - defaultPersistedState: any; - id: string; - persistedStateReducer: PersistedStateReducer | null; - maxQueueSize?: number; - } - | _SandyPluginInstance, + plugin: _SandyPluginInstance, messages: Message[], ) { - const pluginId = - plugin instanceof _SandyPluginInstance ? plugin.definition.id : plugin.id; + const pluginId = plugin.definition.id; const isSelected = pluginKey === getSelectedPluginKey(store.getState().connections); switch (true) { // Navigation events are always processed immediately, to make sure the navbar stays up to date, see also T69991064 case pluginId === 'Navigation': case isSelected && getPendingMessages(store, pluginKey).length === 0: - processMessagesImmediately(store, pluginKey, plugin, messages); + processMessagesImmediately(plugin, messages); break; case isSelected: case plugin instanceof _SandyPluginInstance: @@ -129,13 +64,7 @@ export function processMessagesLater( pluginId, ): store.dispatch( - queueMessages( - pluginKey, - messages, - plugin instanceof _SandyPluginInstance - ? DEFAULT_MAX_QUEUE_SIZE - : plugin.maxQueueSize, - ), + queueMessages(pluginKey, messages, DEFAULT_MAX_QUEUE_SIZE), ); break; default: @@ -148,21 +77,12 @@ export function processMessagesLater( } export async function processMessageQueue( - plugin: - | { - defaultPersistedState: any; - id: string; - persistedStateReducer: PersistedStateReducer | null; - } - | _SandyPluginInstance, + plugin: _SandyPluginInstance, pluginKey: string, store: MiddlewareAPI, progressCallback?: (progress: {current: number; total: number}) => void, idler: Idler = new IdlerImpl(), ): Promise { - if (!_SandyPluginInstance.is(plugin) && !plugin.persistedStateReducer) { - return true; - } const total = getPendingMessages(store, pluginKey).length; let progress = 0; do { @@ -171,25 +91,11 @@ export async function processMessageQueue( break; } // there are messages to process! lets do so until we have to idle - // persistedState is irrelevant for SandyPlugins, as they store state locally - const persistedState = _SandyPluginInstance.is(plugin) - ? undefined - : getCurrentPluginState(store, plugin, pluginKey); let offset = 0; - let newPluginState = persistedState; batch(() => { do { - if (_SandyPluginInstance.is(plugin)) { - // Optimization: we could send a batch of messages here - processMessagesSandy(plugin, [messages[offset]]); - } else { - newPluginState = processMessageClassic( - newPluginState, - pluginKey, - plugin, - messages[offset], - ); - } + // Optimization: we could send a batch of messages here + processMessagesImmediately(plugin, [messages[offset]]); offset++; progress++; @@ -203,17 +109,6 @@ export async function processMessageQueue( // resistent to kicking off this process twice; grabbing, processing messages, saving state is done synchronosly // until the idler has to break store.dispatch(clearMessageQueue(pluginKey, offset)); - if ( - !_SandyPluginInstance.is(plugin) && - newPluginState !== persistedState - ) { - store.dispatch( - setPluginState({ - pluginKey, - state: newPluginState, - }), - ); - } }); if (idler.isCancelled()) { @@ -232,15 +127,3 @@ function getPendingMessages( ): Message[] { return store.getState().pluginMessageQueue[pluginKey] || []; } - -function getCurrentPluginState( - store: MiddlewareAPI, - plugin: {defaultPersistedState: any}, - pluginKey: string, -) { - // possible optimization: don't spread default state here by put proper default state when initializing clients - return { - ...plugin.defaultPersistedState, - ...store.getState().pluginStates[pluginKey], - }; -} diff --git a/desktop/app/src/utils/pluginUtils.tsx b/desktop/app/src/utils/pluginUtils.tsx index fa28e2417..8e118aba8 100644 --- a/desktop/app/src/utils/pluginUtils.tsx +++ b/desktop/app/src/utils/pluginUtils.tsx @@ -7,17 +7,9 @@ * @format */ -import { - FlipperDevicePlugin, - FlipperBasePlugin, - PluginDefinition, - DevicePluginDefinition, - ClientPluginDefinition, -} from '../plugin'; +import {PluginDefinition} from '../plugin'; import type {State} from '../reducers'; -import type {State as PluginStatesState} from '../reducers/pluginStates'; import type {State as PluginsState} from '../reducers/plugins'; -import {_SandyPluginDefinition} from 'flipper-plugin'; import type BaseDevice from '../devices/BaseDevice'; import type Client from '../Client'; import type { @@ -29,9 +21,9 @@ import type { import {getLatestCompatibleVersionOfEachPlugin} from '../dispatcher/plugins'; export type PluginLists = { - devicePlugins: DevicePluginDefinition[]; - metroPlugins: DevicePluginDefinition[]; - enabledPlugins: ClientPluginDefinition[]; + devicePlugins: PluginDefinition[]; + metroPlugins: PluginDefinition[]; + enabledPlugins: PluginDefinition[]; disabledPlugins: PluginDefinition[]; unavailablePlugins: [plugin: PluginDetails, reason: string][]; downloadablePlugins: (DownloadablePluginDetails | BundledPluginDetails)[]; @@ -86,33 +78,12 @@ export function getPluginKey( return `unknown#${pluginID}`; } -export function isSandyPlugin( - plugin?: PluginDefinition | null, -): plugin is _SandyPluginDefinition { - return plugin instanceof _SandyPluginDefinition; -} - -export function getPersistedState( - pluginKey: string, - persistingPlugin: typeof FlipperBasePlugin | null, - pluginStates: PluginStatesState, -): PersistedState | null { - if (!persistingPlugin) { - return null; - } - - const persistedState: PersistedState = { - ...persistingPlugin.defaultPersistedState, - ...pluginStates[pluginKey], - }; - return persistedState; -} +export const pluginKey = (serial: string, pluginName: string): string => { + return `${serial}#${pluginName}`; +}; export function computeExportablePlugins( - state: Pick< - State, - 'plugins' | 'connections' | 'pluginStates' | 'pluginMessageQueue' - >, + state: Pick, device: BaseDevice | null, client: Client | null, availablePlugins: PluginLists, @@ -131,25 +102,14 @@ export function computeExportablePlugins( } function isExportablePlugin( - { - pluginStates, - pluginMessageQueue, - }: Pick, + {pluginMessageQueue}: Pick, device: BaseDevice | null, client: Client | null, plugin: PluginDefinition, ): boolean { - // can generate an export when requested - if (!isSandyPlugin(plugin) && plugin.exportPersistedState) { - return true; - } const pluginKey = isDevicePluginDefinition(plugin) ? getPluginKey(undefined, device, plugin.id) : getPluginKey(client?.id, undefined, plugin.id); - // plugin has exportable redux state - if (pluginStates[pluginKey]) { - return true; - } // plugin has exportable sandy state if (client?.sandyPluginStates.get(plugin.id)?.isPersistable()) { return true; @@ -158,10 +118,7 @@ function isExportablePlugin( return true; } // plugin has pending messages and a persisted state reducer or isSandy - if ( - pluginMessageQueue[pluginKey] && - ((plugin as any).defaultPersistedState || isSandyPlugin(plugin)) - ) { + if (pluginMessageQueue[pluginKey]) { return true; } // nothing to serialize @@ -201,11 +158,8 @@ export function isDevicePlugin(activePlugin: ActivePluginListItem) { export function isDevicePluginDefinition( definition: PluginDefinition, -): definition is DevicePluginDefinition { - return ( - (definition as any).prototype instanceof FlipperDevicePlugin || - (definition instanceof _SandyPluginDefinition && definition.isDevicePlugin) - ); +): boolean { + return definition.isDevicePlugin; } export function getPluginTooltip(details: PluginDetails): string { @@ -234,9 +188,9 @@ export function computePluginLists( metroDevice: BaseDevice | null, client: Client | null, ): { - devicePlugins: DevicePluginDefinition[]; - metroPlugins: DevicePluginDefinition[]; - enabledPlugins: ClientPluginDefinition[]; + devicePlugins: PluginDefinition[]; + metroPlugins: PluginDefinition[]; + enabledPlugins: PluginDefinition[]; disabledPlugins: PluginDefinition[]; unavailablePlugins: [plugin: PluginDetails, reason: string][]; downloadablePlugins: (DownloadablePluginDetails | BundledPluginDetails)[]; @@ -247,17 +201,13 @@ export function computePluginLists( ...plugins.bundledPlugins.values(), ...plugins.marketplacePlugins, ]).filter((p) => !plugins.loadedPlugins.has(p.id)); - const devicePlugins: DevicePluginDefinition[] = [ - ...plugins.devicePlugins.values(), - ] + const devicePlugins: PluginDefinition[] = [...plugins.devicePlugins.values()] .filter((p) => device?.supportsPlugin(p)) .filter((p) => enabledDevicePluginsState.has(p.id)); - const metroPlugins: DevicePluginDefinition[] = [ - ...plugins.devicePlugins.values(), - ] + const metroPlugins: PluginDefinition[] = [...plugins.devicePlugins.values()] .filter((p) => metroDevice?.supportsPlugin(p)) .filter((p) => enabledDevicePluginsState.has(p.id)); - const enabledPlugins: ClientPluginDefinition[] = []; + const enabledPlugins: PluginDefinition[] = []; const disabledPlugins: PluginDefinition[] = [ ...plugins.devicePlugins.values(), ] diff --git a/desktop/flipper-plugin/src/plugin/FlipperLib.tsx b/desktop/flipper-plugin/src/plugin/FlipperLib.tsx index 1712012cc..5ae8ab320 100644 --- a/desktop/flipper-plugin/src/plugin/FlipperLib.tsx +++ b/desktop/flipper-plugin/src/plugin/FlipperLib.tsx @@ -49,6 +49,6 @@ export function getFlipperLibImplementation(): FlipperLib { return flipperLibInstance; } -export function setFlipperLibImplementation(impl: FlipperLib) { +export function setFlipperLibImplementation(impl: FlipperLib | undefined) { flipperLibInstance = impl; } diff --git a/desktop/flipper-plugin/src/ui/elements-inspector/elements.tsx b/desktop/flipper-plugin/src/ui/elements-inspector/elements.tsx index 41ccc3e5c..839ccd872 100644 --- a/desktop/flipper-plugin/src/ui/elements-inspector/elements.tsx +++ b/desktop/flipper-plugin/src/ui/elements-inspector/elements.tsx @@ -14,7 +14,7 @@ import styled from '@emotion/styled'; import React, {MouseEvent, KeyboardEvent} from 'react'; import {theme} from '../theme'; import {Layout} from '../Layout'; -import {tryGetFlipperLibImplementation} from 'flipper-plugin/src/plugin/FlipperLib'; +import {_getFlipperLibImplementation} from 'flipper-plugin'; import {DownOutlined, RightOutlined} from '@ant-design/icons'; const {Text} = Typography; @@ -221,7 +221,7 @@ class ElementsRow extends PureComponent { { label: 'Copy', click: () => { - tryGetFlipperLibImplementation()?.writeTextToClipboard( + _getFlipperLibImplementation()?.writeTextToClipboard( props.onCopyExpandedTree(props.element, 0), ); }, @@ -229,7 +229,7 @@ class ElementsRow extends PureComponent { { label: 'Copy expanded child elements', click: () => - tryGetFlipperLibImplementation()?.writeTextToClipboard( + _getFlipperLibImplementation()?.writeTextToClipboard( props.onCopyExpandedTree(props.element, 255), ), }, @@ -253,7 +253,7 @@ class ElementsRow extends PureComponent { return { label: `Copy ${o.name}`, click: () => { - tryGetFlipperLibImplementation()?.writeTextToClipboard(o.value); + _getFlipperLibImplementation()?.writeTextToClipboard(o.value); }, }; }), @@ -555,7 +555,7 @@ export class Elements extends PureComponent { ) { e.stopPropagation(); e.preventDefault(); - tryGetFlipperLibImplementation()?.writeTextToClipboard( + _getFlipperLibImplementation()?.writeTextToClipboard( selectedElement.name, ); return;