1/* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * 4 * This source code is licensed under the MIT license found in the 5 * LICENSE file in the root directory of this source tree. 6 */ 7 8#import <ABI48_0_0FBReactNativeSpec/ABI48_0_0FBReactNativeSpec.h> 9#import <ABI48_0_0RCTTypeSafety/ABI48_0_0RCTConvertHelpers.h> 10#import <ABI48_0_0React/ABI48_0_0RCTInitializing.h> 11#import <ABI48_0_0React/ABI48_0_0RCTNativeAnimatedNodesManager.h> 12#import <ABI48_0_0React/ABI48_0_0RCTNativeAnimatedTurboModule.h> 13 14#import "ABI48_0_0RCTAnimationPlugins.h" 15 16typedef void (^AnimatedOperation)(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager); 17 18@interface ABI48_0_0RCTNativeAnimatedTurboModule () <ABI48_0_0NativeAnimatedModuleSpec, ABI48_0_0RCTInitializing> 19@end 20 21@implementation ABI48_0_0RCTNativeAnimatedTurboModule { 22 ABI48_0_0RCTNativeAnimatedNodesManager *_nodesManager; 23 __weak id<ABI48_0_0RCTSurfacePresenterStub> _surfacePresenter; 24 // Operations called after views have been updated. 25 NSMutableArray<AnimatedOperation> *_operations; 26 // Operations called before views have been updated. 27 NSMutableArray<AnimatedOperation> *_preOperations; 28} 29 30ABI48_0_0RCT_EXPORT_MODULE(); 31 32+ (BOOL)requiresMainQueueSetup 33{ 34 return NO; 35} 36 37- (instancetype)init 38{ 39 if (self = [super init]) { 40 _operations = [NSMutableArray new]; 41 _preOperations = [NSMutableArray new]; 42 } 43 return self; 44} 45 46- (void)initialize 47{ 48 // _surfacePresenter set in setSurfacePresenter: 49 _nodesManager = [[ABI48_0_0RCTNativeAnimatedNodesManager alloc] initWithBridge:nil surfacePresenter:_surfacePresenter]; 50 [_surfacePresenter addObserver:self]; 51 [[self.moduleRegistry moduleForName:"EventDispatcher"] addDispatchObserver:self]; 52} 53 54- (void)invalidate 55{ 56 [super invalidate]; 57 [_nodesManager stopAnimationLoop]; 58 [[self.moduleRegistry moduleForName:"EventDispatcher"] removeDispatchObserver:self]; 59 [_surfacePresenter removeObserver:self]; 60} 61 62- (dispatch_queue_t)methodQueue 63{ 64 // This module needs to be on the same queue as the UIManager to avoid 65 // having to lock `_operations` and `_preOperations` since `uiManagerWillPerformMounting` 66 // will be called from that queue. 67 return ABI48_0_0RCTGetUIManagerQueue(); 68} 69 70/* 71 * In bridgeless mode, `setBridge` is never called during initializtion. Instead this selector is invoked via 72 * BridgelessTurboModuleSetup. 73 */ 74- (void)setSurfacePresenter:(id<ABI48_0_0RCTSurfacePresenterStub>)surfacePresenter 75{ 76 _surfacePresenter = surfacePresenter; 77} 78 79#pragma mark-- API 80 81ABI48_0_0RCT_EXPORT_METHOD(startOperationBatch) 82{ 83 // TODO T71377585 84} 85 86ABI48_0_0RCT_EXPORT_METHOD(finishOperationBatch) 87{ 88 // TODO T71377585 89} 90 91ABI48_0_0RCT_EXPORT_METHOD(createAnimatedNode : (double)tag config : (NSDictionary<NSString *, id> *)config) 92{ 93 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 94 [nodesManager createAnimatedNode:[NSNumber numberWithDouble:tag] config:config]; 95 }]; 96} 97 98ABI48_0_0RCT_EXPORT_METHOD(updateAnimatedNodeConfig : (double)tag config : (NSDictionary<NSString *, id> *)config) 99{ 100 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 101 [nodesManager updateAnimatedNodeConfig:[NSNumber numberWithDouble:tag] config:config]; 102 }]; 103} 104 105ABI48_0_0RCT_EXPORT_METHOD(connectAnimatedNodes : (double)parentTag childTag : (double)childTag) 106{ 107 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 108 [nodesManager connectAnimatedNodes:[NSNumber numberWithDouble:parentTag] 109 childTag:[NSNumber numberWithDouble:childTag]]; 110 }]; 111} 112 113ABI48_0_0RCT_EXPORT_METHOD(disconnectAnimatedNodes : (double)parentTag childTag : (double)childTag) 114{ 115 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 116 [nodesManager disconnectAnimatedNodes:[NSNumber numberWithDouble:parentTag] 117 childTag:[NSNumber numberWithDouble:childTag]]; 118 }]; 119} 120 121ABI48_0_0RCT_EXPORT_METHOD(startAnimatingNode 122 : (double)animationId nodeTag 123 : (double)nodeTag config 124 : (NSDictionary<NSString *, id> *)config endCallback 125 : (ABI48_0_0RCTResponseSenderBlock)callBack) 126{ 127 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 128 [nodesManager startAnimatingNode:[NSNumber numberWithDouble:animationId] 129 nodeTag:[NSNumber numberWithDouble:nodeTag] 130 config:config 131 endCallback:callBack]; 132 }]; 133 134 [self flushOperationQueues]; 135} 136 137ABI48_0_0RCT_EXPORT_METHOD(stopAnimation : (double)animationId) 138{ 139 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 140 [nodesManager stopAnimation:[NSNumber numberWithDouble:animationId]]; 141 }]; 142 [self flushOperationQueues]; 143} 144 145ABI48_0_0RCT_EXPORT_METHOD(setAnimatedNodeValue : (double)nodeTag value : (double)value) 146{ 147 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 148 [nodesManager setAnimatedNodeValue:[NSNumber numberWithDouble:nodeTag] value:[NSNumber numberWithDouble:value]]; 149 }]; 150 // In Bridge, flushing of native animations is done from ABI48_0_0RCTCxxBridge batchDidComplete(). 151 // Since ABI48_0_0RCTCxxBridge doesn't exist in Bridgeless, and components are not remounted in Fabric for native animations, 152 // flush here for changes in Animated.Value for Animated.event. 153 [self flushOperationQueues]; 154} 155 156ABI48_0_0RCT_EXPORT_METHOD(setAnimatedNodeOffset : (double)nodeTag offset : (double)offset) 157{ 158 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 159 [nodesManager setAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag] offset:[NSNumber numberWithDouble:offset]]; 160 }]; 161} 162 163ABI48_0_0RCT_EXPORT_METHOD(flattenAnimatedNodeOffset : (double)nodeTag) 164{ 165 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 166 [nodesManager flattenAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]]; 167 }]; 168} 169 170ABI48_0_0RCT_EXPORT_METHOD(extractAnimatedNodeOffset : (double)nodeTag) 171{ 172 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 173 [nodesManager extractAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]]; 174 }]; 175} 176 177ABI48_0_0RCT_EXPORT_METHOD(connectAnimatedNodeToView : (double)nodeTag viewTag : (double)viewTag) 178{ 179 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 180 // viewName is not used when node is managed by Fabric, and nodes are always managed by Fabric in Bridgeless. 181 [nodesManager connectAnimatedNodeToView:[NSNumber numberWithDouble:nodeTag] 182 viewTag:[NSNumber numberWithDouble:viewTag] 183 viewName:nil]; 184 }]; 185} 186 187ABI48_0_0RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView : (double)nodeTag viewTag : (double)viewTag) 188{ 189 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 190 [nodesManager disconnectAnimatedNodeFromView:[NSNumber numberWithDouble:nodeTag] 191 viewTag:[NSNumber numberWithDouble:viewTag]]; 192 }]; 193} 194 195ABI48_0_0RCT_EXPORT_METHOD(restoreDefaultValues : (double)nodeTag) 196{ 197 [self addPreOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 198 [nodesManager restoreDefaultValues:[NSNumber numberWithDouble:nodeTag]]; 199 }]; 200} 201 202ABI48_0_0RCT_EXPORT_METHOD(dropAnimatedNode : (double)tag) 203{ 204 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 205 [nodesManager dropAnimatedNode:[NSNumber numberWithDouble:tag]]; 206 }]; 207} 208 209ABI48_0_0RCT_EXPORT_METHOD(startListeningToAnimatedNodeValue : (double)tag) 210{ 211 __weak id<ABI48_0_0RCTValueAnimatedNodeObserver> valueObserver = self; 212 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 213 [nodesManager startListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag] valueObserver:valueObserver]; 214 }]; 215} 216 217ABI48_0_0RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue : (double)tag) 218{ 219 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 220 [nodesManager stopListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag]]; 221 }]; 222} 223 224ABI48_0_0RCT_EXPORT_METHOD(addAnimatedEventToView 225 : (double)viewTag eventName 226 : (nonnull NSString *)eventName eventMapping 227 : (ABI48_0_0JS::NativeAnimatedModule::EventMapping &)eventMapping) 228{ 229 NSMutableDictionary *eventMappingDict = [NSMutableDictionary new]; 230 eventMappingDict[@"nativeEventPath"] = ABI48_0_0RCTConvertVecToArray(eventMapping.nativeEventPath()); 231 232 if (eventMapping.animatedValueTag()) { 233 eventMappingDict[@"animatedValueTag"] = @(*eventMapping.animatedValueTag()); 234 } 235 236 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 237 [nodesManager addAnimatedEventToView:[NSNumber numberWithDouble:viewTag] 238 eventName:eventName 239 eventMapping:eventMappingDict]; 240 }]; 241} 242 243ABI48_0_0RCT_EXPORT_METHOD(removeAnimatedEventFromView 244 : (double)viewTag eventName 245 : (nonnull NSString *)eventName animatedNodeTag 246 : (double)animatedNodeTag) 247{ 248 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 249 [nodesManager removeAnimatedEventFromView:[NSNumber numberWithDouble:viewTag] 250 eventName:eventName 251 animatedNodeTag:[NSNumber numberWithDouble:animatedNodeTag]]; 252 }]; 253} 254 255ABI48_0_0RCT_EXPORT_METHOD(getValue : (double)nodeTag saveValueCallback : (ABI48_0_0RCTResponseSenderBlock)saveValueCallback) 256{ 257 [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) { 258 [nodesManager getValue:[NSNumber numberWithDouble:nodeTag] saveCallback:saveValueCallback]; 259 }]; 260} 261 262ABI48_0_0RCT_EXPORT_METHOD(queueAndExecuteBatchedOperations : (NSArray *)operationsAndArgs) 263{ 264 // TODO: implement in the future if we want the same optimization here as on Android 265} 266 267#pragma mark-- Batch handling 268 269- (void)addOperationBlock:(AnimatedOperation)operation 270{ 271 [_operations addObject:operation]; 272} 273 274- (void)addPreOperationBlock:(AnimatedOperation)operation 275{ 276 [_preOperations addObject:operation]; 277} 278 279- (void)flushOperationQueues 280{ 281 if (_preOperations.count == 0 && _operations.count == 0) { 282 return; 283 } 284 NSArray<AnimatedOperation> *preOperations = _preOperations; 285 NSArray<AnimatedOperation> *operations = _operations; 286 _preOperations = [NSMutableArray new]; 287 _operations = [NSMutableArray new]; 288 289 ABI48_0_0RCTExecuteOnMainQueue(^{ 290 for (AnimatedOperation operation in preOperations) { 291 operation(self->_nodesManager); 292 } 293 for (AnimatedOperation operation in operations) { 294 operation(self->_nodesManager); 295 } 296 [self->_nodesManager updateAnimations]; 297 }); 298} 299 300#pragma mark - ABI48_0_0RCTSurfacePresenterObserver 301 302- (void)willMountComponentsWithRootTag:(NSInteger)rootTag 303{ 304 ABI48_0_0RCTAssertMainQueue(); 305 ABI48_0_0RCTExecuteOnUIManagerQueue(^{ 306 NSArray<AnimatedOperation> *preOperations = self->_preOperations; 307 self->_preOperations = [NSMutableArray new]; 308 309 ABI48_0_0RCTExecuteOnMainQueue(^{ 310 for (AnimatedOperation preOperation in preOperations) { 311 preOperation(self->_nodesManager); 312 } 313 }); 314 }); 315} 316 317- (void)didMountComponentsWithRootTag:(NSInteger)rootTag 318{ 319 ABI48_0_0RCTAssertMainQueue(); 320 ABI48_0_0RCTExecuteOnUIManagerQueue(^{ 321 NSArray<AnimatedOperation> *operations = self->_operations; 322 self->_operations = [NSMutableArray new]; 323 324 ABI48_0_0RCTExecuteOnMainQueue(^{ 325 for (AnimatedOperation operation in operations) { 326 operation(self->_nodesManager); 327 } 328 }); 329 }); 330} 331 332#pragma mark-- Events 333 334- (NSArray<NSString *> *)supportedEvents 335{ 336 return @[ @"onAnimatedValueUpdate" ]; 337} 338 339- (void)animatedNode:(ABI48_0_0RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value 340{ 341 [self sendEventWithName:@"onAnimatedValueUpdate" body:@{@"tag" : node.nodeTag, @"value" : @(value)}]; 342} 343 344- (void)eventDispatcherWillDispatchEvent:(id<ABI48_0_0RCTEvent>)event 345{ 346 // Events can be dispatched from any queue so we have to make sure handleAnimatedEvent 347 // is run from the main queue. 348 ABI48_0_0RCTExecuteOnMainQueue(^{ 349 [self->_nodesManager handleAnimatedEvent:event]; 350 }); 351} 352 353- (std::shared_ptr<ABI48_0_0facebook::ABI48_0_0React::TurboModule>)getTurboModule: 354 (const ABI48_0_0facebook::ABI48_0_0React::ObjCTurboModule::InitParams &)params 355{ 356 return std::make_shared<ABI48_0_0facebook::ABI48_0_0React::NativeAnimatedModuleSpecJSI>(params); 357} 358 359@end 360 361Class ABI48_0_0RCTNativeAnimatedTurboModuleCls(void) 362{ 363 return ABI48_0_0RCTNativeAnimatedTurboModule.class; 364} 365