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