Summary: This diff is part of the bigger task T60496135 This diff changes the RN support from crude to decent citizen, making sure we don't recycle callbacks over the bridge, use subscriptions were possible, and making sure connecting, disconnecting, etc works correctly For example, connect and disconnect hooks should work. Finally, throw in hot reloading into the mix, which causes the registerPlugin to be triggered another time, without the old one every been unloaded. This should trigger a new 'onConnect' on the client, to make sure it can restore any state / subscriptions necessary, even though the never disappeared in the Java world. These cases should all be handled well. Reviewed By: jknoxville Differential Revision: D19347330 fbshipit-source-id: de64a08f4043f01528c794430ccc3c717abf0180
129 lines
3.2 KiB
JavaScript
129 lines
3.2 KiB
JavaScript
/**
|
|
* 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
|
|
*/
|
|
|
|
// $FlowFixMe
|
|
import {NativeModules, NativeEventEmitter} from 'react-native';
|
|
|
|
const {Flipper} = NativeModules;
|
|
|
|
export default Flipper;
|
|
|
|
const listeners = {}; // plugin#method -> callback
|
|
const plugins = {}; // plugin -> Plugin
|
|
|
|
class Connection {
|
|
connected;
|
|
pluginId;
|
|
|
|
constructor(pluginId) {
|
|
this.connected = false;
|
|
this.pluginId = pluginId;
|
|
}
|
|
|
|
send(method, data) {
|
|
if (!this.connected) {
|
|
throw new Error('Cannot send data, not connected');
|
|
}
|
|
Flipper.send(this.pluginId, method, JSON.stringify(data));
|
|
}
|
|
|
|
reportErrorWithMetadata(reason, stackTrace) {
|
|
Flipper.reportErrorWithMetadata(this.pluginId, reason, stackTrace);
|
|
}
|
|
|
|
reportError(error) {
|
|
Flipper.reportError(this.pluginId, error);
|
|
}
|
|
|
|
receive(method, listener) {
|
|
if (!this.connected) {
|
|
throw new Error('Cannot receive data, not connected');
|
|
}
|
|
|
|
listeners[this.pluginId + '#' + method] = listener;
|
|
Flipper.subscribe(this.pluginId, method);
|
|
}
|
|
}
|
|
|
|
class Responder {
|
|
responderId;
|
|
|
|
constructor(responderId) {
|
|
this.responderId = responderId;
|
|
}
|
|
|
|
success(response) {
|
|
Flipper.respondSuccess(
|
|
this.responderId,
|
|
response == null ? null : JSON.stringify(response),
|
|
);
|
|
}
|
|
|
|
error(response) {
|
|
Flipper.respondError(this.responderId, JSON.stringify(response));
|
|
}
|
|
}
|
|
|
|
function startEventListeners() {
|
|
const emitter = new NativeEventEmitter(Flipper);
|
|
emitter.removeAllListeners('react-native-flipper-plugin-connect');
|
|
emitter.removeAllListeners('react-native-flipper-plugin-disconnect');
|
|
emitter.removeAllListeners('react-native-flipper-receive-event');
|
|
|
|
emitter.addListener('react-native-flipper-plugin-connect', event => {
|
|
const {plugin} = event;
|
|
if (plugins[plugin]) {
|
|
const p = plugins[plugin];
|
|
p._connection.connected = true;
|
|
p.onConnect(p._connection);
|
|
}
|
|
});
|
|
|
|
emitter.addListener('react-native-flipper-plugin-disconnect', event => {
|
|
const {plugin} = event;
|
|
if (plugins[plugin]) {
|
|
const p = plugins[plugin];
|
|
p._connection.connected = false;
|
|
p.onDisconnect();
|
|
}
|
|
});
|
|
|
|
emitter.addListener('react-native-flipper-receive-event', event => {
|
|
const {plugin, method, params, responderId} = event;
|
|
const key = plugin + '#' + method;
|
|
if (listeners[key]) {
|
|
const responder = new Responder(responderId);
|
|
listeners[key](JSON.parse(params), responder);
|
|
}
|
|
});
|
|
}
|
|
|
|
// $FlowFixMe
|
|
export function addPlugin(plugin) {
|
|
if (!plugin || typeof plugin !== 'object') {
|
|
throw new Error('Expected plugin, got ' + plugin);
|
|
}
|
|
['getId', 'onConnect', 'onDisconnect'].forEach(method => {
|
|
if (typeof plugin[method] !== 'function') {
|
|
throw new Error(`Plugin misses an implementation for '${method}'`);
|
|
}
|
|
});
|
|
const runInBackground =
|
|
typeof plugin.runInBackground === 'function'
|
|
? !!plugin.runInBackground()
|
|
: false;
|
|
const id = plugin.getId();
|
|
plugin._connection = new Connection(id);
|
|
plugins[id] = plugin;
|
|
|
|
Flipper.registerPlugin(id, runInBackground);
|
|
}
|
|
|
|
startEventListeners();
|