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 @@ + +