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 <ABI47_0_0FBReactNativeSpec/ABI47_0_0FBReactNativeSpec.h> 9#import <ABI47_0_0React/ABI47_0_0RCTNativeAnimatedModule.h> 10#import <ABI47_0_0React/ABI47_0_0RCTNativeAnimatedNodesManager.h> 11#import <ABI47_0_0React/ABI47_0_0RCTLog.h> 12#import <ABI47_0_0React/ABI47_0_0RCTInitializing.h> 13 14#import <ABI47_0_0RCTTypeSafety/ABI47_0_0RCTConvertHelpers.h> 15 16#import "ABI47_0_0RCTAnimationPlugins.h" 17 18typedef void (^AnimatedOperation)(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager); 19 20@interface ABI47_0_0RCTNativeAnimatedModule () <ABI47_0_0RCTInitializing> 21@end 22 23@implementation ABI47_0_0RCTNativeAnimatedModule 24{ 25 ABI47_0_0RCTNativeAnimatedNodesManager *_nodesManager; 26 27 // Operations called after views have been updated. 28 NSMutableArray<AnimatedOperation> *_operations; 29 // Operations called before views have been updated. 30 NSMutableArray<AnimatedOperation> *_preOperations; 31 NSMutableDictionary<NSNumber *, NSNumber *> *_animIdIsManagedByFabric; 32} 33 34ABI47_0_0RCT_EXPORT_MODULE(); 35 36+ (BOOL)requiresMainQueueSetup 37{ 38 return NO; 39} 40 41- (instancetype)init 42{ 43 if (self = [super init]) { 44 _operations = [NSMutableArray new]; 45 _preOperations = [NSMutableArray new]; 46 _animIdIsManagedByFabric = [NSMutableDictionary new]; 47 } 48 return self; 49} 50 51- (void)invalidate 52{ 53 [super invalidate]; 54 [_nodesManager stopAnimationLoop]; 55 [[self.moduleRegistry moduleForName:"EventDispatcher"] removeDispatchObserver:self]; 56 [self.bridge.uiManager.observerCoordinator removeObserver:self]; 57 [self.bridge.surfacePresenter removeObserver:self]; 58} 59 60- (dispatch_queue_t)methodQueue 61{ 62 // This module needs to be on the same queue as the UIManager to avoid 63 // having to lock `_operations` and `_preOperations` since `uiManagerWillPerformMounting` 64 // will be called from that queue. 65 return ABI47_0_0RCTGetUIManagerQueue(); 66} 67 68- (void)setBridge:(ABI47_0_0RCTBridge *)bridge 69{ 70 [super setBridge:bridge]; 71 _nodesManager = [[ABI47_0_0RCTNativeAnimatedNodesManager alloc] initWithBridge:self.bridge surfacePresenter:bridge.surfacePresenter]; 72 [bridge.uiManager.observerCoordinator addObserver:self]; 73 [bridge.surfacePresenter addObserver:self]; 74} 75 76- (void)initialize 77{ 78 [[self.moduleRegistry moduleForName:"EventDispatcher"] addDispatchObserver:self]; 79} 80 81/* 82 * This selector should only be invoked in bridgeless mode, which is not compatible with this non turbo module. 83 */ 84- (void)setSurfacePresenter:(id<ABI47_0_0RCTSurfacePresenterStub>)surfacePresenter 85{ 86 ABI47_0_0RCTLogWarn(@"setSurfacePresenter should only be invoked in ABI47_0_0RCTNativeAnimatedTurboModule"); 87} 88 89#pragma mark -- API 90 91ABI47_0_0RCT_EXPORT_METHOD(createAnimatedNode:(double)tag 92 config:(NSDictionary<NSString *, id> *)config) 93{ 94 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 95 [nodesManager createAnimatedNode:[NSNumber numberWithDouble:tag] config:config]; 96 }]; 97} 98 99ABI47_0_0RCT_EXPORT_METHOD(updateAnimatedNodeConfig:(double)tag 100 config:(NSDictionary<NSString *, id> *)config) 101{ 102 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 103 [nodesManager updateAnimatedNodeConfig:[NSNumber numberWithDouble:tag] config:config]; 104 }]; 105} 106 107ABI47_0_0RCT_EXPORT_METHOD(connectAnimatedNodes:(double)parentTag 108 childTag:(double)childTag) 109{ 110 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 111 [nodesManager connectAnimatedNodes:[NSNumber numberWithDouble:parentTag] childTag:[NSNumber numberWithDouble:childTag]]; 112 }]; 113} 114 115ABI47_0_0RCT_EXPORT_METHOD(disconnectAnimatedNodes:(double)parentTag 116 childTag:(double)childTag) 117{ 118 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 119 [nodesManager disconnectAnimatedNodes:[NSNumber numberWithDouble:parentTag] childTag:[NSNumber numberWithDouble:childTag]]; 120 }]; 121} 122 123ABI47_0_0RCT_EXPORT_METHOD(startAnimatingNode:(double)animationId 124 nodeTag:(double)nodeTag 125 config:(NSDictionary<NSString *, id> *)config 126 endCallback:(ABI47_0_0RCTResponseSenderBlock)callBack) 127{ 128 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 129 [nodesManager startAnimatingNode:[NSNumber numberWithDouble:animationId] nodeTag:[NSNumber numberWithDouble:nodeTag] config:config endCallback:callBack]; 130 }]; 131 132 ABI47_0_0RCTExecuteOnMainQueue(^{ 133 if (![self->_nodesManager isNodeManagedByFabric:[NSNumber numberWithDouble:nodeTag]]) { 134 return; 135 } 136 137 ABI47_0_0RCTExecuteOnUIManagerQueue(^{ 138 self->_animIdIsManagedByFabric[[NSNumber numberWithDouble:animationId]] = @YES; 139 [self flushOperationQueues]; 140 }); 141 }); 142} 143 144ABI47_0_0RCT_EXPORT_METHOD(stopAnimation:(double)animationId) 145{ 146 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 147 [nodesManager stopAnimation:[NSNumber numberWithDouble:animationId]]; 148 }]; 149 if ([_animIdIsManagedByFabric[[NSNumber numberWithDouble:animationId]] boolValue]) { 150 [self flushOperationQueues]; 151 } 152} 153 154ABI47_0_0RCT_EXPORT_METHOD(setAnimatedNodeValue:(double)nodeTag 155 value:(double)value) 156{ 157 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 158 [nodesManager setAnimatedNodeValue:[NSNumber numberWithDouble:nodeTag] value:[NSNumber numberWithDouble:value]]; 159 }]; 160} 161 162ABI47_0_0RCT_EXPORT_METHOD(setAnimatedNodeOffset:(double)nodeTag 163 offset:(double)offset) 164{ 165 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 166 [nodesManager setAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag] offset:[NSNumber numberWithDouble:offset]]; 167 }]; 168} 169 170ABI47_0_0RCT_EXPORT_METHOD(flattenAnimatedNodeOffset:(double)nodeTag) 171{ 172 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 173 [nodesManager flattenAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]]; 174 }]; 175} 176 177ABI47_0_0RCT_EXPORT_METHOD(extractAnimatedNodeOffset:(double)nodeTag) 178{ 179 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 180 [nodesManager extractAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]]; 181 }]; 182} 183 184ABI47_0_0RCT_EXPORT_METHOD(connectAnimatedNodeToView:(double)nodeTag 185 viewTag:(double)viewTag) 186{ 187 NSString *viewName = [self.bridge.uiManager viewNameForABI47_0_0ReactTag:[NSNumber numberWithDouble:viewTag]]; 188 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 189 [nodesManager connectAnimatedNodeToView:[NSNumber numberWithDouble:nodeTag] viewTag:[NSNumber numberWithDouble:viewTag] viewName:viewName]; 190 }]; 191} 192 193ABI47_0_0RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView:(double)nodeTag 194 viewTag:(double)viewTag) 195{ 196 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 197 [nodesManager disconnectAnimatedNodeFromView:[NSNumber numberWithDouble:nodeTag] viewTag:[NSNumber numberWithDouble:viewTag]]; 198 }]; 199} 200 201ABI47_0_0RCT_EXPORT_METHOD(restoreDefaultValues:(double)nodeTag) 202{ 203 [self addPreOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 204 [nodesManager restoreDefaultValues:[NSNumber numberWithDouble:nodeTag]]; 205 }]; 206} 207 208ABI47_0_0RCT_EXPORT_METHOD(dropAnimatedNode:(double)tag) 209{ 210 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 211 [nodesManager dropAnimatedNode:[NSNumber numberWithDouble:tag]]; 212 }]; 213} 214 215ABI47_0_0RCT_EXPORT_METHOD(startListeningToAnimatedNodeValue:(double)tag) 216{ 217 __weak id<ABI47_0_0RCTValueAnimatedNodeObserver> valueObserver = self; 218 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 219 [nodesManager startListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag] valueObserver:valueObserver]; 220 }]; 221} 222 223ABI47_0_0RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(double)tag) 224{ 225 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 226 [nodesManager stopListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag]]; 227 }]; 228} 229 230ABI47_0_0RCT_EXPORT_METHOD(addAnimatedEventToView:(double)viewTag 231 eventName:(nonnull NSString *)eventName 232 eventMapping:(ABI47_0_0JS::NativeAnimatedModule::EventMapping &)eventMapping) 233{ 234 NSMutableDictionary *eventMappingDict = [NSMutableDictionary new]; 235 eventMappingDict[@"nativeEventPath"] = ABI47_0_0RCTConvertVecToArray(eventMapping.nativeEventPath()); 236 237 if (eventMapping.animatedValueTag()) { 238 eventMappingDict[@"animatedValueTag"] = @(*eventMapping.animatedValueTag()); 239 } 240 241 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 242 [nodesManager addAnimatedEventToView:[NSNumber numberWithDouble:viewTag] eventName:eventName eventMapping:eventMappingDict]; 243 }]; 244} 245 246ABI47_0_0RCT_EXPORT_METHOD(removeAnimatedEventFromView:(double)viewTag 247 eventName:(nonnull NSString *)eventName 248 animatedNodeTag:(double)animatedNodeTag) 249{ 250 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 251 [nodesManager removeAnimatedEventFromView:[NSNumber numberWithDouble:viewTag] eventName:eventName animatedNodeTag:[NSNumber numberWithDouble:animatedNodeTag]]; 252 }]; 253} 254 255ABI47_0_0RCT_EXPORT_METHOD(getValue:(double)nodeTag saveValueCallback:(ABI47_0_0RCTResponseSenderBlock)saveValueCallback) { 256 [self addOperationBlock:^(ABI47_0_0RCTNativeAnimatedNodesManager *nodesManager) { 257 [nodesManager getValue:[NSNumber numberWithDouble:nodeTag] saveCallback:saveValueCallback]; 258 }]; 259} 260 261ABI47_0_0RCT_EXPORT_METHOD(queueAndExecuteBatchedOperations:(NSArray *)operationsAndArgs) { 262 // TODO: implement in the future if we want the same optimization here as on Android 263} 264 265 266#pragma mark -- Batch handling 267 268- (void)addOperationBlock:(AnimatedOperation)operation 269{ 270 [_operations addObject:operation]; 271} 272 273- (void)addPreOperationBlock:(AnimatedOperation)operation 274{ 275 [_preOperations addObject:operation]; 276} 277 278- (void)flushOperationQueues 279{ 280 if (_preOperations.count == 0 && _operations.count == 0) { 281 return; 282 } 283 NSArray<AnimatedOperation> *preOperations = _preOperations; 284 NSArray<AnimatedOperation> *operations = _operations; 285 _preOperations = [NSMutableArray new]; 286 _operations = [NSMutableArray new]; 287 288 289 ABI47_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 - ABI47_0_0RCTSurfacePresenterObserver 301 302- (void)willMountComponentsWithRootTag:(NSInteger)rootTag 303{ 304 ABI47_0_0RCTAssertMainQueue(); 305 ABI47_0_0RCTExecuteOnUIManagerQueue(^{ 306 NSArray<AnimatedOperation> *preOperations = self->_preOperations; 307 self->_preOperations = [NSMutableArray new]; 308 309 ABI47_0_0RCTExecuteOnMainQueue(^{ 310 for (AnimatedOperation preOperation in preOperations) { 311 preOperation(self->_nodesManager); 312 } 313 }); 314 }); 315} 316 317- (void)didMountComponentsWithRootTag:(NSInteger)rootTag 318{ 319 ABI47_0_0RCTAssertMainQueue(); 320 ABI47_0_0RCTExecuteOnUIManagerQueue(^{ 321 NSArray<AnimatedOperation> *operations = self->_operations; 322 self->_operations = [NSMutableArray new]; 323 324 ABI47_0_0RCTExecuteOnMainQueue(^{ 325 for (AnimatedOperation operation in operations) { 326 operation(self->_nodesManager); 327 } 328 }); 329 }); 330} 331 332#pragma mark - ABI47_0_0RCTUIManagerObserver 333 334- (void)uiManagerWillPerformMounting:(ABI47_0_0RCTUIManager *)uiManager 335{ 336 if (_preOperations.count == 0 && _operations.count == 0) { 337 return; 338 } 339 340 NSArray<AnimatedOperation> *preOperations = _preOperations; 341 NSArray<AnimatedOperation> *operations = _operations; 342 _preOperations = [NSMutableArray new]; 343 _operations = [NSMutableArray new]; 344 345 [uiManager prependUIBlock:^(__unused ABI47_0_0RCTUIManager *manager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) { 346 for (AnimatedOperation operation in preOperations) { 347 operation(self->_nodesManager); 348 } 349 }]; 350 [uiManager addUIBlock:^(__unused ABI47_0_0RCTUIManager *manager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) { 351 for (AnimatedOperation operation in operations) { 352 operation(self->_nodesManager); 353 } 354 355 [self->_nodesManager updateAnimations]; 356 }]; 357} 358 359#pragma mark -- Events 360 361- (NSArray<NSString *> *)supportedEvents 362{ 363 return @[@"onAnimatedValueUpdate"]; 364} 365 366- (void)animatedNode:(ABI47_0_0RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value 367{ 368 [self sendEventWithName:@"onAnimatedValueUpdate" 369 body:@{@"tag": node.nodeTag, @"value": @(value)}]; 370} 371 372- (void)eventDispatcherWillDispatchEvent:(id<ABI47_0_0RCTEvent>)event 373{ 374 // Events can be dispatched from any queue so we have to make sure handleAnimatedEvent 375 // is run from the main queue. 376 ABI47_0_0RCTExecuteOnMainQueue(^{ 377 [self->_nodesManager handleAnimatedEvent:event]; 378 }); 379} 380 381@end 382 383Class ABI47_0_0RCTNativeAnimatedModuleCls(void) { 384 return ABI47_0_0RCTNativeAnimatedModule.class; 385} 386