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