Add JS Plugin API reference page
Summary: This documents all pretty much the entire API. Reviewed By: passy Differential Revision: D15187834 fbshipit-source-id: 073c6a0dc40e3373967e9e5c5b7d44ab9c0aa07f
This commit is contained in:
committed by
Facebook Github Bot
parent
49be7fd35f
commit
57af6fa2bf
98
docs/extending/js-plugin-api.md
Normal file
98
docs/extending/js-plugin-api.md
Normal file
@@ -0,0 +1,98 @@
|
||||
---
|
||||
id: js-plugin-api
|
||||
title: JavaScript Plugin API
|
||||
---
|
||||
|
||||
Provided a plugin is setup as defined in [JS Plugin Definiton](js-setup), the basic requirement of a Flipper plugin is that `index.js` exports a default class that extends `FlipperPlugin`.
|
||||
|
||||
`FlipperPlugin` is an extension of `React.Component` with extra Flipper-related functionality. This means to define the UI of your plugin, you just need to implement this React component.
|
||||
|
||||
Below is a reference of the APIs available to the `FlipperPlugin` class.
|
||||
|
||||
## Client
|
||||
|
||||
This object is provided for communicating with the client plugin, and is accessible using `this.client` inside `FlipperPlugin`. Methods called on it will be routed to the client plugin with the same identifier as the JS plugin.
|
||||
|
||||
### call
|
||||
`client.call(method: string, params: Object): Promise<Object>`
|
||||
|
||||
Call a method on your client plugin implementation.
|
||||
|
||||
### subscribe
|
||||
`client.subscribe(method: string, callback: (Object => void)): void`
|
||||
|
||||
Subscribe to messages sent proactively from the client plugin.
|
||||
|
||||
### supportsMethod
|
||||
`client.supportsMethod(method: string): Promise<Boolean>`
|
||||
|
||||
Resolves to true if the client supports the specified method. Useful when adding functionality to existing plugins, when connectivity to older clients is still required. Also useful when client plugins are implemented on multitple platforms and don't all have feature parity.
|
||||
|
||||
### send (DEPRECATED)
|
||||
`client.send(method, params): void`
|
||||
|
||||
Use call instead which allows error handling and tracking.
|
||||
|
||||
## Props
|
||||
|
||||
Since `FlipperClient` inherits from `React.Component` we've defined some props that are provided. The main ones are explained below. Consult the code for the full set.
|
||||
|
||||
### persistedState
|
||||
As well as React state, a FlipperPlugin also has persisted state. This state is retained even when the plugin is not active, for example when the user is using a different plugin, or when a client is temporarily disconnected, however it is not persisted across restarts of Flipper (by default).
|
||||
|
||||
Like React state, it should **never** be modified directly. Instead, you should use the `setPersistedState` prop.
|
||||
|
||||
If using persisted state, make sure to set a **static** `defaultPersistedState` in your class, so that the state is correctly initialized.
|
||||
|
||||
`static defaultPersistedState = {myValue: 55};`
|
||||
|
||||
### setPersistedState
|
||||
A callback for updating persisted state. Similar to React's `setState`, you can pass either a complete PersistedState or a partial one that will be merged with the current persisted state.
|
||||
|
||||
Persisted state can also be modified when a plugin is not active. See [Background Plugins](#background-plugins) for details.
|
||||
|
||||
### selectPlugin
|
||||
A callback for deep-linking to another plugin. When called, Flipper will switch from the current active plugin to the one specified and include a payload to provide context for the receiving plugin.
|
||||
|
||||
### deepLinkPayload
|
||||
When a plugin is activated through a deep-link, this prop will contain the payload, allowing the plugin to highlight some particular data, or perform an action for example. A good time to check for the deepLinkPayload is in the `componentDidMount` React callback.
|
||||
|
||||
### isArchivedDevice
|
||||
Informs the plugin whether or not the client is archived, and therefore not currently connected.
|
||||
|
||||
## Background Plugins
|
||||
|
||||
Sometimes it's desirable for a plugin to be able to process incoming messages from the client even when inactive.
|
||||
|
||||
To do this, define a static `persistedStateReducer` function in the plugin class:
|
||||
```
|
||||
static persistedStateReducer(
|
||||
persistedState: PersistedState,
|
||||
method: string,
|
||||
data: Object
|
||||
): PersistedState
|
||||
```
|
||||
|
||||
The job of the `persistedStateReducer` is to merge incoming data into the state, so that next time the plugin is activated, the persisted state will be ready.
|
||||
|
||||
## Notifications
|
||||
|
||||
Plugins can publish system notifications to alert the user of something. This is particularly useful when the plugin isn't the current active plugin. All notifications are aggregated in Flipper's notifications pane, accessible from the sidebar.
|
||||
|
||||
A notification should provide actionable and high-signal information for important events the user is likely to take action on. Notifications are generated from the data in your persistedState. To trigger notifications you need to implement a static function `getActiveNotifications`. This function should return all currently active notifications. To invalidate a notification, you simply stop including it in the result.
|
||||
```
|
||||
static getActiveNotifications(
|
||||
persistedState: PersistedState
|
||||
): Array<Notification>
|
||||
```
|
||||
|
||||
When the user clicks on a notification, they will be sent back to your plugin with the [deepLinkPayload](#deeplinkpayload) equal to the notification's action.
|
||||
|
||||
## Type Parameters
|
||||
`FlipperPlugin<S, A, P>` can optionally take the following type parameters. It is highly recommended you provide them to benefit from type safety, but you can pass `*` when not using these features.
|
||||
|
||||
**State**: Specifies the type of the FlipperPlugin state. A `FlipperPlugin` is a React component, and this is equivalent to the React state type parameter.
|
||||
|
||||
**Actions**: `FlipperPlugin` has an infrequently used dispatchAction mechanism allowing your plugin dispatch actions and reduce state in a redux-like manner. This specifies the type of actions that can be dispatched.
|
||||
|
||||
**PersistedState**: This specifies the type of the persisted state of the plugin.
|
||||
@@ -3,11 +3,11 @@ id: js-setup
|
||||
title: JavaScript Plugin Definition
|
||||
---
|
||||
|
||||
All JavaScript Flipper plugins consist of a directory. This directory must contain the following two files:
|
||||
All JavaScript Flipper plugins must be self-contained in a directory. This directory must contain at a minimum the following two files:
|
||||
* package.json
|
||||
* index.js
|
||||
|
||||
To create the desktop part of your plugin, initiate a new JavaScript project using `yarn init` and make sure your package name is the same as the identifier of the client plugin. Create a file called `index.js`, which is the entry point to your plugin. An example `package.json` file could look like this:
|
||||
The best way to initialize a JS plugin is to create a directory, and run `yarn init` inside it. Make sure your package name is the same as the identifier of the client plugin. After that create an `index.js` file which will be the entry point to your plugin. An example `package.json` file could look like this:
|
||||
|
||||
Example `package.json`:
|
||||
```
|
||||
@@ -35,7 +35,18 @@ Important attributes of `package.json`:
|
||||
|
||||
`bugs` Specify an email and/or url, where plugin bugs should be reported.
|
||||
|
||||
In `index.js` you can now create your plugin. Take a look at [Writing a plugin](writing-a-plugin.md) to see what this code looks like. Also, make sure to check out the [Flipper UI Component Library](ui-components.md) for lots of pre-made components.
|
||||
In `index.js` you will define the plugin in JavaScript. This file must export a default class that extends `FlipperPlugin`. Browse our [JS API docs](js-plugin-api) to see what you can do, and make sure to check out our [UI Component Library](ui-components.md) for lots of pre-made components.
|
||||
|
||||
Example `index.js`:
|
||||
```js
|
||||
import {FlipperPlugin} from 'flipper';
|
||||
|
||||
export default class extends FlipperPlugin {
|
||||
render() {
|
||||
return 'hello world';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Dynamically loading plugins
|
||||
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
---
|
||||
id: writing-a-plugin
|
||||
title: Writing a plugin in JavaScript
|
||||
---
|
||||
|
||||
Every plugin needs to be self-contained in its own folder. At the root-level of this folder we are expecting a `package.json` for your plugin and an `index.js` file which is the entry-point to your plugin. To learn more about the basic setup of a plugin, have a look at [JavaScript Setup](jssetup.md).
|
||||
|
||||
|
||||
We expect this file to have a default export of type `FlipperPlugin`. A hello-world-plugin could look like this:
|
||||
|
||||
```js
|
||||
import {FlipperPlugin} from 'flipper';
|
||||
|
||||
export default class extends FlipperPlugin {
|
||||
render() {
|
||||
return 'hello world';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## persistedState
|
||||
|
||||
React state will be gone once your plugin unmounts. Every time the user switches between plugins, the React state will be reset. This might be fine for UI state, but doesn't make sense for your plugin's data. To persist your data when switching between plugins, you can use our `persistedState`-API.
|
||||
|
||||
Flipper passes a prop with the current `persistedState` to your plugin. You can access it via `this.props.persistedState`. To changes values in the `persistedState` you call `this.setPersistedState({...})`. Our API works similar to React's state API. You can pass a partial state object to `setPersistedState` and it will be merged with the previous state. You can also define a `static defaultPersistedState` to populate it.
|
||||
|
||||
A common pattern for plugins is to receive data from the client and store this data in `persistedState`. To allow your plugin to receive this data, even when the UI is not visible, you can implement a `static persistedStateReducer`. This reducer's job is to merge the current `persistedState` with the data received from the device. Flipper will call this method, even when your plugin is not visible and the next time your plugin is mounted, `persistedState` will already have the latest data.
|
||||
|
||||
```js
|
||||
static defaultPersistedState = {
|
||||
myData: [],
|
||||
}
|
||||
|
||||
static persistedStateReducer = (
|
||||
persistedState: PersistedState,
|
||||
method: string,
|
||||
newData: Object,
|
||||
): PersistedState => {
|
||||
// Logic to merge current state with new data
|
||||
return {
|
||||
myData: [...persistedState.myData, newData],
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Have a look at the [network plugin](https://github.com/facebook/flipper/blob/14e38c087f099a5afed4d7a1e4b5713468eabb28/src/plugins/network/index.js#L122) to see this in action. To send data while your plugin is not active, you need to opt-in on the native side of your plugin. For more info around this, read the mobile [setup](create-plugin.md).
|
||||
|
||||
## Notifications
|
||||
|
||||
Plugins can publish notifications which are displayed in Flipper's notification panel and trigger system level notifications. This can be used by your plugin to make the user aware of something that happened inside the plugin, even when the user is not looking at the plugin right now. The network plugin uses this for failed network requests. All notifications are aggregated in Flipper's notifications pane, accessible from the sidebar.
|
||||
|
||||
A notification should provide actionable and high-signal information for important events the user is likely to take action on. Notifications are generated from the data in your `persistedState`. To trigger notifications you need to implement a static function called `getActiveNotifications`. This function returns an array of all currently active notifications. To invalidate a notification, you simply stop including this notification in the array you are returning.
|
||||
|
||||
```js
|
||||
type Notification = {|
|
||||
id: string, // used to identify your notification and needs to be unique to your plugin
|
||||
title: string, // title shown in the system-level notification
|
||||
message: string, // detailed information about the event
|
||||
severity: 'warning' | 'error',
|
||||
timestamp?: number, // unix timestamp of when the event occurred
|
||||
category?: string, // used to group similar notifications (not shown to the user)
|
||||
action?: string, // passed to your plugin when navigating from a notification back to the plugin
|
||||
|};
|
||||
|
||||
static getActiveNotifications = (
|
||||
persistedState: PersistedState,
|
||||
): Array<Notification> => {
|
||||
return persistedState.myData.filter(d => d.error).map(d => {
|
||||
id: d.id,
|
||||
title: 'Something is rotten in the state of Denmark',
|
||||
message: d.errorMessage,
|
||||
severity: 'error',
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
When the user clicks on a notification, it links back into your plugin. `this.props.deepLinkPayload` will be set to the string provided in the notification's `action`. This can be used to highlight the particular part of the data that triggered the notification. In many cases the `action` will be some sort of ID for your data. You can use React's `componentDidMount` to check if a `deepLinkPayload` is provided.
|
||||
Reference in New Issue
Block a user