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