Summary:
This diff addresses two problems:
1. Since clients plugins can be active beyond having a connection, we have to make it possible for plugin authors to check if they are connected before they make a call.
2. if there is a custom `exportPersistedState`, plugins should be able to skip making calls if the device has disconnected.
Introducing this change makes it possible to interact with a reasonable level with disconnected clients, and makes it possible to create Flipper traces for disconnected clients.
Note that both items were already problems before supporting offline clients; as there can be a noticeable delay between disconnecting and Flipper detecting that (i've seen up to 30 secs). What happend previously in those cases is that the export would simply hang, as would other user interactions, as loosing the connection in the middle of a process would cause the promise chains to be neither rejected or resolved, which is pretty iffy.
Before this diff, trying to export a disconnected device would hang forever like:
{F369600601}
Reviewed By: nikoant
Differential Revision: D26250895
fbshipit-source-id: 177624a116883c3cba14390cd0fe164e243bb97c
123 lines
3.6 KiB
TypeScript
123 lines
3.6 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 {ActivatablePluginDetails} from 'flipper-plugin-lib';
|
|
import {PluginFactory, FlipperPluginComponent} from './Plugin';
|
|
import {DevicePluginPredicate, DevicePluginFactory} from './DevicePlugin';
|
|
|
|
/**
|
|
* FlipperPluginModule describe the exports that are provided by a typical Flipper Desktop plugin
|
|
*/
|
|
export type FlipperDevicePluginModule = {
|
|
/** predicate that determines if this plugin applies to the currently selcted device */
|
|
supportsDevice: DevicePluginPredicate;
|
|
/** the factory function that initializes a plugin instance */
|
|
devicePlugin: DevicePluginFactory;
|
|
/** the component type that can render this plugin */
|
|
Component: FlipperPluginComponent;
|
|
};
|
|
|
|
/**
|
|
* FlipperPluginModule describe the exports that are provided by a typical Flipper Desktop plugin
|
|
*/
|
|
export type FlipperPluginModule<Factory extends PluginFactory<any, any>> = {
|
|
/** the factory function that initializes a plugin instance */
|
|
plugin: Factory;
|
|
/** the component type that can render this plugin */
|
|
Component: FlipperPluginComponent;
|
|
};
|
|
|
|
/**
|
|
* A sandy plugin definition represents a loaded plugin definition, storing two things:
|
|
* the loaded JS module, and the meta data (typically coming from package.json).
|
|
*
|
|
* Also delegates some of the standard plugin functionality to have a similar public static api as FlipperPlugin
|
|
*/
|
|
export class SandyPluginDefinition {
|
|
id: string;
|
|
module: FlipperPluginModule<any> | FlipperDevicePluginModule;
|
|
details: ActivatablePluginDetails;
|
|
isDevicePlugin: boolean;
|
|
|
|
constructor(
|
|
details: ActivatablePluginDetails,
|
|
module: FlipperPluginModule<any> | FlipperDevicePluginModule,
|
|
);
|
|
constructor(details: ActivatablePluginDetails, module: any) {
|
|
this.id = details.id;
|
|
this.details = details;
|
|
if (module.supportsDevice) {
|
|
// device plugin
|
|
this.isDevicePlugin = true;
|
|
if (!module.devicePlugin || typeof module.devicePlugin !== 'function') {
|
|
throw new Error(
|
|
`Flipper device plugin '${this.id}' should export named function called 'devicePlugin'`,
|
|
);
|
|
}
|
|
} else {
|
|
this.isDevicePlugin = false;
|
|
if (!module.plugin || typeof module.plugin !== 'function') {
|
|
throw new Error(
|
|
`Flipper plugin '${this.id}' should export named function called 'plugin'`,
|
|
);
|
|
}
|
|
}
|
|
if (!module.Component || typeof module.Component !== 'function') {
|
|
throw new Error(
|
|
`Flipper plugin '${this.id}' should export named function called 'Component'`,
|
|
);
|
|
}
|
|
this.module = module;
|
|
this.module.Component.displayName = `FlipperPlugin(${this.id})`;
|
|
}
|
|
|
|
asDevicePluginModule(): FlipperDevicePluginModule {
|
|
if (!this.isDevicePlugin) throw new Error('Not a device plugin');
|
|
return this.module as FlipperDevicePluginModule;
|
|
}
|
|
|
|
asPluginModule(): FlipperPluginModule<any> {
|
|
if (this.isDevicePlugin) throw new Error('Not an application plugin');
|
|
return this.module as FlipperPluginModule<any>;
|
|
}
|
|
|
|
get packageName() {
|
|
return this.details.name;
|
|
}
|
|
|
|
get title() {
|
|
return this.details.title;
|
|
}
|
|
|
|
get icon() {
|
|
return this.details.icon;
|
|
}
|
|
|
|
get category() {
|
|
return this.details.category;
|
|
}
|
|
|
|
get gatekeeper() {
|
|
return this.details.gatekeeper;
|
|
}
|
|
|
|
get version() {
|
|
return this.details.version;
|
|
}
|
|
|
|
get isBundled() {
|
|
return this.details.isBundled;
|
|
}
|
|
|
|
get keyboardActions() {
|
|
// TODO: T68882551 support keyboard actions
|
|
return [];
|
|
}
|
|
}
|