Auto-install favorite plugins

Summary: After unbudling plugins users will need to manually install them. To simlify this transition we can auto-install favorite plugins on every app connection, so users won't need to install plugins which they marked as favorite before.

Reviewed By: passy

Differential Revision: D25538638

fbshipit-source-id: 4ac3ad0c8c026d149f7ce0c4af07da3e8b909772
This commit is contained in:
Anton Nikolaev
2020-12-15 09:28:58 -08:00
committed by Facebook GitHub Bot
parent bd01b58566
commit 965559ee65
6 changed files with 84 additions and 15 deletions

View File

@@ -56,6 +56,7 @@ Object {
"disabledPlugins": Array [], "disabledPlugins": Array [],
"failedPlugins": Array [], "failedPlugins": Array [],
"gatekeepedPlugins": Array [], "gatekeepedPlugins": Array [],
"loadedPlugins": Map {},
"marketplacePlugins": Array [], "marketplacePlugins": Array [],
"selectedPlugins": Array [], "selectedPlugins": Array [],
} }

View File

@@ -19,6 +19,7 @@ import {
addGatekeepedPlugins, addGatekeepedPlugins,
addDisabledPlugins, addDisabledPlugins,
addFailedPlugins, addFailedPlugins,
registerLoadedPlugins,
} from '../reducers/plugins'; } from '../reducers/plugins';
import GK from '../fb-stubs/GK'; import GK from '../fb-stubs/GK';
import {FlipperBasePlugin} from '../plugin'; import {FlipperBasePlugin} from '../plugin';
@@ -63,10 +64,12 @@ export default async (store: Store, logger: Logger) => {
const uninstalledPlugins = store.getState().pluginManager.uninstalledPlugins; const uninstalledPlugins = store.getState().pluginManager.uninstalledPlugins;
const initialPlugins: PluginDefinition[] = filterNewestVersionOfEachPlugin( const loadedPlugins = filterNewestVersionOfEachPlugin(
getBundledPlugins(), getBundledPlugins(),
await getDynamicPlugins(), await getDynamicPlugins(),
) );
const initialPlugins: PluginDefinition[] = loadedPlugins
.filter((p) => !uninstalledPlugins.has(p.name)) .filter((p) => !uninstalledPlugins.has(p.name))
.map(reportVersion) .map(reportVersion)
.filter(checkDisabled(disabledPlugins)) .filter(checkDisabled(disabledPlugins))
@@ -74,6 +77,7 @@ export default async (store: Store, logger: Logger) => {
.map(createRequirePluginFunction(failedPlugins)) .map(createRequirePluginFunction(failedPlugins))
.filter(notNull); .filter(notNull);
store.dispatch(registerLoadedPlugins(loadedPlugins));
store.dispatch(addGatekeepedPlugins(gatekeepedPlugins)); store.dispatch(addGatekeepedPlugins(gatekeepedPlugins));
store.dispatch(addDisabledPlugins(disabledPlugins)); store.dispatch(addDisabledPlugins(disabledPlugins));
store.dispatch(addFailedPlugins(failedPlugins)); store.dispatch(addFailedPlugins(failedPlugins));

View File

@@ -13,6 +13,7 @@ import {
addGatekeepedPlugins, addGatekeepedPlugins,
} from '../plugins'; } from '../plugins';
import {FlipperPlugin, FlipperDevicePlugin, BaseAction} from '../../plugin'; import {FlipperPlugin, FlipperDevicePlugin, BaseAction} from '../../plugin';
import {InstalledPluginDetails} from 'flipper-plugin-lib';
const testPlugin = class extends FlipperPlugin<any, BaseAction, any> { const testPlugin = class extends FlipperPlugin<any, BaseAction, any> {
static id = 'TestPlugin'; static id = 'TestPlugin';
@@ -31,6 +32,7 @@ test('add clientPlugin', () => {
{ {
devicePlugins: new Map(), devicePlugins: new Map(),
clientPlugins: new Map(), clientPlugins: new Map(),
loadedPlugins: new Map(),
gatekeepedPlugins: [], gatekeepedPlugins: [],
failedPlugins: [], failedPlugins: [],
disabledPlugins: [], disabledPlugins: [],
@@ -47,6 +49,7 @@ test('add devicePlugin', () => {
{ {
devicePlugins: new Map(), devicePlugins: new Map(),
clientPlugins: new Map(), clientPlugins: new Map(),
loadedPlugins: new Map(),
gatekeepedPlugins: [], gatekeepedPlugins: [],
failedPlugins: [], failedPlugins: [],
disabledPlugins: [], disabledPlugins: [],
@@ -63,6 +66,7 @@ test('do not add plugin twice', () => {
{ {
devicePlugins: new Map(), devicePlugins: new Map(),
clientPlugins: new Map(), clientPlugins: new Map(),
loadedPlugins: new Map(),
gatekeepedPlugins: [], gatekeepedPlugins: [],
failedPlugins: [], failedPlugins: [],
disabledPlugins: [], disabledPlugins: [],
@@ -75,15 +79,15 @@ test('do not add plugin twice', () => {
}); });
test('add gatekeeped plugin', () => { test('add gatekeeped plugin', () => {
const gatekeepedPlugins = [ const gatekeepedPlugins: InstalledPluginDetails[] = [
{ {
name: 'plugin', name: 'plugin',
out: 'out.js',
version: '1.0.0', version: '1.0.0',
dir: '/plugins/test', dir: '/plugins/test',
specVersion: 2, specVersion: 2,
source: 'src/index.ts', source: 'src/index.ts',
isBundled: false, isBundled: false,
isActivatable: true,
main: 'lib/index.js', main: 'lib/index.js',
title: 'test', title: 'test',
id: 'test', id: 'test',
@@ -94,6 +98,7 @@ test('add gatekeeped plugin', () => {
{ {
devicePlugins: new Map(), devicePlugins: new Map(),
clientPlugins: new Map(), clientPlugins: new Map(),
loadedPlugins: new Map(),
gatekeepedPlugins: [], gatekeepedPlugins: [],
failedPlugins: [], failedPlugins: [],
disabledPlugins: [], disabledPlugins: [],

View File

@@ -8,7 +8,10 @@
*/ */
import {DevicePluginMap, ClientPluginMap, PluginDefinition} from '../plugin'; import {DevicePluginMap, ClientPluginMap, PluginDefinition} from '../plugin';
import {PluginDetails, DownloadablePluginDetails} from 'flipper-plugin-lib'; import {
DownloadablePluginDetails,
ActivatablePluginDetails,
} from 'flipper-plugin-lib';
import {Actions} from '.'; import {Actions} from '.';
import produce from 'immer'; import produce from 'immer';
import {isDevicePluginDefinition} from '../utils/pluginUtils'; import {isDevicePluginDefinition} from '../utils/pluginUtils';
@@ -16,9 +19,10 @@ import {isDevicePluginDefinition} from '../utils/pluginUtils';
export type State = { export type State = {
devicePlugins: DevicePluginMap; devicePlugins: DevicePluginMap;
clientPlugins: ClientPluginMap; clientPlugins: ClientPluginMap;
gatekeepedPlugins: Array<PluginDetails>; loadedPlugins: Map<string, ActivatablePluginDetails>;
disabledPlugins: Array<PluginDetails>; gatekeepedPlugins: Array<ActivatablePluginDetails>;
failedPlugins: Array<[PluginDetails, string]>; disabledPlugins: Array<ActivatablePluginDetails>;
failedPlugins: Array<[ActivatablePluginDetails, string]>;
selectedPlugins: Array<string>; selectedPlugins: Array<string>;
marketplacePlugins: Array<DownloadablePluginDetails>; marketplacePlugins: Array<DownloadablePluginDetails>;
}; };
@@ -32,15 +36,15 @@ export type Action =
| RegisterPluginAction | RegisterPluginAction
| { | {
type: 'GATEKEEPED_PLUGINS'; type: 'GATEKEEPED_PLUGINS';
payload: Array<PluginDetails>; payload: Array<ActivatablePluginDetails>;
} }
| { | {
type: 'DISABLED_PLUGINS'; type: 'DISABLED_PLUGINS';
payload: Array<PluginDetails>; payload: Array<ActivatablePluginDetails>;
} }
| { | {
type: 'FAILED_PLUGINS'; type: 'FAILED_PLUGINS';
payload: Array<[PluginDetails, string]>; payload: Array<[ActivatablePluginDetails, string]>;
} }
| { | {
type: 'SELECTED_PLUGINS'; type: 'SELECTED_PLUGINS';
@@ -49,11 +53,16 @@ export type Action =
| { | {
type: 'MARKETPLACE_PLUGINS'; type: 'MARKETPLACE_PLUGINS';
payload: Array<DownloadablePluginDetails>; payload: Array<DownloadablePluginDetails>;
}
| {
type: 'REGISTER_LOADED_PLUGINS';
payload: Array<ActivatablePluginDetails>;
}; };
const INITIAL_STATE: State = { const INITIAL_STATE: State = {
devicePlugins: new Map(), devicePlugins: new Map(),
clientPlugins: new Map(), clientPlugins: new Map(),
loadedPlugins: new Map(),
gatekeepedPlugins: [], gatekeepedPlugins: [],
disabledPlugins: [], disabledPlugins: [],
failedPlugins: [], failedPlugins: [],
@@ -105,6 +114,11 @@ export default function reducer(
...state, ...state,
marketplacePlugins: action.payload, marketplacePlugins: action.payload,
}; };
} else if (action.type === 'REGISTER_LOADED_PLUGINS') {
return {
...state,
loadedPlugins: new Map(action.payload.map((p) => [p.id, p])),
};
} else { } else {
return state; return state;
} }
@@ -121,19 +135,21 @@ export const registerPlugins = (payload: PluginDefinition[]): Action => ({
}); });
export const addGatekeepedPlugins = ( export const addGatekeepedPlugins = (
payload: Array<PluginDetails>, payload: Array<ActivatablePluginDetails>,
): Action => ({ ): Action => ({
type: 'GATEKEEPED_PLUGINS', type: 'GATEKEEPED_PLUGINS',
payload, payload,
}); });
export const addDisabledPlugins = (payload: Array<PluginDetails>): Action => ({ export const addDisabledPlugins = (
payload: Array<ActivatablePluginDetails>,
): Action => ({
type: 'DISABLED_PLUGINS', type: 'DISABLED_PLUGINS',
payload, payload,
}); });
export const addFailedPlugins = ( export const addFailedPlugins = (
payload: Array<[PluginDetails, string]>, payload: Array<[ActivatablePluginDetails, string]>,
): Action => ({ ): Action => ({
type: 'FAILED_PLUGINS', type: 'FAILED_PLUGINS',
payload, payload,
@@ -145,3 +161,10 @@ export const registerMarketplacePlugins = (
type: 'MARKETPLACE_PLUGINS', type: 'MARKETPLACE_PLUGINS',
payload, payload,
}); });
export const registerLoadedPlugins = (
payload: Array<ActivatablePluginDetails>,
): Action => ({
type: 'REGISTER_LOADED_PLUGINS',
payload,
});

View File

@@ -27,6 +27,7 @@ import {deconstructPluginKey} from './utils/clientUtils';
import {_SandyPluginDefinition} from 'flipper-plugin'; import {_SandyPluginDefinition} from 'flipper-plugin';
import BaseDevice from './devices/BaseDevice'; import BaseDevice from './devices/BaseDevice';
import {State as PluginStates} from './reducers/pluginStates'; import {State as PluginStates} from './reducers/pluginStates';
import {ActivatablePluginDetails} from 'flipper-plugin-lib';
export const store: Store = createStore<StoreState, Actions, any, any>( export const store: Store = createStore<StoreState, Actions, any, any>(
rootReducer, rootReducer,
@@ -173,7 +174,7 @@ function updateClientPlugin(
clientsWithEnabledPlugin.forEach((client) => { clientsWithEnabledPlugin.forEach((client) => {
startPlugin(client, plugin, true); startPlugin(client, plugin, true);
}); });
draft.pluginManager.uninstalledPlugins.delete(plugin.details.name); registerLoadedPlugin(draft, plugin.details);
}); });
} }
@@ -191,6 +192,7 @@ function uninstallPlugin(state: StoreState, plugin: PluginDefinition) {
}); });
cleanupPluginStates(draft.pluginStates, plugin.id); cleanupPluginStates(draft.pluginStates, plugin.id);
draft.plugins.clientPlugins.delete(plugin.id); draft.plugins.clientPlugins.delete(plugin.id);
draft.plugins.devicePlugins.delete(plugin.id);
draft.pluginManager.uninstalledPlugins.add(plugin.details.name); draft.pluginManager.uninstalledPlugins.add(plugin.details.name);
}); });
} }
@@ -209,9 +211,21 @@ function updateDevicePlugin(state: StoreState, plugin: DevicePluginDefinition) {
devicesWithEnabledPlugin.forEach((d) => { devicesWithEnabledPlugin.forEach((d) => {
d.loadDevicePlugin(plugin); d.loadDevicePlugin(plugin);
}); });
registerLoadedPlugin(draft, plugin.details);
}); });
} }
function registerLoadedPlugin(
draft: {
pluginManager: StoreState['pluginManager'];
plugins: StoreState['plugins'];
},
plugin: ActivatablePluginDetails,
) {
draft.pluginManager.uninstalledPlugins.delete(plugin.name);
draft.plugins.loadedPlugins.set(plugin.id, plugin);
}
function supportsDevice(plugin: DevicePluginDefinition, device: BaseDevice) { function supportsDevice(plugin: DevicePluginDefinition, device: BaseDevice) {
if (plugin instanceof _SandyPluginDefinition) { if (plugin instanceof _SandyPluginDefinition) {
return ( return (

View File

@@ -762,6 +762,11 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
['RandomPlugin', TestPlugin], ['RandomPlugin', TestPlugin],
]), ]),
devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]), devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]),
loadedPlugins: new Map([
['TestPlugin', TestPlugin.details],
['RandomPlugin', TestPlugin.details],
['TestDevicePlugin', TestDevicePlugin.details],
]),
gatekeepedPlugins: [], gatekeepedPlugins: [],
disabledPlugins: [], disabledPlugins: [],
failedPlugins: [], failedPlugins: [],
@@ -828,6 +833,11 @@ test('test determinePluginsToProcess for no selected plugin present in any clien
['RandomPlugin', TestPlugin], ['RandomPlugin', TestPlugin],
]), ]),
devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]), devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]),
loadedPlugins: new Map([
['TestPlugin', TestPlugin.details],
['RandomPlugin', TestPlugin.details],
['TestDevicePlugin', TestDevicePlugin.details],
]),
gatekeepedPlugins: [], gatekeepedPlugins: [],
disabledPlugins: [], disabledPlugins: [],
failedPlugins: [], failedPlugins: [],
@@ -872,6 +882,10 @@ test('test determinePluginsToProcess for multiple clients on same device', async
const plugins: PluginsState = { const plugins: PluginsState = {
clientPlugins: new Map([['TestPlugin', TestPlugin]]), clientPlugins: new Map([['TestPlugin', TestPlugin]]),
devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]), devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]),
loadedPlugins: new Map([
['TestPlugin', TestPlugin.details],
['TestDevicePlugin', TestDevicePlugin.details],
]),
gatekeepedPlugins: [], gatekeepedPlugins: [],
disabledPlugins: [], disabledPlugins: [],
failedPlugins: [], failedPlugins: [],
@@ -954,6 +968,10 @@ test('test determinePluginsToProcess for multiple clients on different device',
const plugins: PluginsState = { const plugins: PluginsState = {
clientPlugins: new Map([['TestPlugin', TestPlugin]]), clientPlugins: new Map([['TestPlugin', TestPlugin]]),
devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]), devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]),
loadedPlugins: new Map([
['TestPlugin', TestPlugin.details],
['TestDevicePlugin', TestDevicePlugin.details],
]),
gatekeepedPlugins: [], gatekeepedPlugins: [],
disabledPlugins: [], disabledPlugins: [],
failedPlugins: [], failedPlugins: [],
@@ -1033,6 +1051,10 @@ test('test determinePluginsToProcess to ignore archived clients', async () => {
const plugins: PluginsState = { const plugins: PluginsState = {
clientPlugins: new Map([['TestPlugin', TestPlugin]]), clientPlugins: new Map([['TestPlugin', TestPlugin]]),
devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]), devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]),
loadedPlugins: new Map([
['TestPlugin', TestPlugin.details],
['TestDevicePlugin', TestDevicePlugin.details],
]),
gatekeepedPlugins: [], gatekeepedPlugins: [],
disabledPlugins: [], disabledPlugins: [],
failedPlugins: [], failedPlugins: [],