diff --git a/desktop/app/src/chrome/mainsidebar/MainSidebar2.tsx b/desktop/app/src/chrome/mainsidebar/MainSidebar2.tsx index c251be0b2..43b1d0e59 100644 --- a/desktop/app/src/chrome/mainsidebar/MainSidebar2.tsx +++ b/desktop/app/src/chrome/mainsidebar/MainSidebar2.tsx @@ -242,9 +242,10 @@ class MainSidebar2 extends PureComponent { selectedDevice, } = this.props; const clients = getAvailableClients(device, this.props.clients); - const devicePluginsItems = device.devicePlugins.map((pluginName) => { - const plugin = this.props.devicePlugins.get(pluginName)!; - return ( + const devicePluginsItems = device.devicePlugins + .map((pluginName) => this.props.devicePlugins.get(pluginName)!) + .sort(sortPluginsByName) + .map((plugin) => ( { } plugin={plugin} /> - ); - }); + )); const wrapDevicePlugins = clients.length > 0 && device.devicePlugins.length > 1 && !device.source; diff --git a/desktop/app/src/devices/BaseDevice.tsx b/desktop/app/src/devices/BaseDevice.tsx index 418996a9c..f8e4290e1 100644 --- a/desktop/app/src/devices/BaseDevice.tsx +++ b/desktop/app/src/devices/BaseDevice.tsx @@ -9,14 +9,13 @@ import stream from 'stream'; import type {DeviceLogListener} from 'flipper'; -import {sortPluginsByName} from '../utils/pluginUtils'; import { DeviceLogEntry, SandyDevicePluginInstance, SandyPluginDefinition, DeviceType, } from 'flipper-plugin'; -import type {DevicePluginMap, FlipperDevicePlugin} from '../plugin'; +import type {DevicePluginDefinition, DevicePluginMap} from '../plugin'; import {getFlipperLibImplementation} from '../utils/flipperLibImplementation'; export type DeviceShell = { @@ -170,13 +169,12 @@ export default class BaseDevice { return; } const plugins = Array.from(devicePlugins.values()); - plugins.sort(sortPluginsByName); for (const plugin of plugins) { this.loadDevicePlugin(plugin); } } - loadDevicePlugin(plugin: typeof FlipperDevicePlugin | SandyPluginDefinition) { + loadDevicePlugin(plugin: DevicePluginDefinition) { if (plugin instanceof SandyPluginDefinition) { if (plugin.asDevicePluginModule().supportsDevice(this as any)) { this.devicePlugins.push(plugin.id); @@ -195,4 +193,13 @@ export default class BaseDevice { } } } + + unloadDevicePlugin(pluginId: string) { + const instance = this.sandyPluginStates.get(pluginId); + if (instance) { + instance.destroy(); + this.sandyPluginStates.delete(pluginId); + } + this.devicePlugins.splice(this.devicePlugins.indexOf(pluginId), 1); + } } diff --git a/desktop/app/src/store.tsx b/desktop/app/src/store.tsx index 63ec8a9c6..e2e5f36fd 100644 --- a/desktop/app/src/store.tsx +++ b/desktop/app/src/store.tsx @@ -18,8 +18,15 @@ import { isDevicePluginDefinition, } from './utils/pluginUtils'; import Client from './Client'; -import {PluginDefinition} from './plugin'; +import { + DevicePluginDefinition, + FlipperPlugin, + PluginDefinition, +} from './plugin'; import {deconstructPluginKey} from './utils/clientUtils'; +import {SandyPluginDefinition} from 'flipper-plugin'; +import BaseDevice from './devices/BaseDevice'; +import {State as PluginStates} from './reducers/pluginStates'; export const store: Store = createStore( rootReducer, @@ -69,42 +76,12 @@ export function rootReducer( } }); } else if (action.type === 'UPDATE_PLUGIN' && state) { - const plugin: PluginDefinition = action.payload; - const clients = state.connections.clients; - return produce(state, (draft) => { - const clientsWithEnabledPlugin = clients.filter((c) => { - return ( - c.supportsPlugin(plugin.id) && - state.connections.userStarredPlugins[c.query.app]?.includes(plugin.id) - ); - }); - // stop plugin for each client where it is enabled - clientsWithEnabledPlugin.forEach((client) => { - stopPlugin(client, plugin.id, true); - delete draft.pluginMessageQueue[ - getPluginKey(client.id, {serial: client.query.device_id}, plugin.id) - ]; - }); - // cleanup classic plugin state - Object.keys(draft.pluginStates).forEach((pluginKey) => { - const pluginKeyParts = deconstructPluginKey(pluginKey); - if (pluginKeyParts.pluginName === plugin.id) { - delete draft.pluginStates[pluginKey]; - } - }); - // update plugin definition - const {devicePlugins, clientPlugins} = draft.plugins; - const p = action.payload; - if (isDevicePluginDefinition(p)) { - devicePlugins.set(p.id, p); - } else { - clientPlugins.set(p.id, p); - } - // start plugin for each client - clientsWithEnabledPlugin.forEach((client) => { - startPlugin(client, plugin, true); - }); - }); + const plugin = action.payload; + if (isDevicePluginDefinition(plugin)) { + return updateDevicePlugin(state, plugin); + } else { + return updateClientPlugin(state, plugin); + } } // otherwise @@ -149,3 +126,66 @@ function startPlugin( client.initPlugin(plugin.id); } } + +function updateClientPlugin(state: StoreState, plugin: typeof FlipperPlugin) { + const clients = state.connections.clients; + return produce(state, (draft) => { + const clientsWithEnabledPlugin = clients.filter((c) => { + return ( + c.supportsPlugin(plugin.id) && + state.connections.userStarredPlugins[c.query.app]?.includes(plugin.id) + ); + }); + // stop plugin for each client where it is enabled + clientsWithEnabledPlugin.forEach((client) => { + stopPlugin(client, plugin.id, true); + delete draft.pluginMessageQueue[ + getPluginKey(client.id, {serial: client.query.device_id}, plugin.id) + ]; + }); + cleanupPluginStates(draft.pluginStates, plugin.id); + // update plugin definition + draft.plugins.clientPlugins.set(plugin.id, plugin); + // start plugin for each client + clientsWithEnabledPlugin.forEach((client) => { + startPlugin(client, plugin, true); + }); + }); +} + +function updateDevicePlugin(state: StoreState, plugin: DevicePluginDefinition) { + const devices = state.connections.devices; + return produce(state, (draft) => { + const devicesWithEnabledPlugin = devices.filter((d) => + supportsDevice(plugin, d), + ); + devicesWithEnabledPlugin.forEach((d) => { + d.unloadDevicePlugin(plugin.id); + }); + cleanupPluginStates(draft.pluginStates, plugin.id); + draft.plugins.devicePlugins.set(plugin.id, plugin); + devicesWithEnabledPlugin.forEach((d) => { + d.loadDevicePlugin(plugin); + }); + }); +} + +function supportsDevice(plugin: DevicePluginDefinition, device: BaseDevice) { + if (plugin instanceof SandyPluginDefinition) { + return ( + plugin.isDevicePlugin && + plugin.asDevicePluginModule().supportsDevice(device as any) + ); + } else { + return plugin.supportsDevice(device); + } +} + +function cleanupPluginStates(pluginStates: PluginStates, pluginId: string) { + Object.keys(pluginStates).forEach((pluginKey) => { + const pluginKeyParts = deconstructPluginKey(pluginKey); + if (pluginKeyParts.pluginName === pluginId) { + delete pluginStates[pluginKey]; + } + }); +}