update js-client api (migrate to TS)
Summary: JS/TS api: - migrate to TS - some refactoring (get rid of bridge, make client abstract) Implementation isn't full yet, things to be implemented: - let plugins connect on init command from Flipper - implement Responder Further plans: - make fully compatible with react-native api without breaking changes Reviewed By: mweststrate Differential Revision: D21839377 fbshipit-source-id: 9e9fe4ad01632f958b59eb255c703c6cbc5fafe2
This commit is contained in:
committed by
Facebook GitHub Bot
parent
f88d707dbb
commit
896a90aa26
147
flipper-js-client-sdk/src/api.ts
Normal file
147
flipper-js-client-sdk/src/api.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export type FlipperPluginID = string;
|
||||
|
||||
export type FlipperMethodID = string;
|
||||
|
||||
export class FlipperResponder {
|
||||
pluginId: FlipperPluginID;
|
||||
methodId: FlipperMethodID;
|
||||
private _client: FlipperClient;
|
||||
|
||||
constructor(
|
||||
pluginId: FlipperPluginID,
|
||||
methodId: FlipperMethodID,
|
||||
client: FlipperClient
|
||||
) {
|
||||
this.pluginId = pluginId;
|
||||
this.methodId = methodId;
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
success(_response: any) {}
|
||||
|
||||
error(_response: any) {}
|
||||
}
|
||||
|
||||
export type FlipperReceiver<T> = (
|
||||
params: T,
|
||||
responder: FlipperResponder,
|
||||
) => void;
|
||||
|
||||
export class FlipperConnection {
|
||||
pluginId: FlipperPluginID;
|
||||
private client: FlipperClient;
|
||||
|
||||
constructor(pluginId: FlipperPluginID, client: FlipperClient) {
|
||||
this.pluginId = pluginId;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
send(method: FlipperMethodID, data: any) {
|
||||
this.client.sendData(this.pluginId, method, data);
|
||||
}
|
||||
|
||||
receive<T>(method: FlipperMethodID, receiver: FlipperReceiver<T>) {
|
||||
this.client.subscribe(this.pluginId, method, (data: T) => {
|
||||
receiver(data, new FlipperResponder(this.pluginId, method, this.client));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface FlipperPlugin {
|
||||
/**
|
||||
* @return The id of this plugin. This is the namespace which Flipper desktop plugins will call
|
||||
* methods on to route them to your plugin. This should match the id specified in your React
|
||||
* plugin.
|
||||
*/
|
||||
getId(): string;
|
||||
|
||||
/**
|
||||
* Called when a connection has been established. The connection passed to this method is valid
|
||||
* until {@link FlipperPlugin#onDisconnect()} is called.
|
||||
*/
|
||||
onConnect(connection: FlipperConnection): void;
|
||||
|
||||
/**
|
||||
* Called when the connection passed to `FlipperPlugin#onConnect(FlipperConnection)` is no
|
||||
* longer valid. Do not try to use the connection in or after this method has been called.
|
||||
*/
|
||||
onDisconnect(): void;
|
||||
|
||||
/**
|
||||
* Returns true if the plugin is meant to be run in background too, otherwise it returns false.
|
||||
*/
|
||||
runInBackground(): boolean;
|
||||
}
|
||||
|
||||
export abstract class AbstractFlipperPlugin implements FlipperPlugin{
|
||||
protected connection: FlipperConnection | null | undefined;
|
||||
|
||||
onConnect(connection: FlipperConnection): void {
|
||||
this.connection = connection;
|
||||
}
|
||||
onDisconnect(): void {
|
||||
this.connection = null;
|
||||
}
|
||||
|
||||
abstract getId(): string;
|
||||
abstract runInBackground(): boolean;
|
||||
|
||||
}
|
||||
|
||||
export abstract class FlipperClient {
|
||||
_isConnected: boolean = false;
|
||||
plugins: Map<FlipperPluginID, FlipperPlugin> = new Map();
|
||||
|
||||
addPlugin(plugin: FlipperPlugin) {
|
||||
if (this._isConnected) {
|
||||
plugin.onConnect(new FlipperConnection(plugin.getId(), this));
|
||||
}
|
||||
this.plugins.set(plugin.getId(), plugin);
|
||||
}
|
||||
|
||||
getPlugin(id: FlipperPluginID): FlipperPlugin | undefined {
|
||||
return this.plugins.get(id);
|
||||
}
|
||||
|
||||
onConnect() {
|
||||
if (this._isConnected) {
|
||||
return;
|
||||
}
|
||||
this._isConnected = true;
|
||||
Array.from(this.plugins.values()).map((plugin) =>
|
||||
plugin.onConnect(new FlipperConnection(plugin.getId(), this)),
|
||||
);
|
||||
}
|
||||
|
||||
onDisconnect() {
|
||||
this._isConnected = false;
|
||||
Array.from(this.plugins.values()).map((plugin) => plugin.onDisconnect());
|
||||
}
|
||||
|
||||
abstract start: (appName: string) => void;
|
||||
|
||||
abstract stop: () => void;
|
||||
|
||||
abstract sendData: (
|
||||
plugin: FlipperPluginID,
|
||||
method: FlipperMethodID,
|
||||
data: any,
|
||||
) => void;
|
||||
|
||||
abstract subscribe: <T>(
|
||||
plugin: FlipperPluginID,
|
||||
method: FlipperMethodID,
|
||||
handler: (message: T) => void,
|
||||
) => void;
|
||||
|
||||
abstract isAvailable: () => boolean;
|
||||
}
|
||||
50
flipper-js-client-sdk/src/example.ts
Normal file
50
flipper-js-client-sdk/src/example.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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 {FlipperClient, AbstractFlipperPlugin} from './api';
|
||||
import {newWebviewClient} from './webviewImpl';
|
||||
|
||||
class SeaMammalPlugin extends AbstractFlipperPlugin {
|
||||
getId(): string {
|
||||
return 'sea-mammals';
|
||||
}
|
||||
|
||||
runInBackground(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
newRow(row: {id: string, url: string, title: string}) {
|
||||
this.connection?.send("newRow", row)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FlipperManager {
|
||||
flipperClient: FlipperClient;
|
||||
seaMammalPlugin: SeaMammalPlugin;
|
||||
|
||||
constructor() {
|
||||
this.flipperClient = newWebviewClient();
|
||||
this.seaMammalPlugin = new SeaMammalPlugin();
|
||||
this.flipperClient.addPlugin(this.seaMammalPlugin);
|
||||
this.flipperClient.start('Example JS App');
|
||||
}
|
||||
}
|
||||
|
||||
let flipperManager: FlipperManager | undefined;
|
||||
|
||||
export function init() {
|
||||
if (!flipperManager) {
|
||||
flipperManager = new FlipperManager();
|
||||
}
|
||||
}
|
||||
|
||||
export function flipper(): FlipperManager | undefined {
|
||||
return flipperManager;
|
||||
}
|
||||
10
flipper-js-client-sdk/src/index.ts
Normal file
10
flipper-js-client-sdk/src/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export * from './api';
|
||||
58
flipper-js-client-sdk/src/webviewImpl.ts
Normal file
58
flipper-js-client-sdk/src/webviewImpl.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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 {FlipperClient} from './api';
|
||||
|
||||
import type {FlipperPluginID, FlipperMethodID} from './api';
|
||||
|
||||
class FlipperWebviewClient extends FlipperClient {
|
||||
_subscriptions: Map<string, (message: any) => void> = new Map();
|
||||
_client: FlipperClient | null = null;
|
||||
|
||||
start = (appName: string) => {
|
||||
const bridge = (window as any).FlipperWebviewBridge;
|
||||
bridge?.registerPlugins(this.plugins);
|
||||
bridge?.start(appName);
|
||||
};
|
||||
|
||||
stop = () => {
|
||||
const bridge = (window as any).FlipperWebviewBridge;
|
||||
bridge?.FlipperWebviewBridge.stop();
|
||||
};
|
||||
|
||||
sendData = (plugin: FlipperPluginID, method: FlipperMethodID, data: any) => {
|
||||
const bridge = (window as any).FlipperWebviewBridge;
|
||||
bridge && bridge.sendFlipperObject(plugin, method, JSON.stringify(data));
|
||||
};
|
||||
|
||||
subscribe = (
|
||||
plugin: FlipperPluginID,
|
||||
method: FlipperMethodID,
|
||||
handler: (msg: any) => void,
|
||||
) => {
|
||||
this._subscriptions.set(plugin + method, handler);
|
||||
};
|
||||
|
||||
isAvailable = () => {
|
||||
return (window as any).FlipperWebviewBridge != null;
|
||||
};
|
||||
|
||||
receive(plugin: FlipperPluginID, method: FlipperMethodID, data: string) {
|
||||
const handler = this._subscriptions.get(plugin + method);
|
||||
handler && handler(JSON.parse(data));
|
||||
}
|
||||
|
||||
setClient(client: FlipperClient) {
|
||||
this._client = client;
|
||||
}
|
||||
}
|
||||
|
||||
export function newWebviewClient(): FlipperClient {
|
||||
return new FlipperWebviewClient();
|
||||
}
|
||||
Reference in New Issue
Block a user