Device plugin management (2/n): enable/disable, install/uninstall
Summary: *Stack summary*: this stack adds ability to manage device plugins in the same way as client plugins: install, update, uninstall, enable (star) and disable (unstar) them. *Diff summary*: implemented all plugin management actions for device plugins. Changelog: it is now possible to enable/disable and install/uninstall device plugins Reviewed By: mweststrate Differential Revision: D26337377 fbshipit-source-id: 7d1ed61a8dc5f3339e5e548c613b67bca0c27f4f
This commit is contained in:
committed by
Facebook GitHub Bot
parent
899fcd0783
commit
4541cdc23b
@@ -48,7 +48,6 @@ import {processMessageQueue} from './utils/messageQueue';
|
||||
import {ToggleButton, SmallText, Layout} from './ui';
|
||||
import {theme, TrackingScope, _SandyPluginRenderer} from 'flipper-plugin';
|
||||
import {isDevicePluginDefinition} from './utils/pluginUtils';
|
||||
import ArchivedDevice from './devices/ArchivedDevice';
|
||||
import {ContentContainer} from './sandy-chrome/ContentContainer';
|
||||
import {Alert, Typography} from 'antd';
|
||||
import {InstalledPluginDetails} from 'plugin-lib';
|
||||
@@ -318,7 +317,7 @@ class PluginContainer extends PureComponent<Props, State> {
|
||||
onClick={() => {
|
||||
this.props.starPlugin({
|
||||
plugin: activePlugin,
|
||||
selectedApp: (this.props.target as Client).query.app,
|
||||
selectedApp: (this.props.target as Client)?.query?.app,
|
||||
});
|
||||
}}
|
||||
large
|
||||
@@ -554,6 +553,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||
clients,
|
||||
deepLinkPayload,
|
||||
userStarredPlugins,
|
||||
userStarredDevicePlugins,
|
||||
},
|
||||
pluginStates,
|
||||
plugins: {devicePlugins, clientPlugins, installedPlugins},
|
||||
@@ -567,23 +567,25 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||
|
||||
if (selectedPlugin) {
|
||||
activePlugin = devicePlugins.get(selectedPlugin);
|
||||
target = selectedDevice;
|
||||
if (selectedDevice && activePlugin) {
|
||||
target = selectedDevice;
|
||||
pluginKey = getPluginKey(selectedDevice.serial, activePlugin.id);
|
||||
pluginIsEnabled = true;
|
||||
} else {
|
||||
target =
|
||||
clients.find((client: Client) => client.id === selectedApp) || null;
|
||||
activePlugin = clientPlugins.get(selectedPlugin);
|
||||
if (activePlugin && target) {
|
||||
pluginKey = getPluginKey(target.id, activePlugin.id);
|
||||
pluginIsEnabled = pluginIsStarred(
|
||||
userStarredPlugins,
|
||||
selectedApp,
|
||||
activePlugin.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
pluginIsEnabled =
|
||||
activePlugin !== undefined &&
|
||||
pluginIsStarred(
|
||||
userStarredPlugins,
|
||||
userStarredDevicePlugins,
|
||||
selectedApp,
|
||||
activePlugin.id,
|
||||
);
|
||||
}
|
||||
const isArchivedDevice = !selectedDevice
|
||||
? false
|
||||
|
||||
@@ -961,7 +961,8 @@ test('Sandy plugins support isPluginSupported + selectPlugin', async () => {
|
||||
expect(pluginInstance.deactivatedStub).toBeCalledTimes(0);
|
||||
expect(linksSeen).toEqual([]);
|
||||
|
||||
// open a device plugin
|
||||
// star and navigate to a device plugin
|
||||
store.dispatch(starPlugin({plugin: definition3}));
|
||||
pluginInstance.selectPlugin(definition3.id);
|
||||
expect(store.getState().connections.selectedPlugin).toBe(definition3.id);
|
||||
expect(renderer.baseElement.querySelector('h1')).toMatchInlineSnapshot(`
|
||||
|
||||
@@ -37,6 +37,7 @@ Object {
|
||||
"userPreferredApp": "TestApp#Android#MockAndroidDevice#serial",
|
||||
"userPreferredDevice": "MockAndroidDevice",
|
||||
"userPreferredPlugin": "TestPlugin",
|
||||
"userStarredDevicePlugins": Set {},
|
||||
"userStarredPlugins": Object {
|
||||
"TestApp": Array [
|
||||
"TestPlugin",
|
||||
|
||||
@@ -248,7 +248,10 @@ export default class BaseDevice {
|
||||
instance.destroy();
|
||||
this.sandyPluginStates.delete(pluginId);
|
||||
}
|
||||
this.devicePlugins.splice(this.devicePlugins.indexOf(pluginId), 1);
|
||||
const index = this.devicePlugins.indexOf(pluginId);
|
||||
if (index >= 0) {
|
||||
this.devicePlugins.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
|
||||
@@ -21,6 +21,8 @@ import * as TestPlugin from '../../test-utils/TestPlugin';
|
||||
import {_SandyPluginDefinition as SandyPluginDefinition} from 'flipper-plugin';
|
||||
import MockFlipper from '../../test-utils/MockFlipper';
|
||||
import Client from '../../Client';
|
||||
import React from 'react';
|
||||
import BaseDevice from '../../devices/BaseDevice';
|
||||
|
||||
const pluginDetails1 = TestUtils.createMockPluginDetails({
|
||||
id: 'plugin1',
|
||||
@@ -45,10 +47,27 @@ const pluginDetails2 = TestUtils.createMockPluginDetails({
|
||||
});
|
||||
const pluginDefinition2 = new SandyPluginDefinition(pluginDetails2, TestPlugin);
|
||||
|
||||
const devicePluginDetails = TestUtils.createMockPluginDetails({
|
||||
id: 'device',
|
||||
name: 'flipper-device',
|
||||
});
|
||||
const devicePluginDefinition = new SandyPluginDefinition(devicePluginDetails, {
|
||||
supportsDevice() {
|
||||
return true;
|
||||
},
|
||||
devicePlugin() {
|
||||
return {};
|
||||
},
|
||||
Component() {
|
||||
return <h1>Plugin3</h1>;
|
||||
},
|
||||
});
|
||||
|
||||
const mockedRequirePlugin = mocked(requirePlugin);
|
||||
|
||||
let mockFlipper: MockFlipper;
|
||||
let mockClient: Client;
|
||||
let mockDevice: BaseDevice;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockedRequirePlugin.mockImplementation(
|
||||
@@ -59,6 +78,8 @@ beforeEach(async () => {
|
||||
? pluginDefinition2
|
||||
: details === pluginDetails1V2
|
||||
? pluginDefinition1V2
|
||||
: details === devicePluginDetails
|
||||
? devicePluginDefinition
|
||||
: undefined)!,
|
||||
);
|
||||
mockFlipper = new MockFlipper();
|
||||
@@ -66,6 +87,7 @@ beforeEach(async () => {
|
||||
clientOptions: {supportedPlugins: ['plugin1', 'plugin2']},
|
||||
});
|
||||
mockClient = initResult.client;
|
||||
mockDevice = initResult.device;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -199,3 +221,46 @@ test('unstar plugin', async () => {
|
||||
).not.toContain('plugin1');
|
||||
expect(mockClient.sandyPluginStates.has('plugin1')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('star device plugin', async () => {
|
||||
mockFlipper.dispatch(
|
||||
loadPlugin({
|
||||
plugin: devicePluginDetails,
|
||||
enable: false,
|
||||
notifyIfFailed: false,
|
||||
}),
|
||||
);
|
||||
mockFlipper.dispatch(
|
||||
starPlugin({
|
||||
plugin: devicePluginDefinition,
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
mockFlipper.getState().connections.userStarredDevicePlugins.has('device'),
|
||||
).toBeTruthy();
|
||||
expect(mockDevice.sandyPluginStates.has('device')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('unstar device plugin', async () => {
|
||||
mockFlipper.dispatch(
|
||||
loadPlugin({
|
||||
plugin: devicePluginDetails,
|
||||
enable: false,
|
||||
notifyIfFailed: false,
|
||||
}),
|
||||
);
|
||||
mockFlipper.dispatch(
|
||||
starPlugin({
|
||||
plugin: devicePluginDefinition,
|
||||
}),
|
||||
);
|
||||
mockFlipper.dispatch(
|
||||
starPlugin({
|
||||
plugin: devicePluginDefinition,
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
mockFlipper.getState().connections.userStarredDevicePlugins.has('device'),
|
||||
).toBeFalsy();
|
||||
expect(mockDevice.sandyPluginStates.has('device')).toBeFalsy();
|
||||
});
|
||||
|
||||
@@ -12,11 +12,11 @@ import {clearPluginState} from '../reducers/pluginStates';
|
||||
import type {Logger} from '../fb-interfaces/Logger';
|
||||
import {
|
||||
LoadPluginActionPayload,
|
||||
PluginCommand,
|
||||
UninstallPluginActionPayload,
|
||||
UpdatePluginActionPayload,
|
||||
pluginCommandsProcessed,
|
||||
StarPluginActionPayload,
|
||||
PluginCommand,
|
||||
} from '../reducers/pluginManager';
|
||||
import {
|
||||
getInstalledPlugins,
|
||||
@@ -28,8 +28,8 @@ import {sideEffect} from '../utils/sideEffect';
|
||||
import {requirePlugin} from './plugins';
|
||||
import {showErrorNotification} from '../utils/notifications';
|
||||
import {
|
||||
ClientPluginDefinition,
|
||||
DevicePluginDefinition,
|
||||
FlipperDevicePlugin,
|
||||
FlipperPlugin,
|
||||
PluginDefinition,
|
||||
} from '../plugin';
|
||||
@@ -41,11 +41,17 @@ import {
|
||||
registerInstalledPlugins,
|
||||
} from '../reducers/plugins';
|
||||
import {_SandyPluginDefinition} from 'flipper-plugin';
|
||||
import {pluginStarred, pluginUnstarred} from '../reducers/connections';
|
||||
import {
|
||||
devicePluginStarred,
|
||||
devicePluginUnstarred,
|
||||
pluginStarred,
|
||||
pluginUnstarred,
|
||||
} from '../reducers/connections';
|
||||
import {deconstructClientId} from '../utils/clientUtils';
|
||||
import {clearMessageQueue} from '../reducers/pluginMessageQueue';
|
||||
import {
|
||||
getPluginKey,
|
||||
isDevicePluginDefinition,
|
||||
defaultEnabledBackgroundPlugins,
|
||||
} from '../utils/pluginUtils';
|
||||
|
||||
@@ -161,7 +167,7 @@ function uninstallPlugin(store: Store, {plugin}: UninstallPluginActionPayload) {
|
||||
function updatePlugin(store: Store, payload: UpdatePluginActionPayload) {
|
||||
const {plugin, enablePlugin} = payload;
|
||||
if (isDevicePluginDefinition(plugin)) {
|
||||
return updateDevicePlugin(store, plugin);
|
||||
return updateDevicePlugin(store, plugin, enablePlugin);
|
||||
} else {
|
||||
return updateClientPlugin(store, plugin, enablePlugin);
|
||||
}
|
||||
@@ -175,8 +181,26 @@ function getSelectedAppId(store: Store) {
|
||||
return selectedApp;
|
||||
}
|
||||
|
||||
function starPlugin(store: Store, payload: StarPluginActionPayload) {
|
||||
const {plugin, selectedApp} = payload;
|
||||
function starPlugin(
|
||||
store: Store,
|
||||
{plugin, selectedApp}: StarPluginActionPayload,
|
||||
) {
|
||||
if (isDevicePluginDefinition(plugin)) {
|
||||
starDevicePlugin(store, plugin);
|
||||
} else {
|
||||
starClientPlugin(store, plugin, selectedApp);
|
||||
}
|
||||
}
|
||||
|
||||
function starClientPlugin(
|
||||
store: Store,
|
||||
plugin: ClientPluginDefinition,
|
||||
selectedApp: string | undefined,
|
||||
) {
|
||||
selectedApp = selectedApp ?? getSelectedAppId(store);
|
||||
if (!selectedApp) {
|
||||
return;
|
||||
}
|
||||
const {connections} = store.getState();
|
||||
const clients = connections.clients.filter(
|
||||
(client) => client.query.app === selectedApp,
|
||||
@@ -200,6 +224,24 @@ function starPlugin(store: Store, payload: StarPluginActionPayload) {
|
||||
}
|
||||
}
|
||||
|
||||
function starDevicePlugin(store: Store, plugin: DevicePluginDefinition) {
|
||||
const {connections} = store.getState();
|
||||
const devicesWithPlugin = connections.devices.filter((d) =>
|
||||
d.supportsPlugin(plugin.details),
|
||||
);
|
||||
if (connections.userStarredDevicePlugins.has(plugin.id)) {
|
||||
devicesWithPlugin.forEach((d) => {
|
||||
d.unloadDevicePlugin(plugin.id);
|
||||
});
|
||||
store.dispatch(devicePluginUnstarred(plugin));
|
||||
} else {
|
||||
devicesWithPlugin.forEach((d) => {
|
||||
d.loadDevicePlugin(plugin);
|
||||
});
|
||||
store.dispatch(devicePluginStarred(plugin));
|
||||
}
|
||||
}
|
||||
|
||||
function updateClientPlugin(
|
||||
store: Store,
|
||||
plugin: typeof FlipperPlugin,
|
||||
@@ -235,9 +277,16 @@ function updateClientPlugin(
|
||||
}
|
||||
}
|
||||
|
||||
function updateDevicePlugin(store: Store, plugin: DevicePluginDefinition) {
|
||||
const devices = store.getState().connections.devices;
|
||||
const devicesWithEnabledPlugin = devices.filter((d) =>
|
||||
function updateDevicePlugin(
|
||||
store: Store,
|
||||
plugin: DevicePluginDefinition,
|
||||
enable: boolean,
|
||||
) {
|
||||
if (enable) {
|
||||
store.dispatch(devicePluginStarred(plugin));
|
||||
}
|
||||
const connections = store.getState().connections;
|
||||
const devicesWithEnabledPlugin = connections.devices.filter((d) =>
|
||||
d.supportsPlugin(plugin),
|
||||
);
|
||||
devicesWithEnabledPlugin.forEach((d) => {
|
||||
@@ -295,12 +344,3 @@ function unloadPluginModule(plugin: ActivatablePluginDetails) {
|
||||
}
|
||||
unloadModule(plugin.entry);
|
||||
}
|
||||
|
||||
export function isDevicePluginDefinition(
|
||||
definition: PluginDefinition,
|
||||
): definition is DevicePluginDefinition {
|
||||
return (
|
||||
(definition as any).prototype instanceof FlipperDevicePlugin ||
|
||||
(definition instanceof _SandyPluginDefinition && definition.isDevicePlugin)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ export type State = {
|
||||
userPreferredPlugin: null | string;
|
||||
userPreferredApp: null | string;
|
||||
userStarredPlugins: {[client: string]: string[]};
|
||||
userStarredDevicePlugins: Set<string>;
|
||||
clients: Array<Client>;
|
||||
uninitializedClients: Array<{
|
||||
client: UninitializedClient;
|
||||
@@ -115,6 +116,12 @@ export type Action =
|
||||
selectedApp: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: 'DEVICE_PLUGIN_STARRED';
|
||||
payload: {
|
||||
plugin: PluginDefinition;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: 'PLUGIN_UNSTARRED';
|
||||
payload: {
|
||||
@@ -122,6 +129,12 @@ export type Action =
|
||||
selectedApp: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: 'DEVICE_PLUGIN_UNSTARRED';
|
||||
payload: {
|
||||
plugin: PluginDefinition;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: 'SELECT_CLIENT';
|
||||
payload: string | null;
|
||||
@@ -140,6 +153,7 @@ const INITAL_STATE: State = {
|
||||
userPreferredPlugin: null,
|
||||
userPreferredApp: null,
|
||||
userStarredPlugins: {},
|
||||
userStarredDevicePlugins: new Set(),
|
||||
clients: [],
|
||||
uninitializedClients: [],
|
||||
deepLinkPayload: null,
|
||||
@@ -379,6 +393,12 @@ export default (state: State = INITAL_STATE, action: Actions): State => {
|
||||
}
|
||||
});
|
||||
}
|
||||
case 'DEVICE_PLUGIN_STARRED': {
|
||||
const {plugin} = action.payload;
|
||||
return produce(state, (draft) => {
|
||||
draft.userStarredDevicePlugins.add(plugin.id);
|
||||
});
|
||||
}
|
||||
case 'PLUGIN_UNSTARRED': {
|
||||
const {plugin, selectedApp} = action.payload;
|
||||
const selectedPlugin = plugin.id;
|
||||
@@ -393,6 +413,12 @@ export default (state: State = INITAL_STATE, action: Actions): State => {
|
||||
}
|
||||
});
|
||||
}
|
||||
case 'DEVICE_PLUGIN_UNSTARRED': {
|
||||
const {plugin} = action.payload;
|
||||
return produce(state, (draft) => {
|
||||
draft.userStarredDevicePlugins.delete(plugin.id);
|
||||
});
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
@@ -449,6 +475,20 @@ export const pluginStarred = (
|
||||
},
|
||||
});
|
||||
|
||||
export const devicePluginStarred = (plugin: PluginDefinition): Action => ({
|
||||
type: 'DEVICE_PLUGIN_STARRED',
|
||||
payload: {
|
||||
plugin,
|
||||
},
|
||||
});
|
||||
|
||||
export const devicePluginUnstarred = (plugin: PluginDefinition): Action => ({
|
||||
type: 'DEVICE_PLUGIN_UNSTARRED',
|
||||
payload: {
|
||||
plugin,
|
||||
},
|
||||
});
|
||||
|
||||
export const pluginUnstarred = (
|
||||
plugin: PluginDefinition,
|
||||
appId: string,
|
||||
@@ -583,9 +623,13 @@ export function getSelectedPluginKey(state: State): string | undefined {
|
||||
|
||||
export function pluginIsStarred(
|
||||
userStarredPlugins: State['userStarredPlugins'],
|
||||
userStarredDevicePlugins: State['userStarredDevicePlugins'],
|
||||
app: string | null,
|
||||
pluginId: string,
|
||||
): boolean {
|
||||
if (userStarredDevicePlugins.has(pluginId)) {
|
||||
return true;
|
||||
}
|
||||
if (!app) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -141,7 +141,9 @@ export default combineReducers<State, Actions>({
|
||||
'userPreferredPlugin',
|
||||
'userPreferredApp',
|
||||
'userStarredPlugins',
|
||||
'userStarredDevicePlugins',
|
||||
],
|
||||
transforms: [setTransformer({whitelist: ['userStarredDevicePlugins']})],
|
||||
},
|
||||
connections,
|
||||
),
|
||||
|
||||
@@ -54,7 +54,7 @@ export type UpdatePluginAction = {
|
||||
|
||||
export type StarPluginActionPayload = {
|
||||
plugin: PluginDefinition;
|
||||
selectedApp: string;
|
||||
selectedApp?: string;
|
||||
};
|
||||
|
||||
export type StarPluginAction = {
|
||||
|
||||
@@ -87,6 +87,7 @@ export const PluginList = memo(function PluginList({
|
||||
client,
|
||||
plugins,
|
||||
connections.userStarredPlugins,
|
||||
connections.userStarredDevicePlugins,
|
||||
pluginsChanged,
|
||||
]);
|
||||
const isConnected = useValue(activeDevice?.connected, false);
|
||||
@@ -143,14 +144,16 @@ export const PluginList = memo(function PluginList({
|
||||
);
|
||||
const handleStarPlugin = useCallback(
|
||||
(id: string) => {
|
||||
const plugin = (plugins.clientPlugins.get(id) ??
|
||||
plugins.devicePlugins.get(id))!;
|
||||
dispatch(
|
||||
starPlugin({
|
||||
selectedApp: client!.query.app,
|
||||
plugin: plugins.clientPlugins.get(id)!,
|
||||
selectedApp: client?.query.app,
|
||||
plugin,
|
||||
}),
|
||||
);
|
||||
},
|
||||
[client, plugins.clientPlugins, dispatch],
|
||||
[client, plugins.clientPlugins, plugins.devicePlugins, dispatch],
|
||||
);
|
||||
const handleInstallPlugin = useCallback(
|
||||
(id: string) => {
|
||||
@@ -200,6 +203,18 @@ export const PluginList = memo(function PluginList({
|
||||
}
|
||||
onClick={handleAppPluginClick}
|
||||
tooltip={getPluginTooltip(plugin.details)}
|
||||
actions={
|
||||
isArchived ? null : (
|
||||
<ActionButton
|
||||
id={plugin.id}
|
||||
onClick={handleStarPlugin}
|
||||
title="Disable plugin"
|
||||
icon={
|
||||
<MinusOutlined size={16} style={{marginRight: 0}} />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</PluginGroup>
|
||||
|
||||
@@ -16,7 +16,7 @@ import {FlipperPlugin} from '../../../plugin';
|
||||
import MetroDevice from '../../../devices/MetroDevice';
|
||||
import BaseDevice from '../../../devices/BaseDevice';
|
||||
import {_SandyPluginDefinition} from 'flipper-plugin';
|
||||
import {createMockPluginDetails} from 'flipper-plugin/src/test-utils/test-utils';
|
||||
import {TestUtils} from 'flipper-plugin';
|
||||
import {selectPlugin} from '../../../reducers/connections';
|
||||
import {registerMetroDevice} from '../../../dispatcher/metroDevice';
|
||||
import {
|
||||
@@ -31,6 +31,8 @@ import * as LogsPluginModule from '../../../../../plugins/logs/index';
|
||||
import {createMockDownloadablePluginDetails} from '../../../utils/testUtils';
|
||||
import {computePluginLists} from '../../../utils/pluginUtils';
|
||||
|
||||
const createMockPluginDetails = TestUtils.createMockPluginDetails;
|
||||
|
||||
const logsPlugin = new _SandyPluginDefinition(
|
||||
createMockPluginDetails({id: 'DeviceLogs'}),
|
||||
LogsPluginModule,
|
||||
@@ -173,6 +175,7 @@ describe('basic findBestDevice with metro present', () => {
|
||||
flipper.client,
|
||||
state.plugins,
|
||||
state.connections.userStarredPlugins,
|
||||
state.connections.userStarredDevicePlugins,
|
||||
),
|
||||
).toEqual({
|
||||
downloadablePlugins: [],
|
||||
@@ -284,6 +287,7 @@ describe('basic findBestDevice with metro present', () => {
|
||||
flipper.client,
|
||||
state.plugins,
|
||||
state.connections.userStarredPlugins,
|
||||
state.connections.userStarredDevicePlugins,
|
||||
);
|
||||
expect(pluginLists).toEqual({
|
||||
devicePlugins: [logsPlugin],
|
||||
@@ -297,15 +301,15 @@ describe('basic findBestDevice with metro present', () => {
|
||||
],
|
||||
[
|
||||
unsupportedDevicePlugin.details,
|
||||
"Device plugin 'Unsupported Device Plugin' is not supported by the current device type.",
|
||||
"Device plugin 'Unsupported Device Plugin' is not supported by the currently connected device.",
|
||||
],
|
||||
[
|
||||
unsupportedPlugin.details,
|
||||
"Plugin 'Unsupported Plugin' is installed in Flipper, but not supported by the client application",
|
||||
"Plugin 'Unsupported Plugin' is not supported by the client application",
|
||||
],
|
||||
[
|
||||
unsupportedDownloadablePlugin,
|
||||
"Plugin 'Unsupported Uninstalled Plugin' is not installed in Flipper and not supported by the client application",
|
||||
"Plugin 'Unsupported Uninstalled Plugin' is not supported by the client application and not installed in Flipper",
|
||||
],
|
||||
],
|
||||
downloadablePlugins: [supportedDownloadablePlugin],
|
||||
@@ -325,6 +329,7 @@ describe('basic findBestDevice with metro present', () => {
|
||||
flipper.client,
|
||||
state.plugins,
|
||||
state.connections.userStarredPlugins,
|
||||
state.connections.userStarredDevicePlugins,
|
||||
),
|
||||
).toMatchObject({
|
||||
enabledPlugins: [plugin2],
|
||||
|
||||
@@ -62,6 +62,23 @@ type MockOptions = Partial<{
|
||||
supportedPlugins?: string[];
|
||||
}>;
|
||||
|
||||
function isPluginEnabled(
|
||||
store: Store,
|
||||
pluginClazz: PluginDefinition,
|
||||
selectedApp: string,
|
||||
) {
|
||||
return (
|
||||
(!isDevicePluginDefinition(pluginClazz) &&
|
||||
store
|
||||
.getState()
|
||||
.connections.userStarredPlugins[selectedApp]?.includes(
|
||||
pluginClazz.id,
|
||||
)) ||
|
||||
(isDevicePluginDefinition(pluginClazz) &&
|
||||
store.getState().connections.userStarredDevicePlugins.has(pluginClazz.id))
|
||||
);
|
||||
}
|
||||
|
||||
export async function createMockFlipperWithPlugin(
|
||||
pluginClazz: PluginDefinition,
|
||||
options?: MockOptions,
|
||||
@@ -89,14 +106,7 @@ export async function createMockFlipperWithPlugin(
|
||||
backgroundPlugins: options?.asBackgroundPlugin ? [pluginClazz.id] : [],
|
||||
});
|
||||
// enable the plugin
|
||||
if (
|
||||
!isDevicePluginDefinition(pluginClazz) &&
|
||||
!store
|
||||
.getState()
|
||||
.connections.userStarredPlugins[client.query.app]?.includes(
|
||||
pluginClazz.id,
|
||||
)
|
||||
) {
|
||||
if (!isPluginEnabled(store, pluginClazz, name)) {
|
||||
store.dispatch(
|
||||
starPlugin({
|
||||
plugin: pluginClazz,
|
||||
@@ -106,7 +116,7 @@ export async function createMockFlipperWithPlugin(
|
||||
}
|
||||
if (!options?.dontEnableAdditionalPlugins) {
|
||||
options?.additionalPlugins?.forEach((plugin) => {
|
||||
if (!isDevicePluginDefinition(plugin)) {
|
||||
if (!isPluginEnabled(store, plugin, name)) {
|
||||
store.dispatch(
|
||||
starPlugin({
|
||||
plugin,
|
||||
|
||||
@@ -135,6 +135,7 @@ export function processMessagesLater(
|
||||
case (plugin as any).prototype instanceof FlipperDevicePlugin:
|
||||
case pluginIsStarred(
|
||||
store.getState().connections.userStarredPlugins,
|
||||
store.getState().connections.userStarredDevicePlugins,
|
||||
deconstructPluginKey(pluginKey).client,
|
||||
pluginId,
|
||||
):
|
||||
|
||||
@@ -84,6 +84,7 @@ export function getExportablePlugins(
|
||||
client,
|
||||
state.plugins,
|
||||
state.connections.userStarredPlugins,
|
||||
state.connections.userStarredDevicePlugins,
|
||||
);
|
||||
|
||||
return [
|
||||
@@ -179,16 +180,33 @@ export function computePluginLists(
|
||||
client: Client | undefined,
|
||||
plugins: State['plugins'],
|
||||
userStarredPlugins: State['connections']['userStarredPlugins'],
|
||||
userStarredDevicePlugins: Set<string>,
|
||||
_pluginsChanged?: number, // this argument is purely used to invalidate the memoization cache
|
||||
) {
|
||||
const uninstalledMarketplacePlugins = filterNewestVersionOfEachPlugin(
|
||||
[...plugins.bundledPlugins.values()],
|
||||
plugins.marketplacePlugins,
|
||||
).filter((p) => !plugins.loadedPlugins.has(p.id));
|
||||
const devicePlugins: DevicePluginDefinition[] = [
|
||||
...plugins.devicePlugins.values(),
|
||||
].filter((p) => device?.supportsPlugin(p));
|
||||
]
|
||||
.filter((p) => device?.supportsPlugin(p))
|
||||
.filter((p) => userStarredDevicePlugins.has(p.id));
|
||||
const metroPlugins: DevicePluginDefinition[] = [
|
||||
...plugins.devicePlugins.values(),
|
||||
].filter((p) => metroDevice?.supportsPlugin(p));
|
||||
]
|
||||
.filter((p) => metroDevice?.supportsPlugin(p))
|
||||
.filter((p) => userStarredDevicePlugins.has(p.id));
|
||||
const enabledPlugins: ClientPluginDefinition[] = [];
|
||||
const disabledPlugins: ClientPluginDefinition[] = [];
|
||||
const disabledPlugins: PluginDefinition[] = [
|
||||
...plugins.devicePlugins.values(),
|
||||
]
|
||||
.filter(
|
||||
(p) =>
|
||||
device?.supportsPlugin(p.details) ||
|
||||
metroDevice?.supportsPlugin(p.details),
|
||||
)
|
||||
.filter((p) => !userStarredDevicePlugins.has(p.id));
|
||||
const unavailablePlugins: [plugin: PluginDetails, reason: string][] = [];
|
||||
const downloadablePlugins: (
|
||||
| DownloadablePluginDetails
|
||||
@@ -203,10 +221,20 @@ export function computePluginLists(
|
||||
p.details,
|
||||
`Device plugin '${getPluginTitle(
|
||||
p.details,
|
||||
)}' is not supported by the current device type.`,
|
||||
)}' is not supported by the currently connected device.`,
|
||||
]);
|
||||
}
|
||||
}
|
||||
for (const plugin of uninstalledMarketplacePlugins.filter(
|
||||
(d) => d.pluginType === 'device',
|
||||
)) {
|
||||
if (
|
||||
device.supportsPlugin(plugin) ||
|
||||
metroDevice?.supportsPlugin(plugin)
|
||||
) {
|
||||
downloadablePlugins.push(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process problematic plugins
|
||||
@@ -244,7 +272,7 @@ export function computePluginLists(
|
||||
plugin.details,
|
||||
`Plugin '${getPluginTitle(
|
||||
plugin.details,
|
||||
)}' is installed in Flipper, but not supported by the client application`,
|
||||
)}' is not supported by the client application`,
|
||||
]);
|
||||
} else if (favoritePlugins.includes(plugin)) {
|
||||
enabledPlugins.push(plugin);
|
||||
@@ -252,23 +280,25 @@ export function computePluginLists(
|
||||
disabledPlugins.push(plugin);
|
||||
}
|
||||
});
|
||||
const uninstalledMarketplacePlugins = filterNewestVersionOfEachPlugin(
|
||||
[...plugins.bundledPlugins.values()],
|
||||
plugins.marketplacePlugins,
|
||||
).filter((p) => !plugins.loadedPlugins.has(p.id));
|
||||
uninstalledMarketplacePlugins.forEach((plugin) => {
|
||||
if (client.supportsPlugin(plugin.id)) {
|
||||
downloadablePlugins.push(plugin);
|
||||
} else {
|
||||
unavailablePlugins.push([
|
||||
plugin,
|
||||
`Plugin '${getPluginTitle(
|
||||
plugin,
|
||||
)}' is not installed in Flipper and not supported by the client application`,
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
const downloadablePluginSet = new Set<string>(
|
||||
downloadablePlugins.map((p) => p.id),
|
||||
);
|
||||
uninstalledMarketplacePlugins
|
||||
.filter((p) => !downloadablePluginSet.has(p.id))
|
||||
.forEach((plugin) => {
|
||||
unavailablePlugins.push([
|
||||
plugin,
|
||||
`Plugin '${getPluginTitle(
|
||||
plugin,
|
||||
)}' is not supported by the client application and not installed in Flipper`,
|
||||
]);
|
||||
});
|
||||
|
||||
devicePlugins.sort(sortPluginsByName);
|
||||
metroPlugins.sort(sortPluginsByName);
|
||||
|
||||
Reference in New Issue
Block a user