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_0React/ABI48_0_0RCTInitializing.h>
10#import <ABI48_0_0React/ABI48_0_0RCTLog.h>
11#import <ABI48_0_0React/ABI48_0_0RCTNativeAnimatedModule.h>
12#import <ABI48_0_0React/ABI48_0_0RCTNativeAnimatedNodesManager.h>
13
14#import <ABI48_0_0RCTTypeSafety/ABI48_0_0RCTConvertHelpers.h>
15
16#import "ABI48_0_0RCTAnimationPlugins.h"
17
18typedef void (^AnimatedOperation)(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager);
19
20@interface ABI48_0_0RCTNativeAnimatedModule () <ABI48_0_0RCTInitializing>
21@end
22
23@implementation ABI48_0_0RCTNativeAnimatedModule {
24  ABI48_0_0RCTNativeAnimatedNodesManager *_nodesManager;
25
26  // Operations called after views have been updated.
27  NSMutableArray<AnimatedOperation> *_operations;
28  // Operations called before views have been updated.
29  NSMutableArray<AnimatedOperation> *_preOperations;
30  NSMutableDictionary<NSNumber *, NSNumber *> *_animIdIsManagedByFabric;
31}
32
33ABI48_0_0RCT_EXPORT_MODULE();
34
35+ (BOOL)requiresMainQueueSetup
36{
37  return NO;
38}
39
40- (instancetype)init
41{
42  if (self = [super init]) {
43    _operations = [NSMutableArray new];
44    _preOperations = [NSMutableArray new];
45    _animIdIsManagedByFabric = [NSMutableDictionary new];
46  }
47  return self;
48}
49
50- (void)invalidate
51{
52  [super invalidate];
53  [_nodesManager stopAnimationLoop];
54  [[self.moduleRegistry moduleForName:"EventDispatcher"] removeDispatchObserver:self];
55  [self.bridge.uiManager.observerCoordinator removeObserver:self];
56  [self.bridge.surfacePresenter removeObserver:self];
57}
58
59- (dispatch_queue_t)methodQueue
60{
61  // This module needs to be on the same queue as the UIManager to avoid
62  // having to lock `_operations` and `_preOperations` since `uiManagerWillPerformMounting`
63  // will be called from that queue.
64  return ABI48_0_0RCTGetUIManagerQueue();
65}
66
67- (void)setBridge:(ABI48_0_0RCTBridge *)bridge
68{
69  [super setBridge:bridge];
70  _nodesManager = [[ABI48_0_0RCTNativeAnimatedNodesManager alloc] initWithBridge:self.bridge
71                                                       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<ABI48_0_0RCTSurfacePresenterStub>)surfacePresenter
85{
86  ABI48_0_0RCTLogWarn(@"setSurfacePresenter should only be invoked in ABI48_0_0RCTNativeAnimatedTurboModule");
87}
88
89#pragma mark-- API
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  ABI48_0_0RCTExecuteOnMainQueue(^{
135    if (![self->_nodesManager isNodeManagedByFabric:[NSNumber numberWithDouble:nodeTag]]) {
136      return;
137    }
138
139    ABI48_0_0RCTExecuteOnUIManagerQueue(^{
140      self->_animIdIsManagedByFabric[[NSNumber numberWithDouble:animationId]] = @YES;
141      [self flushOperationQueues];
142    });
143  });
144}
145
146ABI48_0_0RCT_EXPORT_METHOD(stopAnimation : (double)animationId)
147{
148  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
149    [nodesManager stopAnimation:[NSNumber numberWithDouble:animationId]];
150  }];
151  if ([_animIdIsManagedByFabric[[NSNumber numberWithDouble:animationId]] boolValue]) {
152    [self flushOperationQueues];
153  }
154}
155
156ABI48_0_0RCT_EXPORT_METHOD(setAnimatedNodeValue : (double)nodeTag value : (double)value)
157{
158  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
159    [nodesManager setAnimatedNodeValue:[NSNumber numberWithDouble:nodeTag] value:[NSNumber numberWithDouble:value]];
160  }];
161}
162
163ABI48_0_0RCT_EXPORT_METHOD(setAnimatedNodeOffset : (double)nodeTag offset : (double)offset)
164{
165  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
166    [nodesManager setAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag] offset:[NSNumber numberWithDouble:offset]];
167  }];
168}
169
170ABI48_0_0RCT_EXPORT_METHOD(flattenAnimatedNodeOffset : (double)nodeTag)
171{
172  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
173    [nodesManager flattenAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]];
174  }];
175}
176
177ABI48_0_0RCT_EXPORT_METHOD(extractAnimatedNodeOffset : (double)nodeTag)
178{
179  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
180    [nodesManager extractAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]];
181  }];
182}
183
184ABI48_0_0RCT_EXPORT_METHOD(connectAnimatedNodeToView : (double)nodeTag viewTag : (double)viewTag)
185{
186  NSString *viewName = [self.bridge.uiManager viewNameForABI48_0_0ReactTag:[NSNumber numberWithDouble:viewTag]];
187  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
188    [nodesManager connectAnimatedNodeToView:[NSNumber numberWithDouble:nodeTag]
189                                    viewTag:[NSNumber numberWithDouble:viewTag]
190                                   viewName:viewName];
191  }];
192}
193
194ABI48_0_0RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView : (double)nodeTag viewTag : (double)viewTag)
195{
196  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
197    [nodesManager disconnectAnimatedNodeFromView:[NSNumber numberWithDouble:nodeTag]
198                                         viewTag:[NSNumber numberWithDouble:viewTag]];
199  }];
200}
201
202ABI48_0_0RCT_EXPORT_METHOD(restoreDefaultValues : (double)nodeTag)
203{
204  [self addPreOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
205    [nodesManager restoreDefaultValues:[NSNumber numberWithDouble:nodeTag]];
206  }];
207}
208
209ABI48_0_0RCT_EXPORT_METHOD(dropAnimatedNode : (double)tag)
210{
211  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
212    [nodesManager dropAnimatedNode:[NSNumber numberWithDouble:tag]];
213  }];
214}
215
216ABI48_0_0RCT_EXPORT_METHOD(startListeningToAnimatedNodeValue : (double)tag)
217{
218  __weak id<ABI48_0_0RCTValueAnimatedNodeObserver> valueObserver = self;
219  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
220    [nodesManager startListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag] valueObserver:valueObserver];
221  }];
222}
223
224ABI48_0_0RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue : (double)tag)
225{
226  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
227    [nodesManager stopListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag]];
228  }];
229}
230
231ABI48_0_0RCT_EXPORT_METHOD(addAnimatedEventToView
232                  : (double)viewTag eventName
233                  : (nonnull NSString *)eventName eventMapping
234                  : (ABI48_0_0JS::NativeAnimatedModule::EventMapping &)eventMapping)
235{
236  NSMutableDictionary *eventMappingDict = [NSMutableDictionary new];
237  eventMappingDict[@"nativeEventPath"] = ABI48_0_0RCTConvertVecToArray(eventMapping.nativeEventPath());
238
239  if (eventMapping.animatedValueTag()) {
240    eventMappingDict[@"animatedValueTag"] = @(*eventMapping.animatedValueTag());
241  }
242
243  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
244    [nodesManager addAnimatedEventToView:[NSNumber numberWithDouble:viewTag]
245                               eventName:eventName
246                            eventMapping:eventMappingDict];
247  }];
248}
249
250ABI48_0_0RCT_EXPORT_METHOD(removeAnimatedEventFromView
251                  : (double)viewTag eventName
252                  : (nonnull NSString *)eventName animatedNodeTag
253                  : (double)animatedNodeTag)
254{
255  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
256    [nodesManager removeAnimatedEventFromView:[NSNumber numberWithDouble:viewTag]
257                                    eventName:eventName
258                              animatedNodeTag:[NSNumber numberWithDouble:animatedNodeTag]];
259  }];
260}
261
262ABI48_0_0RCT_EXPORT_METHOD(getValue : (double)nodeTag saveValueCallback : (ABI48_0_0RCTResponseSenderBlock)saveValueCallback)
263{
264  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
265    [nodesManager getValue:[NSNumber numberWithDouble:nodeTag] saveCallback:saveValueCallback];
266  }];
267}
268
269ABI48_0_0RCT_EXPORT_METHOD(queueAndExecuteBatchedOperations : (NSArray *)operationsAndArgs)
270{
271  // TODO: implement in the future if we want the same optimization here as on Android
272}
273
274#pragma mark-- Batch handling
275
276- (void)addOperationBlock:(AnimatedOperation)operation
277{
278  [_operations addObject:operation];
279}
280
281- (void)addPreOperationBlock:(AnimatedOperation)operation
282{
283  [_preOperations addObject:operation];
284}
285
286- (void)flushOperationQueues
287{
288  if (_preOperations.count == 0 && _operations.count == 0) {
289    return;
290  }
291  NSArray<AnimatedOperation> *preOperations = _preOperations;
292  NSArray<AnimatedOperation> *operations = _operations;
293  _preOperations = [NSMutableArray new];
294  _operations = [NSMutableArray new];
295
296  ABI48_0_0RCTExecuteOnMainQueue(^{
297    for (AnimatedOperation operation in preOperations) {
298      operation(self->_nodesManager);
299    }
300    for (AnimatedOperation operation in operations) {
301      operation(self->_nodesManager);
302    }
303    [self->_nodesManager updateAnimations];
304  });
305}
306
307#pragma mark - ABI48_0_0RCTSurfacePresenterObserver
308
309- (void)willMountComponentsWithRootTag:(NSInteger)rootTag
310{
311  ABI48_0_0RCTAssertMainQueue();
312  ABI48_0_0RCTExecuteOnUIManagerQueue(^{
313    NSArray<AnimatedOperation> *preOperations = self->_preOperations;
314    self->_preOperations = [NSMutableArray new];
315
316    ABI48_0_0RCTExecuteOnMainQueue(^{
317      for (AnimatedOperation preOperation in preOperations) {
318        preOperation(self->_nodesManager);
319      }
320    });
321  });
322}
323
324- (void)didMountComponentsWithRootTag:(NSInteger)rootTag
325{
326  ABI48_0_0RCTAssertMainQueue();
327  ABI48_0_0RCTExecuteOnUIManagerQueue(^{
328    NSArray<AnimatedOperation> *operations = self->_operations;
329    self->_operations = [NSMutableArray new];
330
331    ABI48_0_0RCTExecuteOnMainQueue(^{
332      for (AnimatedOperation operation in operations) {
333        operation(self->_nodesManager);
334      }
335    });
336  });
337}
338
339#pragma mark - ABI48_0_0RCTUIManagerObserver
340
341- (void)uiManagerWillPerformMounting:(ABI48_0_0RCTUIManager *)uiManager
342{
343  if (_preOperations.count == 0 && _operations.count == 0) {
344    return;
345  }
346
347  NSArray<AnimatedOperation> *preOperations = _preOperations;
348  NSArray<AnimatedOperation> *operations = _operations;
349  _preOperations = [NSMutableArray new];
350  _operations = [NSMutableArray new];
351
352  [uiManager
353      prependUIBlock:^(__unused ABI48_0_0RCTUIManager *manager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) {
354        for (AnimatedOperation operation in preOperations) {
355          operation(self->_nodesManager);
356        }
357      }];
358  [uiManager addUIBlock:^(__unused ABI48_0_0RCTUIManager *manager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) {
359    for (AnimatedOperation operation in operations) {
360      operation(self->_nodesManager);
361    }
362
363    [self->_nodesManager updateAnimations];
364  }];
365}
366
367#pragma mark-- Events
368
369- (NSArray<NSString *> *)supportedEvents
370{
371  return @[ @"onAnimatedValueUpdate" ];
372}
373
374- (void)animatedNode:(ABI48_0_0RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value
375{
376  [self sendEventWithName:@"onAnimatedValueUpdate" body:@{@"tag" : node.nodeTag, @"value" : @(value)}];
377}
378
379- (void)eventDispatcherWillDispatchEvent:(id<ABI48_0_0RCTEvent>)event
380{
381  // Events can be dispatched from any queue so we have to make sure handleAnimatedEvent
382  // is run from the main queue.
383  ABI48_0_0RCTExecuteOnMainQueue(^{
384    [self->_nodesManager handleAnimatedEvent:event];
385  });
386}
387
388@end
389
390Class ABI48_0_0RCTNativeAnimatedModuleCls(void)
391{
392  return ABI48_0_0RCTNativeAnimatedModule.class;
393}
394