From fd748ae84cf18c1c204a936af45bb4883d476cd7 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Wed, 17 Jul 2024 09:22:39 -0700 Subject: [PATCH] Receive the onUserDrivenAnimationEnded event in JS (#45383) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45383 This is the second step required to fix the onTouchMove event in the new architecture. In this change, we are retrieving the list of nodes that are connected by the animation, and we are sending an event to the nodes so that we can trigger the commit. ## Changelog [iOS][Added] - retrieve the tags of the nodes connected by the animation and send them to JS Reviewed By: sammy-SC Differential Revision: D59524617 fbshipit-source-id: 584317afa8e4cf0ad9f98f38e4e5d436c5fe3ac5 --- .../RCTNativeAnimatedNodesManager.h | 2 + .../RCTNativeAnimatedNodesManager.mm | 18 ++++++++ .../RCTNativeAnimatedTurboModule.mm | 41 ++++++++++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h b/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h index 932c873eb6c..60798329cd8 100644 --- a/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h +++ b/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h @@ -87,6 +87,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)stopListeningToAnimatedNodeValue:(NSNumber *)tag; +- (NSSet *)getTagsOfConnectedNodesFrom:(NSNumber *)tag andEvent:(NSString *)eventName; + @end NS_ASSUME_NONNULL_END diff --git a/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.mm b/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.mm index c195025f316..db3e2f8b4e1 100644 --- a/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.mm +++ b/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.mm @@ -479,6 +479,24 @@ static NSString *RCTNormalizeAnimatedEventName(NSString *eventName) [self stopAnimationLoopIfNeeded]; } +- (NSSet *)getTagsOfConnectedNodesFrom:(NSNumber *)tag andEvent:(NSString *)eventName +{ + NSMutableSet *tags = [NSMutableSet new]; + NSString *key = [NSString stringWithFormat:@"%@%@", tag, RCTNormalizeAnimatedEventName(eventName)]; + NSArray *eventAnimations = _eventDrivers[key]; + for (RCTEventAnimation *animation in eventAnimations) { + NSNumber *nodeTag = [animation.valueNode nodeTag]; + if (nodeTag) { + [tags addObject:nodeTag]; + } + for (NSNumber *childNodeKey in [animation.valueNode childNodes]) { + [tags addObject:childNodeKey]; + } + } + + return tags; +} + #pragma mark-- Updates - (void)updateAnimations diff --git a/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm b/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm index cdf38d90dcd..4d16e500925 100644 --- a/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm +++ b/packages/react-native/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm @@ -25,6 +25,11 @@ typedef void (^AnimatedOperation)(RCTNativeAnimatedNodesManager *nodesManager); NSMutableArray *_operations; // Operations called before views have been updated. NSMutableArray *_preOperations; + + NSSet *_userDrivenAnimationEndedEvents; + + // TODO: Remove this when https://github.com/facebook/react-native/pull/45457 lands + BOOL _shouldEmitEvent; } RCT_EXPORT_MODULE(); @@ -39,6 +44,8 @@ RCT_EXPORT_MODULE(); if (self = [super init]) { _operations = [NSMutableArray new]; _preOperations = [NSMutableArray new]; + _userDrivenAnimationEndedEvents = [NSSet setWithArray:@[ @"onScrollEnded" ]]; + _shouldEmitEvent = NO; } return self; } @@ -364,7 +371,7 @@ RCT_EXPORT_METHOD(queueAndExecuteBatchedOperations : (NSArray *)operationsAndArg - (NSArray *)supportedEvents { - return @[ @"onAnimatedValueUpdate" ]; + return @[ @"onAnimatedValueUpdate", @"onUserDrivenAnimationEnded" ]; } - (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value @@ -372,12 +379,44 @@ RCT_EXPORT_METHOD(queueAndExecuteBatchedOperations : (NSArray *)operationsAndArg [self sendEventWithName:@"onAnimatedValueUpdate" body:@{@"tag" : node.nodeTag, @"value" : @(value)}]; } +// TODO: Remove this when https://github.com/facebook/react-native/pull/45457 lands +- (void)startObserving +{ + [super startObserving]; + _shouldEmitEvent = YES; +} + +- (void)stopObserving +{ + [super stopObserving]; + _shouldEmitEvent = NO; +} + +// ---- + +- (void)userDrivenAnimationEnded:(NSArray *)nodes +{ + if (!_shouldEmitEvent) { + return; + } + + [self sendEventWithName:@"onUserDrivenAnimationEnded" body:@{@"tags" : nodes}]; +} + - (void)eventDispatcherWillDispatchEvent:(id)event { // Events can be dispatched from any queue so we have to make sure handleAnimatedEvent // is run from the main queue. RCTExecuteOnMainQueue(^{ [self->_nodesManager handleAnimatedEvent:event]; + + if ([self->_userDrivenAnimationEndedEvents containsObject:event.eventName]) { + NSSet *tags = [self->_nodesManager getTagsOfConnectedNodesFrom:event.viewTag + andEvent:event.eventName]; + if (tags.count > 0) { + [self userDrivenAnimationEnded:[tags allObjects]]; + } + } }); }