react-native-reanimated 4.3.1 → 4.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/Common/cpp/reanimated/CSS/interpolation/transforms/TransformOperationInterpolator.cpp +12 -5
  2. package/Common/cpp/reanimated/CSS/misc/ViewStylesRepository.cpp +59 -37
  3. package/Common/cpp/reanimated/CSS/misc/ViewStylesRepository.h +11 -8
  4. package/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.cpp +4 -0
  5. package/Common/cpp/reanimated/CSS/registries/StaticPropsRegistry.cpp +17 -4
  6. package/Common/cpp/reanimated/CSS/registries/StaticPropsRegistry.h +5 -0
  7. package/Common/cpp/reanimated/CSS/svg/values/SVGStrokeDashArray.cpp +5 -0
  8. package/Common/cpp/reanimated/Fabric/ReanimatedMountHook.cpp +25 -12
  9. package/Common/cpp/reanimated/Fabric/ReanimatedMountHook.h +3 -0
  10. package/Common/cpp/reanimated/Fabric/updates/AnimatedPropsRegistry.cpp +48 -23
  11. package/Common/cpp/reanimated/Fabric/updates/AnimatedPropsRegistry.h +13 -6
  12. package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.h +38 -0
  13. package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp +106 -33
  14. package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp +205 -92
  15. package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.h +4 -0
  16. package/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +5 -11
  17. package/android/CMakeLists.txt +4 -1
  18. package/android/build.gradle +2 -0
  19. package/android/generate-stub-pch.gradle.kts +84 -0
  20. package/android/src/main/cpp/ReanimatedPCH.h +46 -0
  21. package/android/src/main/java/com/swmansion/reanimated/NativeProxy.java +14 -1
  22. package/android/src/main/java/com/swmansion/reanimated/keyboard/KeyboardAnimationCallback.java +9 -1
  23. package/apple/reanimated/apple/ReanimatedModule.mm +12 -1
  24. package/lib/module/PropsRegistryGarbageCollector.js +3 -6
  25. package/lib/module/PropsRegistryGarbageCollector.js.map +1 -1
  26. package/lib/module/animation/styleAnimation.js +4 -3
  27. package/lib/module/animation/styleAnimation.js.map +1 -1
  28. package/lib/module/common/constants/platform.js +5 -5
  29. package/lib/module/common/constants/platform.js.map +1 -1
  30. package/lib/module/createAnimatedComponent/getViewInfo.js +3 -1
  31. package/lib/module/createAnimatedComponent/getViewInfo.js.map +1 -1
  32. package/lib/module/css/component/AnimatedComponent.js +0 -2
  33. package/lib/module/css/component/AnimatedComponent.js.map +1 -1
  34. package/lib/module/css/native/managers/CSSManager.js +5 -5
  35. package/lib/module/css/native/managers/CSSManager.js.map +1 -1
  36. package/lib/module/css/native/managers/CSSTransitionsManager.js +8 -1
  37. package/lib/module/css/native/managers/CSSTransitionsManager.js.map +1 -1
  38. package/lib/module/css/svg/native/processors/stroke.js +9 -5
  39. package/lib/module/css/svg/native/processors/stroke.js.map +1 -1
  40. package/lib/module/layoutReanimation/animationsManager.js +9 -6
  41. package/lib/module/layoutReanimation/animationsManager.js.map +1 -1
  42. package/lib/module/platform-specific/findHostInstance.js +8 -5
  43. package/lib/module/platform-specific/findHostInstance.js.map +1 -1
  44. package/lib/module/platform-specific/jsVersion.js +1 -1
  45. package/lib/module/updateProps/updateProps.js +3 -2
  46. package/lib/module/updateProps/updateProps.js.map +1 -1
  47. package/lib/module/valueSetter.js +1 -1
  48. package/lib/module/valueSetter.js.map +1 -1
  49. package/lib/typescript/PropsRegistryGarbageCollector.d.ts +0 -1
  50. package/lib/typescript/PropsRegistryGarbageCollector.d.ts.map +1 -1
  51. package/lib/typescript/animation/styleAnimation.d.ts +1 -1
  52. package/lib/typescript/animation/styleAnimation.d.ts.map +1 -1
  53. package/lib/typescript/common/constants/platform.d.ts.map +1 -1
  54. package/lib/typescript/createAnimatedComponent/commonTypes.d.ts +0 -1
  55. package/lib/typescript/createAnimatedComponent/commonTypes.d.ts.map +1 -1
  56. package/lib/typescript/createAnimatedComponent/getViewInfo.d.ts.map +1 -1
  57. package/lib/typescript/css/component/AnimatedComponent.d.ts +0 -1
  58. package/lib/typescript/css/component/AnimatedComponent.d.ts.map +1 -1
  59. package/lib/typescript/css/native/managers/CSSTransitionsManager.d.ts +5 -1
  60. package/lib/typescript/css/native/managers/CSSTransitionsManager.d.ts.map +1 -1
  61. package/lib/typescript/css/svg/native/processors/stroke.d.ts.map +1 -1
  62. package/lib/typescript/layoutReanimation/animationsManager.d.ts.map +1 -1
  63. package/lib/typescript/platform-specific/findHostInstance.d.ts.map +1 -1
  64. package/lib/typescript/platform-specific/jsVersion.d.ts +1 -1
  65. package/lib/typescript/platform-specific/types.d.ts +1 -0
  66. package/lib/typescript/platform-specific/types.d.ts.map +1 -1
  67. package/lib/typescript/updateProps/updateProps.d.ts.map +1 -1
  68. package/lib/typescript/valueSetter.d.ts.map +1 -1
  69. package/package.json +2 -2
  70. package/src/PropsRegistryGarbageCollector.ts +3 -6
  71. package/src/animation/styleAnimation.ts +5 -3
  72. package/src/common/constants/platform.ts +6 -5
  73. package/src/createAnimatedComponent/commonTypes.ts +0 -1
  74. package/src/createAnimatedComponent/getViewInfo.ts +3 -1
  75. package/src/css/component/AnimatedComponent.tsx +0 -2
  76. package/src/css/native/managers/CSSManager.ts +8 -8
  77. package/src/css/native/managers/CSSTransitionsManager.ts +9 -2
  78. package/src/css/svg/native/processors/stroke.ts +9 -6
  79. package/src/layoutReanimation/animationsManager.ts +9 -7
  80. package/src/platform-specific/findHostInstance.ts +7 -7
  81. package/src/platform-specific/jsVersion.ts +1 -1
  82. package/src/platform-specific/types.ts +2 -0
  83. package/src/updateProps/updateProps.ts +3 -2
  84. package/src/valueSetter.ts +2 -1
