Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ target_link_libraries(react_renderer_scheduler
react_renderer_observers_events
react_renderer_runtimescheduler
react_renderer_uimanager
react_renderer_viewtransition
react_utils
rrc_root
rrc_view
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ Scheduler::Scheduler(
}
uiManager_->setAnimationDelegate(animationDelegate);

// Initialize ViewTransitionModule
if (ReactNativeFeatureFlags::viewTransitionEnabled()) {
viewTransitionModule_ = std::make_unique<ViewTransitionModule>();
viewTransitionModule_->setUIManager(uiManager_.get());
uiManager_->setViewTransitionDelegate(viewTransitionModule_.get());
}

uiManager->registerMountHook(*eventPerformanceLogger_);
}

Expand Down Expand Up @@ -186,6 +193,7 @@ Scheduler::~Scheduler() {
// The thread-safety of this operation is guaranteed by this requirement.
uiManager_->setDelegate(nullptr);
uiManager_->setAnimationDelegate(nullptr);
uiManager_->setViewTransitionDelegate(nullptr);

if (cdpMetricsReporter_) {
performanceEntryReporter_->removeEventListener(&*cdpMetricsReporter_);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <react/renderer/uimanager/UIManagerAnimationDelegate.h>
#include <react/renderer/uimanager/UIManagerBinding.h>
#include <react/renderer/uimanager/UIManagerDelegate.h>
#include <react/renderer/viewtransition/ViewTransitionModule.h>
#include <react/utils/ContextContainer.h>

namespace facebook::react {
Expand Down Expand Up @@ -146,6 +147,8 @@ class Scheduler final : public UIManagerDelegate {

RuntimeScheduler *runtimeScheduler_{nullptr};

std::unique_ptr<ViewTransitionModule> viewTransitionModule_;

mutable std::shared_mutex onSurfaceStartCallbackMutex_;
OnSurfaceStartCallback onSurfaceStartCallback_;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,10 @@ void UIManager::sendAccessibilityEvent(
}
}

UIManagerViewTransitionDelegate* UIManager::getViewTransitionDelegate() const {
return viewTransitionDelegate_;
}

void UIManager::configureNextLayoutAnimation(
jsi::Runtime& runtime,
const RawValue& config,
Expand Down Expand Up @@ -698,6 +702,13 @@ void UIManager::setNativeAnimatedDelegate(
nativeAnimatedDelegate_ = delegate;
}

void UIManager::setViewTransitionDelegate(
UIManagerViewTransitionDelegate* delegate) {
if (ReactNativeFeatureFlags::viewTransitionEnabled()) {
viewTransitionDelegate_ = delegate;
}
}

void UIManager::unstable_setAnimationBackend(
std::shared_ptr<UIManagerAnimationBackend> animationBackend) {
animationBackend_ = animationBackend;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <react/renderer/uimanager/UIManagerAnimationDelegate.h>
#include <react/renderer/uimanager/UIManagerDelegate.h>
#include <react/renderer/uimanager/UIManagerNativeAnimatedDelegate.h>
#include <react/renderer/uimanager/UIManagerViewTransitionDelegate.h>
#include <react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.h>
#include <react/renderer/uimanager/consistency/ShadowTreeRevisionProvider.h>
#include <react/renderer/uimanager/primitives.h>
Expand Down Expand Up @@ -74,6 +75,12 @@ class UIManager final : public ShadowTreeDelegate {

void setNativeAnimatedDelegate(std::weak_ptr<UIManagerNativeAnimatedDelegate> delegate);

/**
* Sets and gets UIManager's ViewTransition API delegate.
*/
void setViewTransitionDelegate(UIManagerViewTransitionDelegate *delegate);
UIManagerViewTransitionDelegate *getViewTransitionDelegate() const;

void animationTick() const;

void synchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props);
Expand Down Expand Up @@ -242,6 +249,7 @@ class UIManager final : public ShadowTreeDelegate {
UIManagerDelegate *delegate_{};
UIManagerAnimationDelegate *animationDelegate_{nullptr};
std::weak_ptr<UIManagerNativeAnimatedDelegate> nativeAnimatedDelegate_;
UIManagerViewTransitionDelegate *viewTransitionDelegate_{nullptr};

const RuntimeExecutor runtimeExecutor_{};
ShadowTreeRegistry shadowTreeRegistry_{};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,256 @@ jsi::Value UIManagerBinding::get(
});
}

if (methodName == "measureInstance") {
auto paramCount = 1;
return jsi::Function::createFromHostFunction(
runtime,
name,
paramCount,
[uiManager, methodName, paramCount](
jsi::Runtime& runtime,
const jsi::Value& /*thisValue*/,
const jsi::Value* arguments,
size_t count) -> jsi::Value {
validateArgumentCount(runtime, methodName, paramCount, count);

if (!arguments[0].isObject()) {
auto result = jsi::Object(runtime);
result.setProperty(runtime, "x", 0);
result.setProperty(runtime, "y", 0);
result.setProperty(runtime, "width", 0);
result.setProperty(runtime, "height", 0);
return result;
}

auto shadowNode = Bridging<std::shared_ptr<const ShadowNode>>::fromJs(
runtime, arguments[0]);

auto currentRevision =
uiManager->getShadowTreeRevisionProvider()->getCurrentRevision(
shadowNode->getSurfaceId());

if (currentRevision == nullptr) {
auto result = jsi::Object(runtime);
result.setProperty(runtime, "x", 0);
result.setProperty(runtime, "y", 0);
result.setProperty(runtime, "width", 0);
result.setProperty(runtime, "height", 0);
return result;
}

auto domRect = dom::getBoundingClientRect(
currentRevision, *shadowNode, true /* includeTransform */);

auto result = jsi::Object(runtime);
result.setProperty(runtime, "x", domRect.x);
result.setProperty(runtime, "y", domRect.y);
result.setProperty(runtime, "width", domRect.width);
result.setProperty(runtime, "height", domRect.height);

auto* viewTransitionDelegate = uiManager->getViewTransitionDelegate();
if (viewTransitionDelegate != nullptr) {
viewTransitionDelegate->captureLayoutMetricsFromRoot(*shadowNode);
}

return result;
});
}

if (methodName == "applyViewTransitionName") {
auto paramCount = 3;
return jsi::Function::createFromHostFunction(
runtime,
name,
paramCount,
[uiManager, methodName, paramCount](
jsi::Runtime& runtime,
const jsi::Value& /*thisValue*/,
const jsi::Value* arguments,
size_t count) -> jsi::Value {
validateArgumentCount(runtime, methodName, paramCount, count);

if (arguments[0].isObject()) {
auto shadowNode =
Bridging<std::shared_ptr<const ShadowNode>>::fromJs(
runtime, arguments[0]);
auto transitionName = arguments[1].isString()
? stringFromValue(runtime, arguments[1])
: "";
auto className = arguments[2].isString()
? stringFromValue(runtime, arguments[2])
: "";
if (!transitionName.empty()) {
auto* viewTransitionDelegate =
uiManager->getViewTransitionDelegate();
if (viewTransitionDelegate != nullptr) {
viewTransitionDelegate->applyViewTransitionName(
*shadowNode, transitionName, className);
}
}
}

return jsi::Value::undefined();
});
}

if (methodName == "cancelViewTransitionName") {
auto paramCount = 2;
return jsi::Function::createFromHostFunction(
runtime,
name,
paramCount,
[uiManager, methodName, paramCount](
jsi::Runtime& runtime,
const jsi::Value& /*thisValue*/,
const jsi::Value* arguments,
size_t count) -> jsi::Value {
validateArgumentCount(runtime, methodName, paramCount, count);

if (arguments[0].isObject()) {
auto shadowNode =
Bridging<std::shared_ptr<const ShadowNode>>::fromJs(
runtime, arguments[0]);
auto transitionName = arguments[1].isString()
? stringFromValue(runtime, arguments[1])
: "";
if (!transitionName.empty()) {
auto* viewTransitionDelegate =
uiManager->getViewTransitionDelegate();
if (viewTransitionDelegate != nullptr) {
viewTransitionDelegate->cancelViewTransitionName(
*shadowNode, transitionName);
}
}
}

return jsi::Value::undefined();
});
}

if (methodName == "restoreViewTransitionName") {
auto paramCount = 1;
return jsi::Function::createFromHostFunction(
runtime,
name,
paramCount,
[uiManager, methodName, paramCount](
jsi::Runtime& runtime,
const jsi::Value& /*thisValue*/,
const jsi::Value* arguments,
size_t count) -> jsi::Value {
validateArgumentCount(runtime, methodName, paramCount, count);

if (arguments[0].isObject()) {
auto shadowNode =
Bridging<std::shared_ptr<const ShadowNode>>::fromJs(
runtime, arguments[0]);
auto* viewTransitionDelegate =
uiManager->getViewTransitionDelegate();
if (viewTransitionDelegate != nullptr) {
viewTransitionDelegate->restoreViewTransitionName(*shadowNode);
}
}

return jsi::Value::undefined();
});
}

if (methodName == "startViewTransition") {
auto paramCount = 1;
return jsi::Function::createFromHostFunction(
runtime,
name,
paramCount,
[uiManager, methodName, paramCount](
jsi::Runtime& runtime,
const jsi::Value& /*thisValue*/,
const jsi::Value* arguments,
size_t count) -> jsi::Value {
validateArgumentCount(runtime, methodName, paramCount, count);

auto* viewTransitionDelegate = uiManager->getViewTransitionDelegate();
if (viewTransitionDelegate == nullptr) {
return jsi::Value::undefined();
}

auto promiseConstructor =
runtime.global().getPropertyAsFunction(runtime, "Promise");

auto readyResolveFunc =
std::make_shared<std::shared_ptr<jsi::Function>>();
auto finishedResolveFunc =
std::make_shared<std::shared_ptr<jsi::Function>>();

auto mutationFunc = std::make_shared<jsi::Function>(
arguments[0].asObject(runtime).asFunction(runtime));

auto readyPromise = promiseConstructor.callAsConstructor(
runtime,
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "readyExecutor"),
2,
[readyResolveFunc](
jsi::Runtime& runtime,
const jsi::Value& /*thisValue*/,
const jsi::Value* args,
size_t /*count*/) -> jsi::Value {
auto onReadyFunc = std::make_shared<jsi::Function>(
args[0].asObject(runtime).asFunction(runtime));
*readyResolveFunc = onReadyFunc;
return jsi::Value::undefined();
}));

auto finishedPromise = promiseConstructor.callAsConstructor(
runtime,
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "finishedExecutor"),
2,
[finishedResolveFunc](
jsi::Runtime& rt,
const jsi::Value& /*thisValue*/,
const jsi::Value* args,
size_t /*count*/) -> jsi::Value {
auto onCompleteFunc = std::make_shared<jsi::Function>(
args[0].asObject(rt).asFunction(rt));
*finishedResolveFunc = onCompleteFunc;
return jsi::Value::undefined();
}));

auto result = jsi::Object(runtime);
result.setProperty(runtime, "ready", std::move(readyPromise));
result.setProperty(runtime, "finished", std::move(finishedPromise));

viewTransitionDelegate->startViewTransition(
[&runtime, mutationFunc = std::move(mutationFunc)]() {
mutationFunc->call(runtime);
},
[readyResolveFunc = std::move(readyResolveFunc), uiManager]() {
uiManager->runtimeExecutor_(
[readyResolveFunc = std::move(readyResolveFunc)](
jsi::Runtime& rt) mutable {
if (*readyResolveFunc) {
(*readyResolveFunc)->call(rt);
}
});
},
[finishedResolveFunc = std::move(finishedResolveFunc),
uiManager]() {
uiManager->runtimeExecutor_(
[finishedResolveFunc = std::move(finishedResolveFunc)](
jsi::Runtime& rt) mutable {
if (*finishedResolveFunc) {
(*finishedResolveFunc)->call(rt);
}
});
});

return jsi::Value(runtime, result);
});
}

return jsi::Value::undefined();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 <react/renderer/core/ShadowNode.h>
#include <functional>

namespace facebook::react {

class UIManagerViewTransitionDelegate {
public:
virtual ~UIManagerViewTransitionDelegate() = default;

virtual void
applyViewTransitionName(const ShadowNode &shadowNode, const std::string &name, const std::string &className)
{
}

virtual void cancelViewTransitionName(const ShadowNode &shadowNode, const std::string &name) {};

virtual void restoreViewTransitionName(const ShadowNode &shadowNode) {};

virtual void captureLayoutMetricsFromRoot(const ShadowNode &shadowNode) {};

virtual void startViewTransition(
std::function<void()> mutationCallback,
std::function<void()> onReadyCallback,
std::function<void()> onCompleteCallback) {};
};

} // namespace facebook::react
Loading
Loading