Reload single device plugin on auto-update

Summary: The same as D23729972 (0982dc06a0), but now it is also possible to reload device plugins independently.

Reviewed By: jknoxville

Differential Revision: D23960058

fbshipit-source-id: 01e0edb29a62ed173dfe6f5946466269adee453a
This commit is contained in:
Anton Nikolaev
2020-09-28 04:45:51 -07:00
committed by Facebook GitHub Bot
parent 1b4c08b282
commit 80c7c76ef9
3 changed files with 93 additions and 46 deletions

View File

@@ -242,9 +242,10 @@ class MainSidebar2 extends PureComponent<Props, State> {
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) => (
<PluginSidebarListItem
key={plugin.id}
isActive={plugin.id === selectedPlugin && selectedDevice === device}
@@ -258,8 +259,7 @@ class MainSidebar2 extends PureComponent<Props, State> {
}
plugin={plugin}
/>
);
});
));
const wrapDevicePlugins =
clients.length > 0 && device.devicePlugins.length > 1 && !device.source;

View File

@@ -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);
}
}

View File

@@ -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<StoreState, Actions, any, any>(
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];
}
});
}