@@ -518,11 +518,10 @@ bool LayoutAnimationsProxy_Experimental::startAnimationsRecursively(
518
518
 
519
519
  if (hasExitAnimation) {
520
520
  node->state = ANIMATING;
521
- startExitingAnimation(node);
522
521
  lightNodes_[node->current.tag] = node;
522
+ startExitingAnimation(node);
523
523
  } else {
524
- // TODO (future): add proper cleanup
525
- // layoutAnimationsManager_->clearLayoutAnimationConfig(node->tag);
524
+ layoutAnimationsManager_->clearLayoutAnimationConfig(node->current.tag);
526
525
  }
527
526
 
528
527
  return wantAnimateExit;
@@ -534,6 +533,13 @@ void LayoutAnimationsProxy_Experimental::updateOngoingAnimationTarget(const int
534
533
  }
535
534
 
536
535
  void LayoutAnimationsProxy_Experimental::maybeCancelAnimation(const int tag) const {
536
+ #ifdef ANDROID
537
+ // also invalidate animation starts that are scheduled but haven't run yet,
538
+ // so they don't re-create the animation after this cancellation
539
+ if (const auto it = pendingStarts_.find(tag); it != pendingStarts_.end()) {
540
+ it->second.handle++;
541
+ }
542
+ #endif
537
543
  if (!layoutAnimations_.contains(tag)) {
538
544
  return;
539
545
  }
@@ -678,9 +684,24 @@ void LayoutAnimationsProxy_Experimental::startEnteringAnimation(const std::share
678
684
  const auto &parent = node->parent.lock();
679
685
  react_native_assert(parent && "Parent node is nullptr");
680
686
  const auto parentTag = parent->current.tag;
687
+ #ifdef ANDROID
688
+ const auto handle = pendingStarts_[newChildShadowView.tag].handle;
689
+ pendingStarts_[newChildShadowView.tag].count++;
690
+ #endif
681
691
 
682
692
  scheduleOnUI(
683
- uiScheduler_, [weakThis = weak_from_this(), finalView, currentView, newChildShadowView, parentTag, opacity]() {
693
+ uiScheduler_,
694
+ [weakThis = weak_from_this(),
695
+ finalView,
696
+ currentView,
697
+ newChildShadowView,
698
+ parentTag,
699
+ opacity
700
+ #ifdef ANDROID
701
+ ,
702
+ handle
703
+ #endif
704
+ ]() {
684
705
  auto strongThis = weakThis.lock();
685
706
  if (!strongThis) {
686
707
  return;
@@ -690,6 +711,12 @@ void LayoutAnimationsProxy_Experimental::startEnteringAnimation(const std::share
690
711
  const auto tag = newChildShadowView.tag;
691
712
  {
692
713
  auto lock = std::unique_lock<std::recursive_mutex>(strongThis->mutex);
714
+ #ifdef ANDROID
715
+ if (consumeIsCancelled(strongThis->pendingStarts_, tag, handle)) {
716
+ // the view was removed before this start could run
717
+ return;
718
+ }
719
+ #endif
693
720
  strongThis->layoutAnimations_[tag] = {
694
721
  .finalView = newChildShadowView,
695
722
  .currentView = newChildShadowView,
@@ -723,38 +750,63 @@ void LayoutAnimationsProxy_Experimental::startExitingAnimation(const std::shared
723
750
  const auto &parent = node->parent.lock();
724
751
  react_native_assert(parent && "Parent node is nullptr");
725
752
  const auto parentTag = parent->current.tag;
753
+ #ifdef ANDROID
754
+ const auto handle = pendingStarts_[tag].handle;
755
+ pendingStarts_[tag].count++;
756
+ #endif
726
757
 
727
- scheduleOnUI(uiScheduler_, [weakThis = weak_from_this(), tag, parentTag, oldChildShadowView, surfaceId]() {
728
- auto strongThis = weakThis.lock();
729
- if (!strongThis) {
730
- return;
731
- }
758
+ scheduleOnUI(
759
+ uiScheduler_,
760
+ [weakThis = weak_from_this(),
761
+ tag,
762
+ parentTag,
763
+ oldChildShadowView,
764
+ surfaceId
765
+ #ifdef ANDROID
766
+ ,
767
+ handle
768
+ #endif
769
+ ]() {
770
+ auto strongThis = weakThis.lock();
771
+ if (!strongThis) {
772
+ return;
773
+ }
732
774
 
733
- auto oldView = oldChildShadowView;
734
- Rect window{};
735
- {
736
- auto &mutex = strongThis->mutex;
737
- auto lock = std::unique_lock<std::recursive_mutex>(mutex);
738
- oldView = strongThis->maybeCreateLayoutAnimation(oldView, oldView, parentTag);
739
- window = strongThis->surfaceManager.getWindow(surfaceId);
740
- }
775
+ auto oldView = oldChildShadowView;
776
+ Rect window{};
777
+ {
778
+ auto &mutex = strongThis->mutex;
779
+ auto lock = std::unique_lock<std::recursive_mutex>(mutex);
780
+ #ifdef ANDROID
781
+ if (consumeIsCancelled(strongThis->pendingStarts_, tag, handle)) {
782
+ // the view was removed (e.g. its subtree was force-ended by a screen
783
+ // pop) before this start could run — its Remove+Delete are already on
784
+ // their way to the mounting layer, so starting the animation now
785
+ // would emit updates for a view that's about to be deleted
786
+ strongThis->layoutAnimationsManager_->clearLayoutAnimationConfig(tag);
787
+ return;
788
+ }
789
+ #endif
790
+ oldView = strongThis->maybeCreateLayoutAnimation(oldView, oldView, parentTag);
791
+ window = strongThis->surfaceManager.getWindow(surfaceId);
792
+ }
741
793
 
742
- const Snapshot values(oldView, window);
794
+ const Snapshot values(oldView, window);
743
795
 
744
- auto &uiRuntime = strongThis->uiRuntime_;
745
- const jsi::Object yogaValues(uiRuntime);
746
- yogaValues.setProperty(uiRuntime, "currentOriginX", values.x);
747
- yogaValues.setProperty(uiRuntime, "currentGlobalOriginX", values.x);
748
- yogaValues.setProperty(uiRuntime, "currentOriginY", values.y);
749
- yogaValues.setProperty(uiRuntime, "currentGlobalOriginY", values.y);
750
- yogaValues.setProperty(uiRuntime, "currentWidth", values.width);
751
- yogaValues.setProperty(uiRuntime, "currentHeight", values.height);
752
- yogaValues.setProperty(uiRuntime, "windowWidth", values.windowWidth);
753
- yogaValues.setProperty(uiRuntime, "windowHeight", values.windowHeight);
754
- strongThis->layoutAnimationsManager_->startLayoutAnimation(
755
- uiRuntime, tag, LayoutAnimationType::EXITING, yogaValues);
756
- strongThis->layoutAnimationsManager_->clearLayoutAnimationConfig(tag);
757
- });
796
+ auto &uiRuntime = strongThis->uiRuntime_;
797
+ const jsi::Object yogaValues(uiRuntime);
798
+ yogaValues.setProperty(uiRuntime, "currentOriginX", values.x);
799
+ yogaValues.setProperty(uiRuntime, "currentGlobalOriginX", values.x);
800
+ yogaValues.setProperty(uiRuntime, "currentOriginY", values.y);
801
+ yogaValues.setProperty(uiRuntime, "currentGlobalOriginY", values.y);
802
+ yogaValues.setProperty(uiRuntime, "currentWidth", values.width);
803
+ yogaValues.setProperty(uiRuntime, "currentHeight", values.height);
804
+ yogaValues.setProperty(uiRuntime, "windowWidth", values.windowWidth);
805
+ yogaValues.setProperty(uiRuntime, "windowHeight", values.windowHeight);
806
+ strongThis->layoutAnimationsManager_->startLayoutAnimation(
807
+ uiRuntime, tag, LayoutAnimationType::EXITING, yogaValues);
808
+ strongThis->layoutAnimationsManager_->clearLayoutAnimationConfig(tag);
809
+ });
758
810
  }
759
811
 
760
812
  void LayoutAnimationsProxy_Experimental::startLayoutAnimation(const std::shared_ptr<LightNode> &node) const {
@@ -765,9 +817,24 @@ void LayoutAnimationsProxy_Experimental::startLayoutAnimation(const std::shared_
765
817
  const auto &parent = node->parent.lock();
766
818
  react_native_assert(parent && "Parent node is nullptr");
767
819
  const auto parentTag = parent->current.tag;
820
+ #ifdef ANDROID
821
+ const auto handle = pendingStarts_[tag].handle;
822
+ pendingStarts_[tag].count++;
823
+ #endif
768
824
 
769
825
  scheduleOnUI(
770
- uiScheduler_, [weakThis = weak_from_this(), surfaceId, oldChildShadowView, newChildShadowView, parentTag, tag]() {
826
+ uiScheduler_,
827
+ [weakThis = weak_from_this(),
828
+ surfaceId,
829
+ oldChildShadowView,
830
+ newChildShadowView,
831
+ parentTag,
832
+ tag
833
+ #ifdef ANDROID
834
+ ,
835
+ handle
836
+ #endif
837
+ ]() {
771
838
  auto strongThis = weakThis.lock();
772
839
  if (!strongThis) {
773
840
  return;
@@ -778,6 +845,12 @@ void LayoutAnimationsProxy_Experimental::startLayoutAnimation(const std::shared_
778
845
  {
779
846
  auto &mutex = strongThis->mutex;
780
847
  auto lock = std::unique_lock<std::recursive_mutex>(mutex);
848
+ #ifdef ANDROID
849
+ if (consumeIsCancelled(strongThis->pendingStarts_, tag, handle)) {
850
+ // the view was removed before this start could run
851
+ return;
852
+ }
853
+ #endif
781
854
  oldView = strongThis->maybeCreateLayoutAnimation(oldView, newChildShadowView, parentTag);
782
855
  window = strongThis->surfaceManager.getWindow(surfaceId);
783
856
  }
@@ -39,6 +39,8 @@ std::optional<MountingTransaction> LayoutAnimationsProxy_Legacy::pullTransaction
39
39
  std::vector<std::shared_ptr<MutationNode>> roots;
40
40
  std::unordered_map<Tag, Tag> movedViews;
41
41
 
42
+ reconcileContradictedRemovals(mutations, filteredMutations, surfaceId);
43
+
42
44
  addOngoingAnimations(surfaceId, filteredMutations);
43
45
 
44
46
  #ifdef ANDROID
@@ -64,6 +66,40 @@ std::optional<MountingTransaction> LayoutAnimationsProxy_Legacy::pullTransaction
64
66
  return MountingTransaction{surfaceId, transactionNumber, std::move(filteredMutations), telemetry};
65
67
  }
66
68
 
69
+ // If React re-creates or re-inserts a tag whose exiting removal we are still
70
+ // withholding, it has contradicted that withheld Remove/Delete. Flush it now
71
+ // instead of letting it fire later against a stale hierarchy (which would
72
+ // unmount the wrong, still-live view and crash the mounting layer).
73
+ //
74
+ // This must run before addOngoingAnimations (which would otherwise emit an
75
+ // Update for a tag we are about to Delete this frame) and before
76
+ // parseRemoveMutations, so the rest of the pipeline sees clean bookkeeping.
77
+ void LayoutAnimationsProxy_Legacy::reconcileContradictedRemovals(
78
+ ShadowViewMutationList &mutations,
79
+ ShadowViewMutationList &filteredMutations,
80
+ SurfaceId surfaceId) const {
81
+ auto &[deadNodes] = surfaceContext_[surfaceId];
82
+ for (auto &mutation : mutations) {
83
+ if (mutation.type != ShadowViewMutation::Type::Create && mutation.type != ShadowViewMutation::Type::Insert) {
84
+ continue;
85
+ }
86
+ auto tag = mutation.newChildShadowView.tag;
87
+ auto it = nodeForTag_.find(tag);
88
+ // Only a MutationNode represents a withheld removal; a plain Node is just a
89
+ // live parent of some removed child and must be left untouched.
90
+ if (it == nodeForTag_.end() || !it->second->isMutationNode()) {
91
+ continue;
92
+ }
93
+ auto node = std::static_pointer_cast<MutationNode>(it->second);
94
+ // Flush the withheld Remove/Delete for this tag (and its withheld subtree)
95
+ // right now, mirroring the deadNodes cleanup in handleRemovals. This removes
96
+ // the stale view before React's Create/Insert re-registers the same tag.
97
+ endAnimationsRecursively(node, filteredMutations);
98
+ maybeDropAncestors(node->unflattenedParent, node, filteredMutations);
99
+ deadNodes.erase(node);
100
+ }
101
+ }
102
+
67
103
  std::optional<SurfaceId> LayoutAnimationsProxy_Legacy::progressLayoutAnimation(int tag, const jsi::Object &newStyle) {
68
104
  #ifdef LAYOUT_ANIMATIONS_LOGS
69
105
  LOG(INFO) << "progress layout animation for tag " << tag << std::endl;
@@ -123,6 +159,10 @@ std::optional<SurfaceId> LayoutAnimationsProxy_Legacy::endLayoutAnimation(int ta
123
159
  }
124
160
 
125
161
  auto node = nodeForTag_[tag];
162
+ if (!node->isMutationNode()) {
163
+ react_native_assert(false && "exiting tag must map to a MutationNode");
164
+ return {};
165
+ }
126
166
  auto mutationNode = std::static_pointer_cast<MutationNode>(node);
127
167
  mutationNode->state = ExitingState_Legacy::DEAD;
128
168
  auto &[deadNodes] = surfaceContext_[surfaceId];
@@ -635,38 +675,60 @@ void LayoutAnimationsProxy_Legacy::startEnteringAnimation(const int tag, ShadowV
635
675
 
636
676
  auto &viewProps = static_cast<const ViewProps &>(*mutation.newChildShadowView.props);
637
677
  auto opacity = viewProps.opacity;
678
+ #ifdef ANDROID
679
+ const auto handle = pendingStarts_[tag].handle;
680
+ pendingStarts_[tag].count++;
681
+ #endif
638
682
 
639
- scheduleOnUI(uiScheduler_, [weakThis = weak_from_this(), finalView, current, mutation, opacity, tag]() {
640
- auto strongThis = weakThis.lock();
641
- if (!strongThis) {
642
- return;
643
- }
683
+ scheduleOnUI(
684
+ uiScheduler_,
685
+ [weakThis = weak_from_this(),
686
+ finalView,
687
+ current,
688
+ mutation,
689
+ opacity,
690
+ tag
691
+ #ifdef ANDROID
692
+ ,
693
+ handle
694
+ #endif
695
+ ]() {
696
+ auto strongThis = weakThis.lock();
697
+ if (!strongThis) {
698
+ return;
699
+ }
644
700
 
645
- Rect window{};
646
- {
647
- auto &mutex = strongThis->mutex;
648
- auto lock = std::unique_lock<std::recursive_mutex>(mutex);
649
- strongThis->layoutAnimations_.insert_or_assign(
650
- tag,
651
- LayoutAnimation{
652
- .finalView = finalView, .currentView = current, .parentTag = mutation.parentTag, .opacity = opacity});
653
- window = strongThis->surfaceManager.getWindow(mutation.newChildShadowView.surfaceId);
654
- }
701
+ Rect window{};
702
+ {
703
+ auto &mutex = strongThis->mutex;
704
+ auto lock = std::unique_lock<std::recursive_mutex>(mutex);
705
+ #ifdef ANDROID
706
+ if (consumeIsCancelled(strongThis->pendingStarts_, tag, handle)) {
707
+ // the view was removed before this start could run
708
+ return;
709
+ }
710
+ #endif
711
+ strongThis->layoutAnimations_.insert_or_assign(
712
+ tag,
713
+ LayoutAnimation{
714
+ .finalView = finalView, .currentView = current, .parentTag = mutation.parentTag, .opacity = opacity});
715
+ window = strongThis->surfaceManager.getWindow(mutation.newChildShadowView.surfaceId);
716
+ }
655
717
 
656
- Snapshot values(mutation.newChildShadowView, window);
657
- auto &uiRuntime = strongThis->uiRuntime_;
658
- jsi::Object yogaValues(uiRuntime);
659
- yogaValues.setProperty(uiRuntime, "targetOriginX", values.x);
660
- yogaValues.setProperty(uiRuntime, "targetGlobalOriginX", values.x);
661
- yogaValues.setProperty(uiRuntime, "targetOriginY", values.y);
662
- yogaValues.setProperty(uiRuntime, "targetGlobalOriginY", values.y);
663
- yogaValues.setProperty(uiRuntime, "targetWidth", values.width);
664
- yogaValues.setProperty(uiRuntime, "targetHeight", values.height);
665
- yogaValues.setProperty(uiRuntime, "windowWidth", values.windowWidth);
666
- yogaValues.setProperty(uiRuntime, "windowHeight", values.windowHeight);
667
- strongThis->layoutAnimationsManager_->startLayoutAnimation(
668
- uiRuntime, tag, LayoutAnimationType::ENTERING, yogaValues);
669
- });
718
+ Snapshot values(mutation.newChildShadowView, window);
719
+ auto &uiRuntime = strongThis->uiRuntime_;
720
+ jsi::Object yogaValues(uiRuntime);
721
+ yogaValues.setProperty(uiRuntime, "targetOriginX", values.x);
722
+ yogaValues.setProperty(uiRuntime, "targetGlobalOriginX", values.x);
723
+ yogaValues.setProperty(uiRuntime, "targetOriginY", values.y);
724
+ yogaValues.setProperty(uiRuntime, "targetGlobalOriginY", values.y);
725
+ yogaValues.setProperty(uiRuntime, "targetWidth", values.width);
726
+ yogaValues.setProperty(uiRuntime, "targetHeight", values.height);
727
+ yogaValues.setProperty(uiRuntime, "windowWidth", values.windowWidth);
728
+ yogaValues.setProperty(uiRuntime, "windowHeight", values.windowHeight);
729
+ strongThis->layoutAnimationsManager_->startLayoutAnimation(
730
+ uiRuntime, tag, LayoutAnimationType::ENTERING, yogaValues);
731
+ });
670
732
  }
671
733
 
672
734
  void LayoutAnimationsProxy_Legacy::startExitingAnimation(const int tag, ShadowViewMutation &mutation) const {
@@ -674,38 +736,62 @@ void LayoutAnimationsProxy_Legacy::startExitingAnimation(const int tag, ShadowVi
674
736
  LOG(INFO) << "start exiting animation for tag " << tag << std::endl;
675
737
  #endif
676
738
  auto surfaceId = mutation.oldChildShadowView.surfaceId;
739
+ #ifdef ANDROID
740
+ const auto handle = pendingStarts_[tag].handle;
741
+ pendingStarts_[tag].count++;
742
+ #endif
677
743
 
678
- scheduleOnUI(uiScheduler_, [weakThis = weak_from_this(), tag, mutation, surfaceId]() {
679
- auto strongThis = weakThis.lock();
680
- if (!strongThis) {
681
- return;
682
- }
683
-
684
- auto oldView = mutation.oldChildShadowView;
685
- Rect window{};
686
- {
687
- auto &mutex = strongThis->mutex;
688
- auto lock = std::unique_lock<std::recursive_mutex>(mutex);
689
- strongThis->createLayoutAnimation(mutation, oldView, surfaceId, tag);
690
- window = strongThis->surfaceManager.getWindow(surfaceId);
691
- }
744
+ scheduleOnUI(
745
+ uiScheduler_,
746
+ [weakThis = weak_from_this(),
747
+ tag,
748
+ mutation,
749
+ surfaceId
750
+ #ifdef ANDROID
751
+ ,
752
+ handle
753
+ #endif
754
+ ]() {
755
+ auto strongThis = weakThis.lock();
756
+ if (!strongThis) {
757
+ return;
758
+ }
692
759
 
693
- Snapshot values(oldView, window);
760
+ auto oldView = mutation.oldChildShadowView;
761
+ Rect window{};
762
+ {
763
+ auto &mutex = strongThis->mutex;
764
+ auto lock = std::unique_lock<std::recursive_mutex>(mutex);
765
+ #ifdef ANDROID
766
+ if (consumeIsCancelled(strongThis->pendingStarts_, tag, handle)) {
767
+ // the view was removed (e.g. its subtree was force-ended by a screen
768
+ // pop) before this start could run — its Remove+Delete are already on
769
+ // their way to the mounting layer, so starting the animation now
770
+ // would emit updates for a view that's about to be deleted
771
+ strongThis->layoutAnimationsManager_->clearLayoutAnimationConfig(tag);
772
+ return;
773
+ }
774
+ #endif
775
+ strongThis->createLayoutAnimation(mutation, oldView, surfaceId, tag);
776
+ window = strongThis->surfaceManager.getWindow(surfaceId);
777
+ }
694
778
 
695
- auto &uiRuntime = strongThis->uiRuntime_;
696
- jsi::Object yogaValues(uiRuntime);
697
- yogaValues.setProperty(uiRuntime, "currentOriginX", values.x);
698
- yogaValues.setProperty(uiRuntime, "currentGlobalOriginX", values.x);
699
- yogaValues.setProperty(uiRuntime, "currentOriginY", values.y);
700
- yogaValues.setProperty(uiRuntime, "currentGlobalOriginY", values.y);
701
- yogaValues.setProperty(uiRuntime, "currentWidth", values.width);
702
- yogaValues.setProperty(uiRuntime, "currentHeight", values.height);
703
- yogaValues.setProperty(uiRuntime, "windowWidth", values.windowWidth);
704
- yogaValues.setProperty(uiRuntime, "windowHeight", values.windowHeight);
705
- strongThis->layoutAnimationsManager_->startLayoutAnimation(
706
- uiRuntime, tag, LayoutAnimationType::EXITING, yogaValues);
707
- strongThis->layoutAnimationsManager_->clearLayoutAnimationConfig(tag);
708
- });
779
+ Snapshot values(oldView, window);
780
+
781
+ auto &uiRuntime = strongThis->uiRuntime_;
782
+ jsi::Object yogaValues(uiRuntime);
783
+ yogaValues.setProperty(uiRuntime, "currentOriginX", values.x);
784
+ yogaValues.setProperty(uiRuntime, "currentGlobalOriginX", values.x);
785
+ yogaValues.setProperty(uiRuntime, "currentOriginY", values.y);
786
+ yogaValues.setProperty(uiRuntime, "currentGlobalOriginY", values.y);
787
+ yogaValues.setProperty(uiRuntime, "currentWidth", values.width);
788
+ yogaValues.setProperty(uiRuntime, "currentHeight", values.height);
789
+ yogaValues.setProperty(uiRuntime, "windowWidth", values.windowWidth);
790
+ yogaValues.setProperty(uiRuntime, "windowHeight", values.windowHeight);
791
+ strongThis->layoutAnimationsManager_->startLayoutAnimation(
792
+ uiRuntime, tag, LayoutAnimationType::EXITING, yogaValues);
793
+ strongThis->layoutAnimationsManager_->clearLayoutAnimationConfig(tag);
794
+ });
709
795
  }
710
796
 
711
797
  void LayoutAnimationsProxy_Legacy::startLayoutAnimation(const int tag, const ShadowViewMutation &mutation) const {
@@ -713,43 +799,64 @@ void LayoutAnimationsProxy_Legacy::startLayoutAnimation(const int tag, const Sha
713
799
  LOG(INFO) << "start layout animation for tag " << tag << std::endl;
714
800
  #endif
715
801
  auto surfaceId = mutation.oldChildShadowView.surfaceId;
802
+ #ifdef ANDROID
803
+ const auto handle = pendingStarts_[tag].handle;
804
+ pendingStarts_[tag].count++;
805
+ #endif
716
806
 
717
- scheduleOnUI(uiScheduler_, [weakThis = weak_from_this(), mutation, surfaceId, tag]() {
718
- auto strongThis = weakThis.lock();
719
- if (!strongThis) {
720
- return;
721
- }
722
-
723
- auto oldView = mutation.oldChildShadowView;
724
- Rect window{};
725
- {
726
- auto &mutex = strongThis->mutex;
727
- auto lock = std::unique_lock<std::recursive_mutex>(mutex);
728
- strongThis->createLayoutAnimation(mutation, oldView, surfaceId, tag);
729
- window = strongThis->surfaceManager.getWindow(surfaceId);
730
- }
807
+ scheduleOnUI(
808
+ uiScheduler_,
809
+ [weakThis = weak_from_this(),
810
+ mutation,
811
+ surfaceId,
812
+ tag
813
+ #ifdef ANDROID
814
+ ,
815
+ handle
816
+ #endif
817
+ ]() {
818
+ auto strongThis = weakThis.lock();
819
+ if (!strongThis) {
820
+ return;
821
+ }
731
822
 
732
- Snapshot currentValues(oldView, window);
733
- Snapshot targetValues(mutation.newChildShadowView, window);
823
+ auto oldView = mutation.oldChildShadowView;
824
+ Rect window{};
825
+ {
826
+ auto &mutex = strongThis->mutex;
827
+ auto lock = std::unique_lock<std::recursive_mutex>(mutex);
828
+ #ifdef ANDROID
829
+ if (consumeIsCancelled(strongThis->pendingStarts_, tag, handle)) {
830
+ // the view was removed before this start could run
831
+ return;
832
+ }
833
+ #endif
834
+ strongThis->createLayoutAnimation(mutation, oldView, surfaceId, tag);
835
+ window = strongThis->surfaceManager.getWindow(surfaceId);
836
+ }
734
837
 
735
- auto &uiRuntime = strongThis->uiRuntime_;
736
- jsi::Object yogaValues(uiRuntime);
737
- yogaValues.setProperty(uiRuntime, "currentOriginX", currentValues.x);
738
- yogaValues.setProperty(uiRuntime, "currentGlobalOriginX", currentValues.x);
739
- yogaValues.setProperty(uiRuntime, "currentOriginY", currentValues.y);
740
- yogaValues.setProperty(uiRuntime, "currentGlobalOriginY", currentValues.y);
741
- yogaValues.setProperty(uiRuntime, "currentWidth", currentValues.width);
742
- yogaValues.setProperty(uiRuntime, "currentHeight", currentValues.height);
743
- yogaValues.setProperty(uiRuntime, "targetOriginX", targetValues.x);
744
- yogaValues.setProperty(uiRuntime, "targetGlobalOriginX", targetValues.x);
745
- yogaValues.setProperty(uiRuntime, "targetOriginY", targetValues.y);
746
- yogaValues.setProperty(uiRuntime, "targetGlobalOriginY", targetValues.y);
747
- yogaValues.setProperty(uiRuntime, "targetWidth", targetValues.width);
748
- yogaValues.setProperty(uiRuntime, "targetHeight", targetValues.height);
749
- yogaValues.setProperty(uiRuntime, "windowWidth", targetValues.windowWidth);
750
- yogaValues.setProperty(uiRuntime, "windowHeight", targetValues.windowHeight);
751
- strongThis->layoutAnimationsManager_->startLayoutAnimation(uiRuntime, tag, LayoutAnimationType::LAYOUT, yogaValues);
752
- });
838
+ Snapshot currentValues(oldView, window);
839
+ Snapshot targetValues(mutation.newChildShadowView, window);
840
+
841
+ auto &uiRuntime = strongThis->uiRuntime_;
842
+ jsi::Object yogaValues(uiRuntime);
843
+ yogaValues.setProperty(uiRuntime, "currentOriginX", currentValues.x);
844
+ yogaValues.setProperty(uiRuntime, "currentGlobalOriginX", currentValues.x);
845
+ yogaValues.setProperty(uiRuntime, "currentOriginY", currentValues.y);
846
+ yogaValues.setProperty(uiRuntime, "currentGlobalOriginY", currentValues.y);
847
+ yogaValues.setProperty(uiRuntime, "currentWidth", currentValues.width);
848
+ yogaValues.setProperty(uiRuntime, "currentHeight", currentValues.height);
849
+ yogaValues.setProperty(uiRuntime, "targetOriginX", targetValues.x);
850
+ yogaValues.setProperty(uiRuntime, "targetGlobalOriginX", targetValues.x);
851
+ yogaValues.setProperty(uiRuntime, "targetOriginY", targetValues.y);
852
+ yogaValues.setProperty(uiRuntime, "targetGlobalOriginY", targetValues.y);
853
+ yogaValues.setProperty(uiRuntime, "targetWidth", targetValues.width);
854
+ yogaValues.setProperty(uiRuntime, "targetHeight", targetValues.height);
855
+ yogaValues.setProperty(uiRuntime, "windowWidth", targetValues.windowWidth);
856
+ yogaValues.setProperty(uiRuntime, "windowHeight", targetValues.windowHeight);
857
+ strongThis->layoutAnimationsManager_->startLayoutAnimation(
858
+ uiRuntime, tag, LayoutAnimationType::LAYOUT, yogaValues);
859
+ });
753
860
  }
754
861
 
755
862
  void LayoutAnimationsProxy_Legacy::updateOngoingAnimationTarget(const int tag, const ShadowViewMutation &mutation)
@@ -759,6 +866,12 @@ void LayoutAnimationsProxy_Legacy::updateOngoingAnimationTarget(const int tag, c
759
866
  }
760
867
 
761
868
  void LayoutAnimationsProxy_Legacy::maybeCancelAnimation(const int tag) const {
869
+ #ifdef ANDROID
870
+ // invalidate animation starts that are scheduled but haven't run yet
871
+ if (const auto it = pendingStarts_.find(tag); it != pendingStarts_.end()) {
872
+ it->second.handle++;
873
+ }
874
+ #endif
762
875
  if (!layoutAnimations_.contains(tag)) {
763
876
  return;
764
877
  }
@@ -148,6 +148,10 @@ struct LayoutAnimationsProxy_Legacy : public LayoutAnimationsProxyCommon,
148
148
  std::optional<SurfaceId> endLayoutAnimation(int tag, bool shouldRemove) override;
149
149
  void maybeCancelAnimation(const int tag) const;
150
150
 
151
+ void reconcileContradictedRemovals(
152
+ ShadowViewMutationList &mutations,
153
+ ShadowViewMutationList &filteredMutations,
154
+ SurfaceId surfaceId) const;
151
155
  void parseRemoveMutations(
152
156
  std::unordered_map<Tag, Tag> &movedViews,
153
157
  ShadowViewMutationList &mutations,
@@ -524,15 +524,13 @@ jsi::Value ReanimatedModuleProxy::getSettledUpdates(jsi::Runtime &rt) {
524
524
  StaticFeatureFlags::getFlag("FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS") &&
525
525
  "getSettledUpdates requires FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS static feature flag to be enabled");
526
526
 
527
+ constexpr double SETTLED_ANIMATION_THRESHOLD_MS = 1000;
528
+
527
529
  // TODO(future): use unified timestamp
528
530
  const auto currentTimestamp = getAnimationTimestamp_();
529
531
 
530
- // TODO: fix bug when threshold difference is smaller than 1 second
531
532
  // TODO(future): flush updates from CSS animations and CSS transitions registries
532
- // TODO(future): find a better way to obtain timestamp for removing updates
533
- // TODO(future): move removing old updates to separate method
534
- return animatedPropsRegistry_->getUpdatesOlderThanTimestamp(
535
- rt, currentTimestamp - 1000 /* 1 second */, currentTimestamp - 2000 /* 2 seconds */);
533
+ return animatedPropsRegistry_->collectSettledUpdates(rt, currentTimestamp - SETTLED_ANIMATION_THRESHOLD_MS);
536
534
  }
537
535
 
538
536
  bool ReanimatedModuleProxy::handleEvent(
@@ -717,11 +715,6 @@ void ReanimatedModuleProxy::performOperations() {
717
715
  }
718
716
 
719
717
  commitUpdates(uiRuntime, updatesBatch);
720
-
721
- // Clear the entire cache after the commit
722
- // (we don't know if the view is updated from outside of Reanimated
723
- // so we have to clear the entire cache)
724
- viewStylesRepository_->clearNodesCache();
725
718
  }
726
719
 
727
720
  void ReanimatedModuleProxy::performNonLayoutOperations() {
@@ -1292,7 +1285,8 @@ void ReanimatedModuleProxy::initializeFabric(const std::shared_ptr<UIManager> &u
1292
1285
 
1293
1286
  strongThis->requestFlushRegistry();
1294
1287
  };
1295
- mountHook_ = std::make_shared<ReanimatedMountHook>(uiManager_, updatesRegistryManager_, request);
1288
+ mountHook_ =
1289
+ std::make_shared<ReanimatedMountHook>(uiManager_, updatesRegistryManager_, viewStylesRepository_, request);
1296
1290
  commitHook_ = std::make_shared<ReanimatedCommitHook>(uiManager_, updatesRegistryManager_, layoutAnimationsProxy_);
1297
1291
  }
1298
1292
 
@@ -1,5 +1,5 @@
1
1
  project(Reanimated)
2
- cmake_minimum_required(VERSION 3.8)
2
+ cmake_minimum_required(VERSION 3.16)
3
3
 
4
4
  set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
5
5
 
@@ -50,6 +50,9 @@ find_package(react-native-worklets REQUIRED CONFIG)
50
50
  add_library(reanimated SHARED ${REANIMATED_COMMON_CPP_SOURCES}
51
51
  ${REANIMATED_ANDROID_CPP_SOURCES})
52
52
 
53
+ target_precompile_headers(reanimated PRIVATE
54
+ "${ANDROID_CPP_DIR}/ReanimatedPCH.h")
55
+
53
56
  if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 80)
54
57
  include(
55
58
  "${REACT_NATIVE_DIR}/ReactCommon/cmake-utils/react-native-flags.cmake")
@@ -130,6 +130,8 @@ if (project == rootProject) {
130
130
  apply plugin: "com.android.library"
131
131
  apply plugin: "maven-publish"
132
132
 
133
+ apply from: "./generate-stub-pch.gradle.kts"
134
+
133
135
  android {
134
136
  compileSdkVersion safeExtGet("compileSdkVersion", 36)
135
137