Summary: This diff fixes an issue where we don't want to have the Navigation plugin be disabled as background plugins like all other plugins, so that the breadcrumb navigation keeps working. Yet another hack concerning the super useful Navigation plugin. On a positive note, since connection management for background is not entirely managed by the Desktop and not the native said, these kind of exceptions are fairly easy to make :) Reviewed By: passy Differential Revision: D21089537 fbshipit-source-id: 209954ff35c95e066fe688a60ad46ccfc3835c44
242 lines
7.8 KiB
TypeScript
242 lines
7.8 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 {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, deconstructClientId} from './clientUtils';
|
|
|
|
type Client = import('../Client').default;
|
|
|
|
export const defaultEnabledBackgroundPlugins = ['Navigation']; // The navigation plugin is enabled always, to make sure the navigation features works
|
|
|
|
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: {serial: string} | 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 starredPlugin starredPlugin is the dictionary of client and its enabled plugin
|
|
* @param client Optional paramater indicating the selected client.
|
|
* @param plugins Plugins from the state which has the mapping to Plugin's Class.
|
|
|
|
* Returns plugins which are enabled or which has exportPersistedState function defined for the passed client.
|
|
* Note all device plugins are enabled.
|
|
*/
|
|
|
|
export function getEnabledOrExportPersistedStatePlugins(
|
|
starredPlugin: {
|
|
[client: string]: string[];
|
|
},
|
|
client: Client,
|
|
plugins: PluginsState,
|
|
): Array<{id: string; label: string}> {
|
|
const appName = deconstructClientId(client.id).app;
|
|
const pluginsMap: Map<
|
|
string,
|
|
typeof FlipperDevicePlugin | typeof FlipperPlugin
|
|
> = pluginsClassMap(plugins);
|
|
// Enabled Plugins with no exportPersistedState function defined
|
|
const enabledPlugins = starredPlugin[appName]
|
|
? starredPlugin[appName]
|
|
.map((pluginName) => pluginsMap.get(pluginName)!)
|
|
.filter((plugin) => {
|
|
return !plugin.exportPersistedState;
|
|
})
|
|
.sort(sortPluginsByName)
|
|
.map((plugin) => {
|
|
return {id: plugin.id, label: getPluginTitle(plugin)};
|
|
})
|
|
: [];
|
|
// Device Plugins
|
|
const devicePlugins = Array.from(plugins.devicePlugins.keys())
|
|
.filter((plugin) => {
|
|
return client.plugins.includes(plugin);
|
|
})
|
|
.map((plugin) => {
|
|
return {
|
|
id: plugin,
|
|
label: getPluginTitle(plugins.devicePlugins.get(plugin)!),
|
|
};
|
|
});
|
|
// Plugins which have defined exportPersistedState.
|
|
const exportPersistedStatePlugins = client.plugins
|
|
.filter((name) => {
|
|
return pluginsMap.get(name)?.exportPersistedState != null;
|
|
})
|
|
.map((name) => {
|
|
const plugin = pluginsMap.get(name)!;
|
|
return {id: plugin.id, label: getPluginTitle(plugin)};
|
|
});
|
|
return [
|
|
...devicePlugins,
|
|
...enabledPlugins,
|
|
...exportPersistedStatePlugins,
|
|
{id: 'DeviceLogs', label: 'Logs'},
|
|
];
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @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;
|
|
}
|