Files
flipper/src/utils/pluginUtils.tsx
Michel Weststrate 82e65c68dc Make sure plugin name selections are displayed consistently with sidebar, [Flipper] Make sure plugins have the same name everywhere
Summary:
Not all plugin names are created equal in flipper. For example, plugins would bear different names in the sidebar and in the plugin selection when making a support request / flipper trace. Fixed this and also introduced a `getPluginTitle` utility that produces this name consistently.

Plugin listview now also sort their items consitently with the sidebar.

Probably also fixed an error in the flipper export screen, where a correct TS error was supressed.

Reviewed By: jknoxville

Differential Revision: D19499404

fbshipit-source-id: c5b23a170d41d96799eb7899e249f70778717d45
2020-01-27 07:22:32 -08:00

179 lines
5.7 KiB
TypeScript

/**
* 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 {FlipperDevicePlugin, FlipperPlugin, FlipperBasePlugin} from '../plugin';
import BaseDevice from '../devices/BaseDevice';
import {State as PluginStatesState} from '../reducers/pluginStates';
import {State as PluginsState} from '../reducers/plugins';
import {State as PluginMessageQueueState} from '../reducers/pluginMessageQueue';
import {PluginDefinition} from '../dispatcher/plugins';
import {deconstructPluginKey} from './clientUtils';
type Client = import('../Client').default;
export function pluginsClassMap(
plugins: PluginsState,
): Map<string, typeof FlipperDevicePlugin | typeof FlipperPlugin> {
const pluginsMap: Map<
string,
typeof FlipperDevicePlugin | typeof FlipperPlugin
> = new Map([]);
plugins.clientPlugins.forEach((val, key) => {
pluginsMap.set(key, val);
});
plugins.devicePlugins.forEach((val, key) => {
pluginsMap.set(key, val);
});
return pluginsMap;
}
export function getPluginKey(
selectedAppId: string | null,
baseDevice: BaseDevice | null,
pluginID: string,
): string {
if (selectedAppId) {
return `${selectedAppId}#${pluginID}`;
}
if (baseDevice) {
// If selected App is not defined, then the plugin is a device plugin
return `${baseDevice.serial}#${pluginID}`;
}
return `unknown#${pluginID}`;
}
export function getPersistedState<PersistedState>(
pluginKey: string,
persistingPlugin: typeof FlipperBasePlugin | null,
pluginStates: PluginStatesState,
): PersistedState | null {
if (!persistingPlugin) {
return null;
}
const persistedState: PersistedState = {
...persistingPlugin.defaultPersistedState,
...pluginStates[pluginKey],
};
return persistedState;
}
/**
*
* @param pluginsState PluginsState of the Redux Store.
* @param plugins Plugins from the state which has the mapping to Plugin's Class.
* @param selectedClient Optional paramater indicating the selected client.
* Returns active persistent plugin, which means plugins which has the data in redux store or has the `exportPersistedState` function defined which can return the plugin's data when called.
* If the selectedClient is defined then the active persistent plugins only for the selectedClient will be returned, otherwise it will return all active persistent plugins.
*/
export function getActivePersistentPlugins(
pluginsState: PluginStatesState,
pluginsMessageQueue: PluginMessageQueueState,
plugins: PluginsState,
selectedClient?: Client,
): {id: string; label: string}[] {
const pluginsMap: Map<
string,
typeof FlipperDevicePlugin | typeof FlipperPlugin
> = pluginsClassMap(plugins);
return getPersistentPlugins(plugins)
.map(pluginName => pluginsMap.get(pluginName)!)
.sort(sortPluginsByName)
.map(plugin => {
const keys = [
...new Set([
...Object.keys(pluginsState),
...Object.keys(pluginsMessageQueue),
]),
]
.filter(k => !selectedClient || k.includes(selectedClient.id))
.map(key => deconstructPluginKey(key).pluginName);
let result = plugin.id == 'DeviceLogs';
const pluginsWithExportPersistedState =
plugin && plugin.exportPersistedState != undefined;
const pluginsWithReduxData = keys.includes(plugin.id);
if (!result && selectedClient) {
// If there is a selected client, active persistent plugin is the plugin which is active for selectedClient and also persistent.
result =
selectedClient.plugins.includes(plugin.id) &&
(pluginsWithExportPersistedState || pluginsWithReduxData);
} else if (!result && !selectedClient) {
// If there is no selected client, active persistent plugin is the plugin which is just persistent.
result =
(plugin && plugin.exportPersistedState != undefined) ||
keys.includes(plugin.id);
}
return (result
? {
id: plugin.id,
label: getPluginTitle(plugin),
}
: undefined)!;
})
.filter(Boolean);
}
export function getPersistentPlugins(plugins: PluginsState): Array<string> {
const pluginsMap: Map<
string,
typeof FlipperDevicePlugin | typeof FlipperPlugin
> = pluginsClassMap(plugins);
const arr: Array<PluginDefinition> = plugins.disabledPlugins.concat(
plugins.gatekeepedPlugins,
);
arr.forEach((plugin: PluginDefinition) => {
if (pluginsMap.has(plugin.name)) {
pluginsMap.delete(plugin.name);
}
});
plugins.failedPlugins.forEach((plugin: [PluginDefinition, string]) => {
if (plugin[0] && plugin[0].name && pluginsMap.has(plugin[0].name)) {
pluginsMap.delete(plugin[0].name);
}
});
const activePlugins = [...pluginsMap.keys()];
return activePlugins.filter(plugin => {
const pluginClass = pluginsMap.get(plugin);
return (
plugin == 'DeviceLogs' ||
(pluginClass &&
(pluginClass.defaultPersistedState != undefined ||
pluginClass.exportPersistedState != undefined))
);
});
}
export function getPluginTitle(pluginClass: typeof FlipperBasePlugin) {
return pluginClass.title || pluginClass.id;
}
export function sortPluginsByName(
a: typeof FlipperBasePlugin,
b: typeof FlipperBasePlugin,
): number {
// make sure Device plugins are sorted before normal plugins
if (
a.prototype instanceof FlipperDevicePlugin &&
!(b.prototype instanceof FlipperDevicePlugin)
) {
return -1;
}
if (
b.prototype instanceof FlipperDevicePlugin &&
!(a.prototype instanceof FlipperDevicePlugin)
) {
return 1;
}
return getPluginTitle(a) > getPluginTitle(b) ? 1 : -1;
}