Change PluginContainer to allow selecting unavailable plugins
Summary: Changed PluginContainer implementation to allow selecting unavailable/uninstalled plugins which was impossible before that. Please note that for now something is shown only for enabled and disabled plugins. In next diff I'm also adding content for unavailable and uninstalled plugins. Reviewed By: mweststrate Differential Revision: D29254788 fbshipit-source-id: da639e5efddc821061abb69df534ffdce8ccd4e0
This commit is contained in:
committed by
Facebook GitHub Bot
parent
ff5d8ba29f
commit
e36eec82b2
@@ -11,7 +11,6 @@ import {
|
|||||||
FlipperPlugin,
|
FlipperPlugin,
|
||||||
FlipperDevicePlugin,
|
FlipperDevicePlugin,
|
||||||
Props as PluginProps,
|
Props as PluginProps,
|
||||||
PluginDefinition,
|
|
||||||
} from './plugin';
|
} from './plugin';
|
||||||
import {Logger} from './fb-interfaces/Logger';
|
import {Logger} from './fb-interfaces/Logger';
|
||||||
import BaseDevice from './devices/BaseDevice';
|
import BaseDevice from './devices/BaseDevice';
|
||||||
@@ -28,11 +27,7 @@ import {
|
|||||||
VBox,
|
VBox,
|
||||||
View,
|
View,
|
||||||
} from './ui';
|
} from './ui';
|
||||||
import {
|
import {StaticView, setStaticView} from './reducers/connections';
|
||||||
StaticView,
|
|
||||||
setStaticView,
|
|
||||||
isPluginEnabled,
|
|
||||||
} from './reducers/connections';
|
|
||||||
import {switchPlugin} from './reducers/pluginManager';
|
import {switchPlugin} from './reducers/pluginManager';
|
||||||
import React, {PureComponent} from 'react';
|
import React, {PureComponent} from 'react';
|
||||||
import {connect, ReactReduxContext} from 'react-redux';
|
import {connect, ReactReduxContext} from 'react-redux';
|
||||||
@@ -46,7 +41,12 @@ import {IdlerImpl} from './utils/Idler';
|
|||||||
import {processMessageQueue} from './utils/messageQueue';
|
import {processMessageQueue} from './utils/messageQueue';
|
||||||
import {Layout} from './ui';
|
import {Layout} from './ui';
|
||||||
import {theme, TrackingScope, _SandyPluginRenderer} from 'flipper-plugin';
|
import {theme, TrackingScope, _SandyPluginRenderer} from 'flipper-plugin';
|
||||||
import {isDevicePluginDefinition, isSandyPlugin} from './utils/pluginUtils';
|
import {
|
||||||
|
ActivePluginListItem,
|
||||||
|
isDevicePlugin,
|
||||||
|
isDevicePluginDefinition,
|
||||||
|
isSandyPlugin,
|
||||||
|
} from './utils/pluginUtils';
|
||||||
import {ContentContainer} from './sandy-chrome/ContentContainer';
|
import {ContentContainer} from './sandy-chrome/ContentContainer';
|
||||||
import {Alert, Typography} from 'antd';
|
import {Alert, Typography} from 'antd';
|
||||||
import {InstalledPluginDetails} from 'flipper-plugin-lib';
|
import {InstalledPluginDetails} from 'flipper-plugin-lib';
|
||||||
@@ -55,6 +55,7 @@ import {loadPlugin} from './reducers/pluginManager';
|
|||||||
import {produce} from 'immer';
|
import {produce} from 'immer';
|
||||||
import {reportUsage} from './utils/metrics';
|
import {reportUsage} from './utils/metrics';
|
||||||
import PluginInfo from './chrome/fb-stubs/PluginInfo';
|
import PluginInfo from './chrome/fb-stubs/PluginInfo';
|
||||||
|
import {getActiveClient, getActivePlugin} from './selectors/connections';
|
||||||
|
|
||||||
const {Text, Link} = Typography;
|
const {Text, Link} = Typography;
|
||||||
|
|
||||||
@@ -107,14 +108,13 @@ type OwnProps = {
|
|||||||
|
|
||||||
type StateFromProps = {
|
type StateFromProps = {
|
||||||
pluginState: Object;
|
pluginState: Object;
|
||||||
activePlugin: PluginDefinition | undefined;
|
activePlugin: ActivePluginListItem | null;
|
||||||
target: Client | BaseDevice | null;
|
target: Client | BaseDevice | null;
|
||||||
pluginKey: string | null;
|
pluginKey: string | null;
|
||||||
deepLinkPayload: unknown;
|
deepLinkPayload: unknown;
|
||||||
selectedApp: string | null;
|
selectedApp: string | null;
|
||||||
isArchivedDevice: boolean;
|
isArchivedDevice: boolean;
|
||||||
pendingMessages: Message[] | undefined;
|
pendingMessages: Message[] | undefined;
|
||||||
pluginIsEnabled: boolean;
|
|
||||||
settingsState: Settings;
|
settingsState: Settings;
|
||||||
latestInstalledVersion: InstalledPluginDetails | undefined;
|
latestInstalledVersion: InstalledPluginDetails | undefined;
|
||||||
};
|
};
|
||||||
@@ -202,14 +202,13 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
const {deepLinkPayload, target, activePlugin} = this.props;
|
const {deepLinkPayload, target, activePlugin} = this.props;
|
||||||
if (deepLinkPayload && activePlugin && target) {
|
if (deepLinkPayload && activePlugin && target) {
|
||||||
target.sandyPluginStates
|
target.sandyPluginStates
|
||||||
.get(activePlugin.id)
|
.get(activePlugin.details.id)
|
||||||
?.triggerDeepLink(deepLinkPayload);
|
?.triggerDeepLink(deepLinkPayload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processMessageQueue() {
|
processMessageQueue() {
|
||||||
const {pluginKey, pendingMessages, activePlugin, pluginIsEnabled, target} =
|
const {pluginKey, pendingMessages, activePlugin, target} = this.props;
|
||||||
this.props;
|
|
||||||
if (pluginKey !== this.pluginBeingProcessed) {
|
if (pluginKey !== this.pluginBeingProcessed) {
|
||||||
this.pluginBeingProcessed = pluginKey;
|
this.pluginBeingProcessed = pluginKey;
|
||||||
this.cancelCurrentQueue();
|
this.cancelCurrentQueue();
|
||||||
@@ -219,23 +218,27 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
// device plugins don't have connections so no message queues
|
// device plugins don't have connections so no message queues
|
||||||
if (!activePlugin || isDevicePluginDefinition(activePlugin)) {
|
if (
|
||||||
|
!activePlugin ||
|
||||||
|
activePlugin.status !== 'enabled' ||
|
||||||
|
isDevicePluginDefinition(activePlugin.definition)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
pluginIsEnabled &&
|
|
||||||
target instanceof Client &&
|
target instanceof Client &&
|
||||||
activePlugin &&
|
activePlugin &&
|
||||||
(isSandyPlugin(activePlugin) || activePlugin.persistedStateReducer) &&
|
(isSandyPlugin(activePlugin.definition) ||
|
||||||
|
activePlugin.definition.persistedStateReducer) &&
|
||||||
pluginKey &&
|
pluginKey &&
|
||||||
pendingMessages?.length
|
pendingMessages?.length
|
||||||
) {
|
) {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
this.idler = new IdlerImpl();
|
this.idler = new IdlerImpl();
|
||||||
processMessageQueue(
|
processMessageQueue(
|
||||||
isSandyPlugin(activePlugin)
|
isSandyPlugin(activePlugin.definition)
|
||||||
? target.sandyPluginStates.get(activePlugin.id)!
|
? target.sandyPluginStates.get(activePlugin.definition.id)!
|
||||||
: activePlugin,
|
: activePlugin.definition,
|
||||||
pluginKey,
|
pluginKey,
|
||||||
this.store,
|
this.store,
|
||||||
(progress) => {
|
(progress) => {
|
||||||
@@ -246,18 +249,22 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
this.idler,
|
this.idler,
|
||||||
).then((completed) => {
|
)
|
||||||
const duration = Date.now() - start;
|
.then((completed) => {
|
||||||
this.props.logger.track(
|
const duration = Date.now() - start;
|
||||||
'duration',
|
this.props.logger.track(
|
||||||
'queue-processing-before-plugin-open',
|
'duration',
|
||||||
{
|
'queue-processing-before-plugin-open',
|
||||||
completed,
|
{
|
||||||
duration,
|
completed,
|
||||||
},
|
duration,
|
||||||
activePlugin.id,
|
},
|
||||||
|
activePlugin.definition.id,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch((err) =>
|
||||||
|
console.error('Error while processing plugin message queue', err),
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,13 +276,11 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {activePlugin, pluginKey, target, pendingMessages, pluginIsEnabled} =
|
const {activePlugin, pluginKey, target, pendingMessages} = this.props;
|
||||||
this.props;
|
|
||||||
if (!activePlugin || !target || !pluginKey) {
|
if (!activePlugin || !target || !pluginKey) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (activePlugin.status !== 'enabled') {
|
||||||
if (!pluginIsEnabled) {
|
|
||||||
return this.renderPluginInfo();
|
return this.renderPluginInfo();
|
||||||
}
|
}
|
||||||
if (!pendingMessages || pendingMessages.length === 0) {
|
if (!pendingMessages || pendingMessages.length === 0) {
|
||||||
@@ -303,7 +308,7 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
<VBox>
|
<VBox>
|
||||||
<Label>
|
<Label>
|
||||||
Processing {this.state.progress.total} events for{' '}
|
Processing {this.state.progress.total} events for{' '}
|
||||||
{this.props.activePlugin?.id ?? 'plugin'}
|
{this.props.activePlugin?.details?.id ?? 'plugin'}
|
||||||
</Label>
|
</Label>
|
||||||
</VBox>
|
</VBox>
|
||||||
<VBox>
|
<VBox>
|
||||||
@@ -367,7 +372,12 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
isSandy,
|
isSandy,
|
||||||
latestInstalledVersion,
|
latestInstalledVersion,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
if (!activePlugin || !target || !pluginKey) {
|
if (
|
||||||
|
!activePlugin ||
|
||||||
|
!target ||
|
||||||
|
!pluginKey ||
|
||||||
|
activePlugin.status !== 'enabled'
|
||||||
|
) {
|
||||||
console.warn(`No selected plugin. Rendering empty!`);
|
console.warn(`No selected plugin. Rendering empty!`);
|
||||||
return this.renderNoPluginActive();
|
return this.renderNoPluginActive();
|
||||||
}
|
}
|
||||||
@@ -378,10 +388,13 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
!this.state.autoUpdateAlertSuppressed.has(
|
!this.state.autoUpdateAlertSuppressed.has(
|
||||||
`${latestInstalledVersion.name}@${latestInstalledVersion.version}`,
|
`${latestInstalledVersion.name}@${latestInstalledVersion.version}`,
|
||||||
) &&
|
) &&
|
||||||
semver.gt(latestInstalledVersion.version, activePlugin.version);
|
semver.gt(
|
||||||
if (isSandyPlugin(activePlugin)) {
|
latestInstalledVersion.version,
|
||||||
|
activePlugin.definition.version,
|
||||||
|
);
|
||||||
|
if (isSandyPlugin(activePlugin.definition)) {
|
||||||
// Make sure we throw away the container for different pluginKey!
|
// Make sure we throw away the container for different pluginKey!
|
||||||
const instance = target.sandyPluginStates.get(activePlugin.id);
|
const instance = target.sandyPluginStates.get(activePlugin.definition.id);
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
// happens if we selected a plugin that is not enabled on a specific app or not supported on a specific device.
|
// happens if we selected a plugin that is not enabled on a specific app or not supported on a specific device.
|
||||||
return this.renderNoPluginActive();
|
return this.renderNoPluginActive();
|
||||||
@@ -403,9 +416,9 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
key: pluginKey,
|
key: pluginKey,
|
||||||
logger: this.props.logger,
|
logger: this.props.logger,
|
||||||
selectedApp,
|
selectedApp,
|
||||||
persistedState: activePlugin.defaultPersistedState
|
persistedState: activePlugin.definition.defaultPersistedState
|
||||||
? {
|
? {
|
||||||
...activePlugin.defaultPersistedState,
|
...activePlugin.definition.defaultPersistedState,
|
||||||
...pluginState,
|
...pluginState,
|
||||||
}
|
}
|
||||||
: pluginState,
|
: pluginState,
|
||||||
@@ -438,8 +451,8 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
settingsState,
|
settingsState,
|
||||||
};
|
};
|
||||||
pluginElement = (
|
pluginElement = (
|
||||||
<TrackingScope scope={'plugin:' + activePlugin.id}>
|
<TrackingScope scope={'plugin:' + activePlugin.definition.id}>
|
||||||
{React.createElement(activePlugin, props)}
|
{React.createElement(activePlugin.definition, props)}
|
||||||
</TrackingScope>
|
</TrackingScope>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -450,7 +463,7 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
<Alert
|
<Alert
|
||||||
message={
|
message={
|
||||||
<Text>
|
<Text>
|
||||||
Plugin "{activePlugin.title}" v
|
Plugin "{activePlugin.definition.title}" v
|
||||||
{latestInstalledVersion?.version} is downloaded and ready to
|
{latestInstalledVersion?.version} is downloaded and ready to
|
||||||
install. <Link onClick={this.reloadPlugin}>Reload</Link> to
|
install. <Link onClick={this.reloadPlugin}>Reload</Link> to
|
||||||
start using the new version.
|
start using the new version.
|
||||||
@@ -475,7 +488,7 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
<Layout.Right>
|
<Layout.Right>
|
||||||
<ErrorBoundary
|
<ErrorBoundary
|
||||||
heading={`Plugin "${
|
heading={`Plugin "${
|
||||||
activePlugin.title || 'Unknown'
|
activePlugin.definition.title || 'Unknown'
|
||||||
}" encountered an error during render`}>
|
}" encountered an error during render`}>
|
||||||
<ContentContainer>{pluginElement}</ContentContainer>
|
<ContentContainer>{pluginElement}</ContentContainer>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
@@ -487,7 +500,7 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
<Container key="plugin">
|
<Container key="plugin">
|
||||||
<ErrorBoundary
|
<ErrorBoundary
|
||||||
heading={`Plugin "${
|
heading={`Plugin "${
|
||||||
activePlugin.title || 'Unknown'
|
activePlugin.definition.title || 'Unknown'
|
||||||
}" encountered an error during render`}>
|
}" encountered an error during render`}>
|
||||||
{pluginElement}
|
{pluginElement}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
@@ -499,54 +512,33 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||||
({
|
(state: Store) => {
|
||||||
connections: {
|
let pluginKey: string | null = null;
|
||||||
selectedPlugin,
|
let target: BaseDevice | Client | null = null;
|
||||||
selectedDevice,
|
const {
|
||||||
selectedApp,
|
connections: {selectedDevice, selectedApp, deepLinkPayload},
|
||||||
clients,
|
pluginStates,
|
||||||
deepLinkPayload,
|
plugins: {installedPlugins},
|
||||||
enabledPlugins,
|
pluginMessageQueue,
|
||||||
enabledDevicePlugins,
|
settingsState,
|
||||||
},
|
} = state;
|
||||||
pluginStates,
|
const selectedClient = getActiveClient(state);
|
||||||
plugins: {devicePlugins, clientPlugins, installedPlugins},
|
const activePlugin = getActivePlugin(state);
|
||||||
pluginMessageQueue,
|
if (activePlugin) {
|
||||||
settingsState,
|
if (selectedDevice && isDevicePlugin(activePlugin)) {
|
||||||
}) => {
|
|
||||||
let pluginKey = null;
|
|
||||||
let target = null;
|
|
||||||
let activePlugin: PluginDefinition | undefined;
|
|
||||||
let pluginIsEnabled = false;
|
|
||||||
|
|
||||||
if (selectedPlugin) {
|
|
||||||
activePlugin = devicePlugins.get(selectedPlugin);
|
|
||||||
if (selectedDevice && activePlugin) {
|
|
||||||
target = selectedDevice;
|
target = selectedDevice;
|
||||||
pluginKey = getPluginKey(selectedDevice.serial, activePlugin.id);
|
pluginKey = getPluginKey(
|
||||||
} else {
|
selectedDevice.serial,
|
||||||
target =
|
activePlugin.details.id,
|
||||||
clients.find((client: Client) => client.id === selectedApp) || null;
|
|
||||||
activePlugin = clientPlugins.get(selectedPlugin);
|
|
||||||
if (activePlugin && target) {
|
|
||||||
pluginKey = getPluginKey(target.id, activePlugin.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pluginIsEnabled =
|
|
||||||
activePlugin !== undefined &&
|
|
||||||
isPluginEnabled(
|
|
||||||
enabledPlugins,
|
|
||||||
enabledDevicePlugins,
|
|
||||||
selectedApp,
|
|
||||||
activePlugin.id,
|
|
||||||
);
|
);
|
||||||
|
} else if (selectedClient) {
|
||||||
|
target = selectedClient;
|
||||||
|
pluginKey = getPluginKey(selectedClient.id, activePlugin.details.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const isArchivedDevice = !selectedDevice
|
const isArchivedDevice = !selectedDevice
|
||||||
? false
|
? false
|
||||||
: selectedDevice.isArchived;
|
: selectedDevice.isArchived;
|
||||||
if (isArchivedDevice) {
|
|
||||||
pluginIsEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pendingMessages = pluginKey
|
const pendingMessages = pluginKey
|
||||||
? pluginMessageQueue[pluginKey]
|
? pluginMessageQueue[pluginKey]
|
||||||
@@ -554,17 +546,16 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
|||||||
|
|
||||||
const s: StateFromProps = {
|
const s: StateFromProps = {
|
||||||
pluginState: pluginStates[pluginKey as string],
|
pluginState: pluginStates[pluginKey as string],
|
||||||
activePlugin: activePlugin,
|
activePlugin,
|
||||||
target,
|
target,
|
||||||
deepLinkPayload,
|
deepLinkPayload,
|
||||||
pluginKey,
|
pluginKey,
|
||||||
isArchivedDevice,
|
isArchivedDevice,
|
||||||
selectedApp: selectedApp || null,
|
selectedApp: selectedApp || null,
|
||||||
pendingMessages,
|
pendingMessages,
|
||||||
pluginIsEnabled,
|
|
||||||
settingsState,
|
settingsState,
|
||||||
latestInstalledVersion: installedPlugins.get(
|
latestInstalledVersion: installedPlugins.get(
|
||||||
activePlugin?.packageName ?? '',
|
activePlugin?.details?.name ?? '',
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
return s;
|
return s;
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ class TestPlugin extends FlipperPlugin<any, any, any> {
|
|||||||
count: 0,
|
count: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static details = TestUtils.createMockPluginDetails({
|
||||||
|
id: 'TestPlugin',
|
||||||
|
});
|
||||||
|
|
||||||
static persistedStateReducer(
|
static persistedStateReducer(
|
||||||
persistedState: PersistedState,
|
persistedState: PersistedState,
|
||||||
method: string,
|
method: string,
|
||||||
@@ -636,7 +640,7 @@ test('PluginContainer can render Sandy device plugins', async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const definition = new _SandyPluginDefinition(
|
const definition = new _SandyPluginDefinition(
|
||||||
TestUtils.createMockPluginDetails(),
|
TestUtils.createMockPluginDetails({pluginType: 'device'}),
|
||||||
{
|
{
|
||||||
supportsDevice: () => true,
|
supportsDevice: () => true,
|
||||||
devicePlugin,
|
devicePlugin,
|
||||||
@@ -749,7 +753,7 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const definition = new _SandyPluginDefinition(
|
const definition = new _SandyPluginDefinition(
|
||||||
TestUtils.createMockPluginDetails(),
|
TestUtils.createMockPluginDetails({pluginType: 'device'}),
|
||||||
{
|
{
|
||||||
devicePlugin,
|
devicePlugin,
|
||||||
supportsDevice: () => true,
|
supportsDevice: () => true,
|
||||||
@@ -922,7 +926,7 @@ test('Sandy plugins support isPluginSupported + selectPlugin', async () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
const definition3 = new _SandyPluginDefinition(
|
const definition3 = new _SandyPluginDefinition(
|
||||||
TestUtils.createMockPluginDetails({id: 'device'}),
|
TestUtils.createMockPluginDetails({id: 'device', pluginType: 'device'}),
|
||||||
{
|
{
|
||||||
supportsDevice() {
|
supportsDevice() {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -248,15 +248,15 @@ describe('basic getActiveDevice with metro present', () => {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
unsupportedDevicePlugin.details,
|
unsupportedDevicePlugin.details,
|
||||||
"Device plugin 'Unsupported Device Plugin' is not supported by the currently connected device.",
|
"Device plugin 'Unsupported Device Plugin' is not supported by the selected device 'MockAndroidDevice' (Android)",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
unsupportedPlugin.details,
|
unsupportedPlugin.details,
|
||||||
"Plugin 'Unsupported Plugin' is not supported by the client application",
|
"Plugin 'Unsupported Plugin' is not supported by the selected application 'TestApp' (Android)",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
unsupportedDownloadablePlugin,
|
unsupportedDownloadablePlugin,
|
||||||
"Plugin 'Unsupported Uninstalled Plugin' is not supported by the client application and not installed in Flipper",
|
"Plugin 'Unsupported Uninstalled Plugin' is not supported by the selected application 'TestApp' (Android) and not installed in Flipper",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
downloadablePlugins: [supportedDownloadablePlugin],
|
downloadablePlugins: [supportedDownloadablePlugin],
|
||||||
|
|||||||
@@ -7,33 +7,16 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
|
||||||
PluginDetails,
|
|
||||||
DownloadablePluginDetails,
|
|
||||||
BundledPluginDetails,
|
|
||||||
} from 'flipper-plugin-lib';
|
|
||||||
import MetroDevice from '../devices/MetroDevice';
|
import MetroDevice from '../devices/MetroDevice';
|
||||||
import {
|
|
||||||
DevicePluginDefinition,
|
|
||||||
ClientPluginDefinition,
|
|
||||||
PluginDefinition,
|
|
||||||
} from '../plugin';
|
|
||||||
import {State} from '../reducers';
|
import {State} from '../reducers';
|
||||||
import {
|
import {
|
||||||
computePluginLists,
|
computePluginLists,
|
||||||
computeExportablePlugins,
|
computeExportablePlugins,
|
||||||
|
computeActivePluginList,
|
||||||
} from '../utils/pluginUtils';
|
} from '../utils/pluginUtils';
|
||||||
import createSelector from './createSelector';
|
import createSelector from './createSelector';
|
||||||
|
|
||||||
export type PluginLists = {
|
const getSelectedPluginId = (state: State) => state.connections.selectedPlugin;
|
||||||
devicePlugins: DevicePluginDefinition[];
|
|
||||||
metroPlugins: DevicePluginDefinition[];
|
|
||||||
enabledPlugins: ClientPluginDefinition[];
|
|
||||||
disabledPlugins: PluginDefinition[];
|
|
||||||
unavailablePlugins: [plugin: PluginDetails, reason: string][];
|
|
||||||
downloadablePlugins: (DownloadablePluginDetails | BundledPluginDetails)[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSelectedApp = (state: State) =>
|
const getSelectedApp = (state: State) =>
|
||||||
state.connections.selectedApp || state.connections.userPreferredApp;
|
state.connections.selectedApp || state.connections.userPreferredApp;
|
||||||
const getSelectedDevice = (state: State) => state.connections.selectedDevice;
|
const getSelectedDevice = (state: State) => state.connections.selectedDevice;
|
||||||
@@ -135,3 +118,18 @@ export const getExportablePlugins = createSelector(
|
|||||||
getPluginLists,
|
getPluginLists,
|
||||||
computeExportablePlugins,
|
computeExportablePlugins,
|
||||||
);
|
);
|
||||||
|
export const getActivePluginList = createSelector(
|
||||||
|
getPluginLists,
|
||||||
|
computeActivePluginList,
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getActivePlugin = createSelector(
|
||||||
|
getSelectedPluginId,
|
||||||
|
getActivePluginList,
|
||||||
|
(pluginId, pluginList) => {
|
||||||
|
if (!pluginId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return pluginList[pluginId] ?? null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
@@ -21,12 +21,44 @@ import {_SandyPluginDefinition} from 'flipper-plugin';
|
|||||||
import type BaseDevice from '../devices/BaseDevice';
|
import type BaseDevice from '../devices/BaseDevice';
|
||||||
import type Client from '../Client';
|
import type Client from '../Client';
|
||||||
import type {
|
import type {
|
||||||
|
ActivatablePluginDetails,
|
||||||
BundledPluginDetails,
|
BundledPluginDetails,
|
||||||
DownloadablePluginDetails,
|
DownloadablePluginDetails,
|
||||||
PluginDetails,
|
PluginDetails,
|
||||||
} from 'flipper-plugin-lib';
|
} from 'flipper-plugin-lib';
|
||||||
import {getLatestCompatibleVersionOfEachPlugin} from '../dispatcher/plugins';
|
import {getLatestCompatibleVersionOfEachPlugin} from '../dispatcher/plugins';
|
||||||
import {PluginLists} from '../selectors/connections';
|
|
||||||
|
export type PluginLists = {
|
||||||
|
devicePlugins: DevicePluginDefinition[];
|
||||||
|
metroPlugins: DevicePluginDefinition[];
|
||||||
|
enabledPlugins: ClientPluginDefinition[];
|
||||||
|
disabledPlugins: PluginDefinition[];
|
||||||
|
unavailablePlugins: [plugin: PluginDetails, reason: string][];
|
||||||
|
downloadablePlugins: (DownloadablePluginDetails | BundledPluginDetails)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ActivePluginListItem =
|
||||||
|
| {
|
||||||
|
status: 'enabled';
|
||||||
|
details: ActivatablePluginDetails;
|
||||||
|
definition: PluginDefinition;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
status: 'disabled';
|
||||||
|
details: ActivatablePluginDetails;
|
||||||
|
definition: PluginDefinition;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
status: 'uninstalled';
|
||||||
|
details: DownloadablePluginDetails | BundledPluginDetails;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
status: 'unavailable';
|
||||||
|
details: PluginDetails;
|
||||||
|
reason: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ActivePluginList = Record<string, ActivePluginListItem | undefined>;
|
||||||
|
|
||||||
export const defaultEnabledBackgroundPlugins = ['Navigation']; // The navigation plugin is enabled always, to make sure the navigation features works
|
export const defaultEnabledBackgroundPlugins = ['Navigation']; // The navigation plugin is enabled always, to make sure the navigation features works
|
||||||
|
|
||||||
@@ -157,6 +189,16 @@ export function sortPluginsByName(
|
|||||||
return getPluginTitle(a) > getPluginTitle(b) ? 1 : -1;
|
return getPluginTitle(a) > getPluginTitle(b) ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isDevicePlugin(activePlugin: ActivePluginListItem) {
|
||||||
|
if (activePlugin.details.pluginType === 'device') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
(activePlugin.status === 'enabled' || activePlugin.status === 'disabled') &&
|
||||||
|
isDevicePluginDefinition(activePlugin.definition)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function isDevicePluginDefinition(
|
export function isDevicePluginDefinition(
|
||||||
definition: PluginDefinition,
|
definition: PluginDefinition,
|
||||||
): definition is DevicePluginDefinition {
|
): definition is DevicePluginDefinition {
|
||||||
@@ -239,7 +281,9 @@ export function computePluginLists(
|
|||||||
p.details,
|
p.details,
|
||||||
`Device plugin '${getPluginTitle(
|
`Device plugin '${getPluginTitle(
|
||||||
p.details,
|
p.details,
|
||||||
)}' is not supported by the currently connected device.`,
|
)}' is not supported by the selected device '${device.title}' (${
|
||||||
|
device.os
|
||||||
|
})`,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -290,7 +334,9 @@ export function computePluginLists(
|
|||||||
plugin.details,
|
plugin.details,
|
||||||
`Plugin '${getPluginTitle(
|
`Plugin '${getPluginTitle(
|
||||||
plugin.details,
|
plugin.details,
|
||||||
)}' is not supported by the client application`,
|
)}' is not supported by the selected application '${
|
||||||
|
client.query.app
|
||||||
|
}' (${client.query.os})`,
|
||||||
]);
|
]);
|
||||||
} else if (favoritePlugins.includes(plugin)) {
|
} else if (favoritePlugins.includes(plugin)) {
|
||||||
enabledPlugins.push(plugin);
|
enabledPlugins.push(plugin);
|
||||||
@@ -312,13 +358,21 @@ export function computePluginLists(
|
|||||||
.forEach((plugin) => {
|
.forEach((plugin) => {
|
||||||
unavailablePlugins.push([
|
unavailablePlugins.push([
|
||||||
plugin,
|
plugin,
|
||||||
`Plugin '${getPluginTitle(
|
`Plugin '${getPluginTitle(plugin)}' is not supported by the selected ${
|
||||||
plugin,
|
plugin.pluginType === 'device' ? 'device' : 'application'
|
||||||
)}' is not supported by the client application and not installed in Flipper`,
|
} '${
|
||||||
|
(plugin.pluginType === 'device'
|
||||||
|
? device?.title
|
||||||
|
: client?.query.app) ?? 'unknown'
|
||||||
|
}' (${
|
||||||
|
plugin.pluginType === 'device' ? device?.os : client?.query.os
|
||||||
|
}) and not installed in Flipper`,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
enabledPlugins.sort(sortPluginsByName);
|
||||||
devicePlugins.sort(sortPluginsByName);
|
devicePlugins.sort(sortPluginsByName);
|
||||||
|
disabledPlugins.sort(sortPluginsByName);
|
||||||
metroPlugins.sort(sortPluginsByName);
|
metroPlugins.sort(sortPluginsByName);
|
||||||
unavailablePlugins.sort(([a], [b]) => {
|
unavailablePlugins.sort(([a], [b]) => {
|
||||||
return getPluginTitle(a) > getPluginTitle(b) ? 1 : -1;
|
return getPluginTitle(a) > getPluginTitle(b) ? 1 : -1;
|
||||||
@@ -359,3 +413,48 @@ function getFavoritePlugins(
|
|||||||
return idx === -1 ? !returnFavoredPlugins : returnFavoredPlugins;
|
return idx === -1 ? !returnFavoredPlugins : returnFavoredPlugins;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function computeActivePluginList({
|
||||||
|
enabledPlugins,
|
||||||
|
devicePlugins,
|
||||||
|
disabledPlugins,
|
||||||
|
downloadablePlugins,
|
||||||
|
unavailablePlugins,
|
||||||
|
}: PluginLists) {
|
||||||
|
const pluginList: ActivePluginList = {};
|
||||||
|
for (const plugin of enabledPlugins) {
|
||||||
|
pluginList[plugin.id] = {
|
||||||
|
status: 'enabled',
|
||||||
|
details: plugin.details,
|
||||||
|
definition: plugin,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for (const plugin of devicePlugins) {
|
||||||
|
pluginList[plugin.id] = {
|
||||||
|
status: 'enabled',
|
||||||
|
details: plugin.details,
|
||||||
|
definition: plugin,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for (const plugin of disabledPlugins) {
|
||||||
|
pluginList[plugin.id] = {
|
||||||
|
status: 'disabled',
|
||||||
|
details: plugin.details,
|
||||||
|
definition: plugin,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for (const plugin of downloadablePlugins) {
|
||||||
|
pluginList[plugin.id] = {
|
||||||
|
status: 'uninstalled',
|
||||||
|
details: plugin,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for (const [plugin, reason] of unavailablePlugins) {
|
||||||
|
pluginList[plugin.id] = {
|
||||||
|
status: 'unavailable',
|
||||||
|
details: plugin,
|
||||||
|
reason,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return pluginList;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user