2018-09-11 15:27:47 -07:00
|
|
|
// Copyright (c) Facebook, Inc. and its affiliates.
|
2018-05-31 15:31:03 -07:00
|
|
|
|
|
|
|
// This source code is licensed under the MIT license found in the
|
|
|
|
// LICENSE file in the root directory of this source tree.
|
2018-04-10 12:46:20 -07:00
|
|
|
|
|
|
|
#include "Scheduler.h"
|
|
|
|
|
2018-10-29 13:02:27 -07:00
|
|
|
#include <jsi/jsi.h>
|
|
|
|
|
2018-11-10 14:19:08 -08:00
|
|
|
#include <react/core/LayoutContext.h>
|
|
|
|
#include <react/uimanager/ComponentDescriptorRegistry.h>
|
|
|
|
#include <react/uimanager/UIManager.h>
|
|
|
|
#include <react/uimanager/UIManagerBinding.h>
|
|
|
|
#include <react/uimanager/UITemplateProcessor.h>
|
2018-04-10 12:46:20 -07:00
|
|
|
|
2018-05-18 19:44:34 -07:00
|
|
|
#include "ComponentDescriptorFactory.h"
|
2018-04-10 12:46:20 -07:00
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace react {
|
|
|
|
|
2018-10-09 16:25:13 -07:00
|
|
|
Scheduler::Scheduler(const SharedContextContainer &contextContainer)
|
|
|
|
: contextContainer_(contextContainer) {
|
|
|
|
const auto asynchronousEventBeatFactory =
|
|
|
|
contextContainer->getInstance<EventBeatFactory>("asynchronous");
|
|
|
|
const auto synchronousEventBeatFactory =
|
|
|
|
contextContainer->getInstance<EventBeatFactory>("synchronous");
|
2018-09-26 10:02:01 -07:00
|
|
|
|
2018-11-06 10:58:51 -08:00
|
|
|
runtimeExecutor_ =
|
2018-10-29 13:02:27 -07:00
|
|
|
contextContainer->getInstance<RuntimeExecutor>("runtime-executor");
|
|
|
|
|
2018-11-06 10:58:51 -08:00
|
|
|
auto uiManager = std::make_unique<UIManager>();
|
|
|
|
auto &uiManagerRef = *uiManager;
|
|
|
|
uiManagerBinding_ =
|
|
|
|
std::make_shared<UIManagerBinding>(std::move(uiManager));
|
2018-10-29 13:02:27 -07:00
|
|
|
|
2018-11-06 10:58:51 -08:00
|
|
|
auto eventPipe = [uiManagerBinding = uiManagerBinding_.get()](
|
|
|
|
jsi::Runtime &runtime,
|
|
|
|
const EventTarget *eventTarget,
|
|
|
|
const std::string &type,
|
|
|
|
const folly::dynamic &payload) {
|
|
|
|
uiManagerBinding->dispatchEvent(runtime, eventTarget, type, payload);
|
|
|
|
};
|
2018-08-27 07:21:26 -07:00
|
|
|
|
2018-10-09 16:25:13 -07:00
|
|
|
auto eventDispatcher = std::make_shared<EventDispatcher>(
|
2018-11-06 10:58:51 -08:00
|
|
|
eventPipe, synchronousEventBeatFactory, asynchronousEventBeatFactory);
|
2018-08-27 07:21:26 -07:00
|
|
|
|
2018-10-10 19:51:29 -07:00
|
|
|
componentDescriptorRegistry_ = ComponentDescriptorFactory::buildRegistry(
|
mostly working on Android + OTA
Summary:
It works great on iOS, and mostly works on Android, and is now OTA'able as part of the screen config! Haven't done template view yet. One remaining issue:
Layout is borked on Android. I'm guessing the issue has to do with the timing of setting the constraints in `updateRootLayoutSpecs` and calling `mBinding.startSurface` which actually builds the shadow tree. If I try to call `updateRootLayoutSpecs` earlier, it just crashes immediately. Here's the layout it spits out, which clearly has -440 for the x of 420006, which is the RCTText component, causing it to get cut off on the left of the screen:
```
updateLayoutMountItem for reactTag: 420006 x: -440, y: -13, width: 931, height: 78
updateLayoutMountItem for reactTag: 420010 x: 26, y: 79, width: 0, height: 1651
updateLayoutMountItem for reactTag: 420012 x: 0, y: 26, width: 0, height: 158
updateLayoutMountItem for reactTag: 420016 x: 0, y: 210, width: 454, height: 454
updateLayoutMountItem for reactTag: 420018 x: 454, y: 210, width: 455, height: 454
updateLayoutMountItem for reactTag: 420022 x: 0, y: 690, width: 454, height: 454
updateLayoutMountItem for reactTag: 420024 x: 454, y: 690, width: 455, height: 454
updateLayoutMountItem for reactTag: 420028 x: 0, y: 1171, width: 454, height: 454
updateLayoutMountItem for reactTag: 420030 x: 454, y: 1171, width: 455, height: 454
updateLayoutMountItem for reactTag: 420032 x: 0, y: 1651, width: 0, height: 0
```
Reviewed By: mdvacca
Differential Revision: D12813192
fbshipit-source-id: 450d646af4883ff25184141721351da67b091b7c
2018-11-05 15:32:47 -08:00
|
|
|
eventDispatcher, contextContainer);
|
2018-06-01 09:36:25 -07:00
|
|
|
|
2018-11-06 10:58:51 -08:00
|
|
|
uiManagerRef.setDelegate(this);
|
2018-11-21 17:10:31 -08:00
|
|
|
uiManagerRef.setShadowTreeRegistry(&shadowTreeRegistry_);
|
2018-11-06 10:58:51 -08:00
|
|
|
uiManagerRef.setComponentDescriptorRegistry(componentDescriptorRegistry_);
|
|
|
|
|
|
|
|
runtimeExecutor_([=](jsi::Runtime &runtime) {
|
|
|
|
UIManagerBinding::install(runtime, uiManagerBinding_);
|
|
|
|
});
|
2018-04-10 12:46:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Scheduler::~Scheduler() {
|
2018-11-06 10:58:51 -08:00
|
|
|
uiManagerBinding_->invalidate();
|
2018-04-10 12:46:20 -07:00
|
|
|
}
|
|
|
|
|
2018-09-26 10:02:04 -07:00
|
|
|
void Scheduler::startSurface(
|
2018-10-09 16:25:13 -07:00
|
|
|
SurfaceId surfaceId,
|
|
|
|
const std::string &moduleName,
|
|
|
|
const folly::dynamic &initialProps,
|
|
|
|
const LayoutConstraints &layoutConstraints,
|
mostly working on Android + OTA
Summary:
It works great on iOS, and mostly works on Android, and is now OTA'able as part of the screen config! Haven't done template view yet. One remaining issue:
Layout is borked on Android. I'm guessing the issue has to do with the timing of setting the constraints in `updateRootLayoutSpecs` and calling `mBinding.startSurface` which actually builds the shadow tree. If I try to call `updateRootLayoutSpecs` earlier, it just crashes immediately. Here's the layout it spits out, which clearly has -440 for the x of 420006, which is the RCTText component, causing it to get cut off on the left of the screen:
```
updateLayoutMountItem for reactTag: 420006 x: -440, y: -13, width: 931, height: 78
updateLayoutMountItem for reactTag: 420010 x: 26, y: 79, width: 0, height: 1651
updateLayoutMountItem for reactTag: 420012 x: 0, y: 26, width: 0, height: 158
updateLayoutMountItem for reactTag: 420016 x: 0, y: 210, width: 454, height: 454
updateLayoutMountItem for reactTag: 420018 x: 454, y: 210, width: 455, height: 454
updateLayoutMountItem for reactTag: 420022 x: 0, y: 690, width: 454, height: 454
updateLayoutMountItem for reactTag: 420024 x: 454, y: 690, width: 455, height: 454
updateLayoutMountItem for reactTag: 420028 x: 0, y: 1171, width: 454, height: 454
updateLayoutMountItem for reactTag: 420030 x: 454, y: 1171, width: 455, height: 454
updateLayoutMountItem for reactTag: 420032 x: 0, y: 1651, width: 0, height: 0
```
Reviewed By: mdvacca
Differential Revision: D12813192
fbshipit-source-id: 450d646af4883ff25184141721351da67b091b7c
2018-11-05 15:32:47 -08:00
|
|
|
const LayoutContext &layoutContext) const {
|
2018-10-09 16:25:13 -07:00
|
|
|
auto shadowTree =
|
|
|
|
std::make_unique<ShadowTree>(surfaceId, layoutConstraints, layoutContext);
|
2018-05-08 22:56:16 -07:00
|
|
|
shadowTree->setDelegate(this);
|
2018-11-21 17:10:30 -08:00
|
|
|
|
|
|
|
shadowTreeRegistry_.add(std::move(shadowTree));
|
mostly working on Android + OTA
Summary:
It works great on iOS, and mostly works on Android, and is now OTA'able as part of the screen config! Haven't done template view yet. One remaining issue:
Layout is borked on Android. I'm guessing the issue has to do with the timing of setting the constraints in `updateRootLayoutSpecs` and calling `mBinding.startSurface` which actually builds the shadow tree. If I try to call `updateRootLayoutSpecs` earlier, it just crashes immediately. Here's the layout it spits out, which clearly has -440 for the x of 420006, which is the RCTText component, causing it to get cut off on the left of the screen:
```
updateLayoutMountItem for reactTag: 420006 x: -440, y: -13, width: 931, height: 78
updateLayoutMountItem for reactTag: 420010 x: 26, y: 79, width: 0, height: 1651
updateLayoutMountItem for reactTag: 420012 x: 0, y: 26, width: 0, height: 158
updateLayoutMountItem for reactTag: 420016 x: 0, y: 210, width: 454, height: 454
updateLayoutMountItem for reactTag: 420018 x: 454, y: 210, width: 455, height: 454
updateLayoutMountItem for reactTag: 420022 x: 0, y: 690, width: 454, height: 454
updateLayoutMountItem for reactTag: 420024 x: 454, y: 690, width: 455, height: 454
updateLayoutMountItem for reactTag: 420028 x: 0, y: 1171, width: 454, height: 454
updateLayoutMountItem for reactTag: 420030 x: 454, y: 1171, width: 455, height: 454
updateLayoutMountItem for reactTag: 420032 x: 0, y: 1651, width: 0, height: 0
```
Reviewed By: mdvacca
Differential Revision: D12813192
fbshipit-source-id: 450d646af4883ff25184141721351da67b091b7c
2018-11-05 15:32:47 -08:00
|
|
|
|
Update and expand bytecode spec
Summary:
* Adds parent tag as param for createNode in place of explicit appendChild commands.
* Adds version info to bytecode
* Adds native conditional support:
Conditionals are represented in product code with the new `NativeConditional` React
component. It takes params necessary to construct a native function call, and takes
a render prop as a child that passes the value of the native call as an arg. In
prod, the component would actually call the native module and render with that value,
but in jest we render for *both* true and false and set them as children
of a new jest-only primitive/host component which we special-case and generate a
special command with `OP_CODE.conditional`, generate the appropriate bytecode commands
for each branch, and embed them as args in the conditional OP_CODE command. When
evaluating the bytecode, only one set of commands is executed, based on the native
module value (which is evaluated with another new opcode which computes the value
and stuffs it in a "register").
Obviously generating this bytecode is kind of a cludge compared to prepack, but
when I asked @[501709947:Dominic] about it, he said they had no bytecode spec right
now, so I'm running ahead with this prototype. The main thing I'm focused on is
the C++/RN bytecode interpretter - this jest stuff is just a way to generate bytecode
for it to consume which could be replaced or augmented with many other approaches,
such as prepack, server rendering, etc.
Also piggybacked a bunch of other cleanup.
Reviewed By: shergin
Differential Revision: D10277121
fbshipit-source-id: 15d3217a59ef481b574c742d17d8a7dc893cba90
2018-11-05 15:32:46 -08:00
|
|
|
#ifndef ANDROID
|
2018-11-06 10:58:51 -08:00
|
|
|
runtimeExecutor_([=](jsi::Runtime &runtime) {
|
|
|
|
uiManagerBinding_->startSurface(
|
|
|
|
runtime, surfaceId, moduleName, initialProps);
|
|
|
|
});
|
2018-09-26 10:02:04 -07:00
|
|
|
#endif
|
2018-04-10 12:46:20 -07:00
|
|
|
}
|
|
|
|
|
mostly working on Android + OTA
Summary:
It works great on iOS, and mostly works on Android, and is now OTA'able as part of the screen config! Haven't done template view yet. One remaining issue:
Layout is borked on Android. I'm guessing the issue has to do with the timing of setting the constraints in `updateRootLayoutSpecs` and calling `mBinding.startSurface` which actually builds the shadow tree. If I try to call `updateRootLayoutSpecs` earlier, it just crashes immediately. Here's the layout it spits out, which clearly has -440 for the x of 420006, which is the RCTText component, causing it to get cut off on the left of the screen:
```
updateLayoutMountItem for reactTag: 420006 x: -440, y: -13, width: 931, height: 78
updateLayoutMountItem for reactTag: 420010 x: 26, y: 79, width: 0, height: 1651
updateLayoutMountItem for reactTag: 420012 x: 0, y: 26, width: 0, height: 158
updateLayoutMountItem for reactTag: 420016 x: 0, y: 210, width: 454, height: 454
updateLayoutMountItem for reactTag: 420018 x: 454, y: 210, width: 455, height: 454
updateLayoutMountItem for reactTag: 420022 x: 0, y: 690, width: 454, height: 454
updateLayoutMountItem for reactTag: 420024 x: 454, y: 690, width: 455, height: 454
updateLayoutMountItem for reactTag: 420028 x: 0, y: 1171, width: 454, height: 454
updateLayoutMountItem for reactTag: 420030 x: 454, y: 1171, width: 455, height: 454
updateLayoutMountItem for reactTag: 420032 x: 0, y: 1651, width: 0, height: 0
```
Reviewed By: mdvacca
Differential Revision: D12813192
fbshipit-source-id: 450d646af4883ff25184141721351da67b091b7c
2018-11-05 15:32:47 -08:00
|
|
|
void Scheduler::renderTemplateToSurface(
|
|
|
|
SurfaceId surfaceId,
|
|
|
|
const std::string &uiTemplate) {
|
|
|
|
try {
|
|
|
|
if (uiTemplate.size() == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
NativeModuleRegistry nMR;
|
2018-11-05 15:32:47 -08:00
|
|
|
auto tree = UITemplateProcessor::buildShadowTree(
|
mostly working on Android + OTA
Summary:
It works great on iOS, and mostly works on Android, and is now OTA'able as part of the screen config! Haven't done template view yet. One remaining issue:
Layout is borked on Android. I'm guessing the issue has to do with the timing of setting the constraints in `updateRootLayoutSpecs` and calling `mBinding.startSurface` which actually builds the shadow tree. If I try to call `updateRootLayoutSpecs` earlier, it just crashes immediately. Here's the layout it spits out, which clearly has -440 for the x of 420006, which is the RCTText component, causing it to get cut off on the left of the screen:
```
updateLayoutMountItem for reactTag: 420006 x: -440, y: -13, width: 931, height: 78
updateLayoutMountItem for reactTag: 420010 x: 26, y: 79, width: 0, height: 1651
updateLayoutMountItem for reactTag: 420012 x: 0, y: 26, width: 0, height: 158
updateLayoutMountItem for reactTag: 420016 x: 0, y: 210, width: 454, height: 454
updateLayoutMountItem for reactTag: 420018 x: 454, y: 210, width: 455, height: 454
updateLayoutMountItem for reactTag: 420022 x: 0, y: 690, width: 454, height: 454
updateLayoutMountItem for reactTag: 420024 x: 454, y: 690, width: 455, height: 454
updateLayoutMountItem for reactTag: 420028 x: 0, y: 1171, width: 454, height: 454
updateLayoutMountItem for reactTag: 420030 x: 454, y: 1171, width: 455, height: 454
updateLayoutMountItem for reactTag: 420032 x: 0, y: 1651, width: 0, height: 0
```
Reviewed By: mdvacca
Differential Revision: D12813192
fbshipit-source-id: 450d646af4883ff25184141721351da67b091b7c
2018-11-05 15:32:47 -08:00
|
|
|
uiTemplate,
|
|
|
|
surfaceId,
|
|
|
|
folly::dynamic::object(),
|
|
|
|
*componentDescriptorRegistry_,
|
|
|
|
nMR);
|
|
|
|
|
2018-11-21 17:10:30 -08:00
|
|
|
shadowTreeRegistry_.get(surfaceId, [=](const ShadowTree &shadowTree) {
|
|
|
|
shadowTree.complete(
|
|
|
|
std::make_shared<SharedShadowNodeList>(SharedShadowNodeList{tree}));
|
|
|
|
});
|
mostly working on Android + OTA
Summary:
It works great on iOS, and mostly works on Android, and is now OTA'able as part of the screen config! Haven't done template view yet. One remaining issue:
Layout is borked on Android. I'm guessing the issue has to do with the timing of setting the constraints in `updateRootLayoutSpecs` and calling `mBinding.startSurface` which actually builds the shadow tree. If I try to call `updateRootLayoutSpecs` earlier, it just crashes immediately. Here's the layout it spits out, which clearly has -440 for the x of 420006, which is the RCTText component, causing it to get cut off on the left of the screen:
```
updateLayoutMountItem for reactTag: 420006 x: -440, y: -13, width: 931, height: 78
updateLayoutMountItem for reactTag: 420010 x: 26, y: 79, width: 0, height: 1651
updateLayoutMountItem for reactTag: 420012 x: 0, y: 26, width: 0, height: 158
updateLayoutMountItem for reactTag: 420016 x: 0, y: 210, width: 454, height: 454
updateLayoutMountItem for reactTag: 420018 x: 454, y: 210, width: 455, height: 454
updateLayoutMountItem for reactTag: 420022 x: 0, y: 690, width: 454, height: 454
updateLayoutMountItem for reactTag: 420024 x: 454, y: 690, width: 455, height: 454
updateLayoutMountItem for reactTag: 420028 x: 0, y: 1171, width: 454, height: 454
updateLayoutMountItem for reactTag: 420030 x: 454, y: 1171, width: 455, height: 454
updateLayoutMountItem for reactTag: 420032 x: 0, y: 1651, width: 0, height: 0
```
Reviewed By: mdvacca
Differential Revision: D12813192
fbshipit-source-id: 450d646af4883ff25184141721351da67b091b7c
2018-11-05 15:32:47 -08:00
|
|
|
} catch (const std::exception &e) {
|
|
|
|
LOG(ERROR) << " >>>> EXCEPTION <<< rendering uiTemplate in "
|
|
|
|
<< "Scheduler::renderTemplateToSurface: " << e.what();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-26 10:02:04 -07:00
|
|
|
void Scheduler::stopSurface(SurfaceId surfaceId) const {
|
2018-11-21 17:10:30 -08:00
|
|
|
shadowTreeRegistry_.get(surfaceId, [](const ShadowTree &shadowTree) {
|
|
|
|
// As part of stopping the Surface, we have to commit an empty tree.
|
|
|
|
shadowTree.complete(std::const_pointer_cast<SharedShadowNodeList>(
|
|
|
|
ShadowNode::emptySharedShadowNodeSharedList()));
|
|
|
|
});
|
|
|
|
|
|
|
|
auto shadowTree = shadowTreeRegistry_.remove(surfaceId);
|
|
|
|
shadowTree->setDelegate(nullptr);
|
2018-11-06 10:58:51 -08:00
|
|
|
|
|
|
|
#ifndef ANDROID
|
|
|
|
runtimeExecutor_([=](jsi::Runtime &runtime) {
|
|
|
|
uiManagerBinding_->stopSurface(runtime, surfaceId);
|
|
|
|
});
|
|
|
|
#endif
|
2018-05-08 22:56:16 -07:00
|
|
|
}
|
|
|
|
|
2018-09-26 10:02:04 -07:00
|
|
|
Size Scheduler::measureSurface(
|
2018-10-09 16:25:13 -07:00
|
|
|
SurfaceId surfaceId,
|
|
|
|
const LayoutConstraints &layoutConstraints,
|
|
|
|
const LayoutContext &layoutContext) const {
|
2018-11-21 17:10:30 -08:00
|
|
|
Size size;
|
|
|
|
shadowTreeRegistry_.get(surfaceId, [&](const ShadowTree &shadowTree) {
|
|
|
|
size = shadowTree.measure(layoutConstraints, layoutContext);
|
|
|
|
});
|
|
|
|
return size;
|
2018-05-08 22:56:16 -07:00
|
|
|
}
|
|
|
|
|
2018-10-09 16:25:03 -07:00
|
|
|
void Scheduler::constraintSurfaceLayout(
|
2018-10-09 16:25:13 -07:00
|
|
|
SurfaceId surfaceId,
|
|
|
|
const LayoutConstraints &layoutConstraints,
|
|
|
|
const LayoutContext &layoutContext) const {
|
2018-11-21 17:10:30 -08:00
|
|
|
shadowTreeRegistry_.get(surfaceId, [&](const ShadowTree &shadowTree) {
|
|
|
|
shadowTree.synchronize([&]() {
|
|
|
|
shadowTree.constraintLayout(layoutConstraints, layoutContext);
|
|
|
|
});
|
2018-10-09 16:25:03 -07:00
|
|
|
});
|
2018-04-10 12:46:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Delegate
|
|
|
|
|
|
|
|
void Scheduler::setDelegate(SchedulerDelegate *delegate) {
|
|
|
|
delegate_ = delegate;
|
|
|
|
}
|
|
|
|
|
2018-05-08 22:56:16 -07:00
|
|
|
SchedulerDelegate *Scheduler::getDelegate() const {
|
2018-04-10 12:46:20 -07:00
|
|
|
return delegate_;
|
|
|
|
}
|
|
|
|
|
2018-05-08 22:56:16 -07:00
|
|
|
#pragma mark - ShadowTreeDelegate
|
2018-04-10 12:46:20 -07:00
|
|
|
|
2018-10-09 16:25:13 -07:00
|
|
|
void Scheduler::shadowTreeDidCommit(
|
|
|
|
const ShadowTree &shadowTree,
|
|
|
|
const ShadowViewMutationList &mutations) const {
|
2018-04-10 12:46:20 -07:00
|
|
|
if (delegate_) {
|
2018-10-09 16:25:13 -07:00
|
|
|
delegate_->schedulerDidFinishTransaction(
|
|
|
|
shadowTree.getSurfaceId(), mutations);
|
2018-04-10 12:46:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-08 22:56:16 -07:00
|
|
|
#pragma mark - UIManagerDelegate
|
2018-05-31 15:31:03 -07:00
|
|
|
|
2018-10-09 16:25:13 -07:00
|
|
|
void Scheduler::uiManagerDidFinishTransaction(
|
2018-11-21 17:10:30 -08:00
|
|
|
SurfaceId surfaceId,
|
2018-10-09 16:25:13 -07:00
|
|
|
const SharedShadowNodeUnsharedList &rootChildNodes) {
|
2018-11-21 17:10:30 -08:00
|
|
|
shadowTreeRegistry_.get(surfaceId, [&](const ShadowTree &shadowTree) {
|
|
|
|
shadowTree.complete(rootChildNodes);
|
|
|
|
});
|
2018-05-08 22:56:16 -07:00
|
|
|
}
|
|
|
|
|
2018-10-09 16:25:13 -07:00
|
|
|
void Scheduler::uiManagerDidCreateShadowNode(
|
|
|
|
const SharedShadowNode &shadowNode) {
|
2018-04-10 12:46:20 -07:00
|
|
|
if (delegate_) {
|
2018-11-11 15:18:09 -08:00
|
|
|
auto layoutableShadowNode =
|
|
|
|
dynamic_cast<const LayoutableShadowNode *>(shadowNode.get());
|
|
|
|
auto isLayoutable = layoutableShadowNode != nullptr;
|
|
|
|
|
2018-10-09 16:25:13 -07:00
|
|
|
delegate_->schedulerDidRequestPreliminaryViewAllocation(
|
2018-11-11 15:18:09 -08:00
|
|
|
shadowNode->getRootTag(),
|
|
|
|
shadowNode->getComponentName(),
|
|
|
|
isLayoutable,
|
|
|
|
shadowNode->getComponentHandle());
|
2018-04-10 12:46:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace react
|
|
|
|
} // namespace facebook
|