From 6a9d54f93af7e9e109649ac795215886ac468fb9 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Sun, 4 Sep 2022 12:19:26 -0700 Subject: [PATCH] Plugin and Module implementations Summary: Last diff in the the RNW saga. This change creates the necessary bridgings between the module and the plugin. With this in place, JS plugins can be written and used. Also, we have a fully functional RNW sample app. Reviewed By: aigoncharov Differential Revision: D39087480 fbshipit-source-id: f4fde404716aa619a64553ffa556d060f49c0ac7 --- .../ReactNativeFlipper/FlipperModule.h | 65 +++++++- .../ReactNativeFlipper/FlipperReactPlugin.cpp | 59 +++++++ .../ReactNativeFlipper/FlipperReactPlugin.h | 52 ++++++ .../FlipperReactPluginManager.cpp | 151 ++++++++++++++++++ .../FlipperReactPluginManager.h | 60 +++++++ .../ReactNativeFlipper.vcxproj | 4 + .../ReactNativeFlipper.vcxproj.filters | 4 + 7 files changed, 388 insertions(+), 7 deletions(-) create mode 100644 react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPlugin.cpp create mode 100644 react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPlugin.h create mode 100644 react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPluginManager.cpp create mode 100644 react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPluginManager.h diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperModule.h b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperModule.h index 62a79ced9..03cfce3f4 100644 --- a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperModule.h +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperModule.h @@ -11,8 +11,11 @@ #include "NativeModules.h" #include +#include +#include #include #include +#include #include "../../../../xplat/Flipper/FlipperClient.h" #include "../../../../xplat/Flipper/FlipperInitConfig.h" #include "../../../../xplat/Flipper/FlipperScheduler.h" @@ -26,6 +29,7 @@ REACT_MODULE(FlipperModule, L"Flipper") struct FlipperModule { std::unique_ptr sonarScheduler; std::unique_ptr connectionScheduler; + std::unique_ptr pluginManager; REACT_INIT(Initialize) void Initialize(ReactContext const& reactContext) noexcept { @@ -36,6 +40,9 @@ struct FlipperModule { connectionScheduler = std::make_unique(); + pluginManager = + std::make_unique(); + facebook::flipper::FlipperReactDeviceInfo deviceInfo; facebook::flipper::FlipperInitConfig config; config.deviceData.host = deviceInfo.getHost(); @@ -60,29 +67,73 @@ struct FlipperModule { void registerPlugin( std::string pluginId, bool inBackground, - std::function&& callback) noexcept {} + std::function&& callback) noexcept { + bool registered = pluginManager->registerPlugin( + pluginId, + inBackground, + [this]( + const std::string& pluginId, + facebook::flipper::FlipperReactPluginEvent e) { + const std::map args{{"plugin", pluginId}}; + switch (e) { + case facebook::flipper::FlipperReactPluginEvent::CONNECTED: + return pluginOnConnect(args); + case facebook::flipper::FlipperReactPluginEvent::DISCONNECTED: + return pluginOnDisconnect(args); + } + }); + if (registered) { + callback("ok"); + } else { + callback("noflipper"); + } + } REACT_METHOD(send) void - send(std::string pluginId, std::string method, std::string data) noexcept {} + send(std::string pluginId, std::string method, std::string data) noexcept { + pluginManager->send(pluginId, method, data); + } REACT_METHOD(reportErrorWithMetadata) void reportErrorWithMetadata( std::string pluginId, std::string reason, - std::string stacktrace) noexcept {} + std::string stacktrace) noexcept { + pluginManager->reportError(pluginId, reason, stacktrace); + } REACT_METHOD(reportError) - void reportError(std::string pluginId, std::string error) noexcept {} + void reportError(std::string pluginId, std::string error) noexcept { + pluginManager->reportError(pluginId, error); + } REACT_METHOD(subscribe) - void subscribe(std::string pluginId, std::string method) noexcept {} + void subscribe(std::string pluginId, std::string method) noexcept { + pluginManager->subscribe( + pluginId, method, [this](std::map args) { + onReceive(args); + }); + } REACT_METHOD(respondSuccess) - void respondSuccess(std::string responderId, std::string data) noexcept {} + void respondSuccess(std::string responderId, std::string data) noexcept { + pluginManager->respondSuccess(responderId, data); + } REACT_METHOD(respondError) - void respondError(std::string responderId, std::string data) noexcept {} + void respondError(std::string responderId, std::string data) noexcept { + pluginManager->respondError(responderId, data); + } + + REACT_EVENT(pluginOnConnect, L"react-native-flipper-plugin-connect") + std::function)> pluginOnConnect; + + REACT_EVENT(pluginOnDisconnect, L"react-native-flipper-plugin-disconnect") + std::function)> pluginOnDisconnect; + + REACT_EVENT(onReceive, L"react-native-flipper-receive-event") + std::function)> onReceive; private: ReactContext m_reactContext{nullptr}; diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPlugin.cpp b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPlugin.cpp new file mode 100644 index 000000000..7d8e640c1 --- /dev/null +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPlugin.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "FlipperReactPlugin.h" + +namespace facebook { +namespace flipper { + +FlipperReactPlugin::FlipperReactPlugin( + std::string pluginId, + bool runInBackground, + FlipperConnectionEvent handler) + : pluginId_(std::move(pluginId)), + runInBackground_(runInBackground), + eventHandler_(std::move(handler)) {} + +FlipperReactPlugin::~FlipperReactPlugin() {} + +std::string FlipperReactPlugin::identifier() const { + return pluginId_; +} + +void FlipperReactPlugin::didConnect( + std::shared_ptr connection) { + connection_ = connection; + fireOnConnect(); +} + +void FlipperReactPlugin::didDisconnect() { + connection_ = nullptr; + fireOnDisconnect(); +} + +bool FlipperReactPlugin::runInBackground() { + return runInBackground_; +} + +void FlipperReactPlugin::fireOnConnect() { + eventHandler_(pluginId_, FlipperReactPluginEvent::CONNECTED); +} + +void FlipperReactPlugin::fireOnDisconnect() { + eventHandler_(pluginId_, FlipperReactPluginEvent::DISCONNECTED); +} + +bool FlipperReactPlugin::isConnected() { + return connection_ != nullptr; +} + +FlipperConnection* FlipperReactPlugin::getConnection() { + return connection_.get(); +} + +} // namespace flipper +} // namespace facebook diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPlugin.h b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPlugin.h new file mode 100644 index 000000000..01c398fa7 --- /dev/null +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPlugin.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include "../../../../xplat/Flipper/FlipperPlugin.h" + +namespace facebook { +namespace flipper { + +enum class FlipperReactPluginEvent { CONNECTED, DISCONNECTED }; + +using FlipperConnectionEvent = + std::function; + +class FlipperReactPlugin : public FlipperPlugin { + public: + FlipperReactPlugin( + std::string pluginId, + bool runInBackground, + FlipperConnectionEvent handler); + virtual ~FlipperReactPlugin(); + + virtual std::string identifier() const override; + + virtual void didConnect(std::shared_ptr conn) override; + + virtual void didDisconnect() override; + + virtual bool runInBackground() override; + + void fireOnConnect(); + void fireOnDisconnect(); + + bool isConnected(); + + FlipperConnection* getConnection(); + + private: + std::string pluginId_; + bool runInBackground_; + std::shared_ptr connection_; + FlipperConnectionEvent eventHandler_; +}; + +} // namespace flipper +} // namespace facebook diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPluginManager.cpp b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPluginManager.cpp new file mode 100644 index 000000000..d0d47c325 --- /dev/null +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPluginManager.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "FlipperReactPluginManager.h" +#include +#include "../../../../xplat/Flipper/FlipperClient.h" + +namespace facebook { +namespace flipper { + +static int FlipperResponderKeyGenerator = 0; + +folly::dynamic FlipperReactPluginManager::getParams(const std::string& params) { + if (params.empty()) { + return ""; + } + + return folly::parseJson(params); +} + +void FlipperReactPluginManager::actionWithPlugin( + const std::string& pluginId, + std::function action) { + auto flipperClient = facebook::flipper::FlipperClient::instance(); + if (flipperClient == nullptr) { + return; + } + + auto existingPlugin = flipperClient->getPlugin(pluginId); + if (existingPlugin != nullptr) { + FlipperReactPlugin* plugin = + dynamic_cast(existingPlugin.get()); + if (plugin) { + action(plugin); + } + } +} + +bool FlipperReactPluginManager::registerPlugin( + const std::string& pluginId, + bool inBackground, + FlipperConnectionEvent eventHandler) { + auto flipperClient = facebook::flipper::FlipperClient::instance(); + if (flipperClient == nullptr) { + return false; + } + + auto existingPlugin = flipperClient->getPlugin(pluginId); + if (existingPlugin != nullptr) { + FlipperReactPlugin* plugin = + dynamic_cast(existingPlugin.get()); + if (plugin && plugin->isConnected()) { + plugin->fireOnConnect(); + } + + return true; + } + + auto newPlugin = std::make_shared( + pluginId, inBackground, eventHandler); + flipperClient->addPlugin(newPlugin); + return true; +} + +void FlipperReactPluginManager::send( + const std::string& pluginId, + const std::string& method, + const std::string& params) { + actionWithPlugin(pluginId, [&method, ¶ms](FlipperReactPlugin* plugin) { + if (plugin->isConnected()) { + plugin->getConnection()->send(method, getParams(params)); + } + }); +} + +void FlipperReactPluginManager::reportError( + const std::string& pluginId, + const std::string& reason, + const std::string& stacktrace) { + actionWithPlugin( + pluginId, [&reason, &stacktrace](FlipperReactPlugin* plugin) { + if (plugin->isConnected()) { + plugin->getConnection()->error(reason, stacktrace); + } + }); +} + +void FlipperReactPluginManager::reportError( + const std::string& pluginId, + const std::string& error) { + actionWithPlugin(pluginId, [&error](FlipperReactPlugin* plugin) { + if (plugin->isConnected()) { + plugin->getConnection()->error(error, ""); + } + }); +} +void FlipperReactPluginManager::subscribe( + const std::string& pluginId, + const std::string& method, + FlipperReactPluginSubscriptionEvent eventHandler) { + actionWithPlugin( + pluginId, + [this, &pluginId, &method, &eventHandler](FlipperReactPlugin* plugin) { + if (plugin->isConnected()) { + plugin->getConnection()->receive( + method, + [this, pluginId, method, eventHandler]( + const folly::dynamic& obj, + std::shared_ptr + responder) { + std::map args{ + {"plugin", pluginId}, + {"method", method}, + {"params", folly::toJson(obj)}}; + if (responder != nullptr) { + auto responderId = + std::to_string(FlipperResponderKeyGenerator++); + responders_[responderId] = responder; + args["responderId"] = responderId; + } + + eventHandler(args); + }); + } + }); +} +void FlipperReactPluginManager::respondSuccess( + const std::string& responderId, + const std::string& params) { + auto responder = responders_.find(responderId); + if (responder != responders_.end()) { + responder->second->success(getParams(params)); + responders_.erase(responder); + } +} +void FlipperReactPluginManager::respondError( + const std::string& responderId, + const std::string& params) { + auto responder = responders_.find(responderId); + if (responder != responders_.end()) { + responder->second->error(getParams(params)); + responders_.erase(responder); + } +} + +} // namespace flipper +} // namespace facebook diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPluginManager.h b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPluginManager.h new file mode 100644 index 000000000..467288807 --- /dev/null +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactPluginManager.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace flipper { + +class FlipperReactPluginManager { + using FlipperReactPluginSubscriptionEvent = + std::function)>; + + public: + bool registerPlugin( + const std::string& pluginId, + bool inBackground, + FlipperConnectionEvent eventHandler); + + void send( + const std::string& pluginId, + const std::string& method, + const std::string& params); + + void reportError( + const std::string& pluginId, + const std::string& reason, + const std::string& stacktrace); + + void reportError(const std::string& pluginId, const std::string& error); + void subscribe( + const std::string& pluginId, + const std::string& method, + FlipperReactPluginSubscriptionEvent eventHandler); + void respondSuccess( + const std::string& responderId, + const std::string& params); + void respondError(const std::string& responderId, const std::string& params); + + private: + static folly::dynamic getParams(const std::string& param); + static void actionWithPlugin( + const std::string& pluginId, + std::function action); + + std::unordered_map< + std::string, + std::shared_ptr> + responders_; +}; + +} // namespace flipper +} // namespace facebook diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/ReactNativeFlipper.vcxproj b/react-native/react-native-flipper/windows/ReactNativeFlipper/ReactNativeFlipper.vcxproj index 45b2c4d00..88cd74ef7 100644 --- a/react-native/react-native-flipper/windows/ReactNativeFlipper/ReactNativeFlipper.vcxproj +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/ReactNativeFlipper.vcxproj @@ -154,6 +154,8 @@ + + @@ -175,6 +177,8 @@ + + diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/ReactNativeFlipper.vcxproj.filters b/react-native/react-native-flipper/windows/ReactNativeFlipper/ReactNativeFlipper.vcxproj.filters index 76eaecce7..f5bfea8af 100644 --- a/react-native/react-native-flipper/windows/ReactNativeFlipper/ReactNativeFlipper.vcxproj.filters +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/ReactNativeFlipper.vcxproj.filters @@ -20,6 +20,8 @@ + + @@ -54,6 +56,8 @@ + +