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_0RCTTypeSafety/ABI48_0_0RCTConvertHelpers.h>
10#import <ABI48_0_0React/ABI48_0_0RCTInitializing.h>
11#import <ABI48_0_0React/ABI48_0_0RCTNativeAnimatedNodesManager.h>
12#import <ABI48_0_0React/ABI48_0_0RCTNativeAnimatedTurboModule.h>
13
14#import "ABI48_0_0RCTAnimationPlugins.h"
15
16typedef void (^AnimatedOperation)(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager);
17
18@interface ABI48_0_0RCTNativeAnimatedTurboModule () <ABI48_0_0NativeAnimatedModuleSpec, ABI48_0_0RCTInitializing>
19@end
20
21@implementation ABI48_0_0RCTNativeAnimatedTurboModule {
22  ABI48_0_0RCTNativeAnimatedNodesManager *_nodesManager;
23  __weak id<ABI48_0_0RCTSurfacePresenterStub> _surfacePresenter;
24  // Operations called after views have been updated.
25  NSMutableArray<AnimatedOperation> *_operations;
26  // Operations called before views have been updated.
27  NSMutableArray<AnimatedOperation> *_preOperations;
28}
29
30ABI48_0_0RCT_EXPORT_MODULE();
31
32+ (BOOL)requiresMainQueueSetup
33{
34  return NO;
35}
36
37- (instancetype)init
38{
39  if (self = [super init]) {
40    _operations = [NSMutableArray new];
41    _preOperations = [NSMutableArray new];
42  }
43  return self;
44}
45
46- (void)initialize
47{
48  // _surfacePresenter set in setSurfacePresenter:
49  _nodesManager = [[ABI48_0_0RCTNativeAnimatedNodesManager alloc] initWithBridge:nil surfacePresenter:_surfacePresenter];
50  [_surfacePresenter addObserver:self];
51  [[self.moduleRegistry moduleForName:"EventDispatcher"] addDispatchObserver:self];
52}
53
54- (void)invalidate
55{
56  [super invalidate];
57  [_nodesManager stopAnimationLoop];
58  [[self.moduleRegistry moduleForName:"EventDispatcher"] removeDispatchObserver:self];
59  [_surfacePresenter removeObserver:self];
60}
61
62- (dispatch_queue_t)methodQueue
63{
64  // This module needs to be on the same queue as the UIManager to avoid
65  // having to lock `_operations` and `_preOperations` since `uiManagerWillPerformMounting`
66  // will be called from that queue.
67  return ABI48_0_0RCTGetUIManagerQueue();
68}
69
70/*
71 * In bridgeless mode, `setBridge` is never called during initializtion. Instead this selector is invoked via
72 * BridgelessTurboModuleSetup.
73 */
74- (void)setSurfacePresenter:(id<ABI48_0_0RCTSurfacePresenterStub>)surfacePresenter
75{
76  _surfacePresenter = surfacePresenter;
77}
78
79#pragma mark-- API
80
81ABI48_0_0RCT_EXPORT_METHOD(startOperationBatch)
82{
83  // TODO T71377585
84}
85
86ABI48_0_0RCT_EXPORT_METHOD(finishOperationBatch)
87{
88  // TODO T71377585
89}
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  [self flushOperationQueues];
135}
136
137ABI48_0_0RCT_EXPORT_METHOD(stopAnimation : (double)animationId)
138{
139  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
140    [nodesManager stopAnimation:[NSNumber numberWithDouble:animationId]];
141  }];
142  [self flushOperationQueues];
143}
144
145ABI48_0_0RCT_EXPORT_METHOD(setAnimatedNodeValue : (double)nodeTag value : (double)value)
146{
147  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
148    [nodesManager setAnimatedNodeValue:[NSNumber numberWithDouble:nodeTag] value:[NSNumber numberWithDouble:value]];
149  }];
150  // In Bridge, flushing of native animations is done from ABI48_0_0RCTCxxBridge batchDidComplete().
151  // Since ABI48_0_0RCTCxxBridge doesn't exist in Bridgeless, and components are not remounted in Fabric for native animations,
152  // flush here for changes in Animated.Value for Animated.event.
153  [self flushOperationQueues];
154}
155
156ABI48_0_0RCT_EXPORT_METHOD(setAnimatedNodeOffset : (double)nodeTag offset : (double)offset)
157{
158  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
159    [nodesManager setAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag] offset:[NSNumber numberWithDouble:offset]];
160  }];
161}
162
163ABI48_0_0RCT_EXPORT_METHOD(flattenAnimatedNodeOffset : (double)nodeTag)
164{
165  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
166    [nodesManager flattenAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]];
167  }];
168}
169
170ABI48_0_0RCT_EXPORT_METHOD(extractAnimatedNodeOffset : (double)nodeTag)
171{
172  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
173    [nodesManager extractAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]];
174  }];
175}
176
177ABI48_0_0RCT_EXPORT_METHOD(connectAnimatedNodeToView : (double)nodeTag viewTag : (double)viewTag)
178{
179  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
180    // viewName is not used when node is managed by Fabric, and nodes are always managed by Fabric in Bridgeless.
181    [nodesManager connectAnimatedNodeToView:[NSNumber numberWithDouble:nodeTag]
182                                    viewTag:[NSNumber numberWithDouble:viewTag]
183                                   viewName:nil];
184  }];
185}
186
187ABI48_0_0RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView : (double)nodeTag viewTag : (double)viewTag)
188{
189  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
190    [nodesManager disconnectAnimatedNodeFromView:[NSNumber numberWithDouble:nodeTag]
191                                         viewTag:[NSNumber numberWithDouble:viewTag]];
192  }];
193}
194
195ABI48_0_0RCT_EXPORT_METHOD(restoreDefaultValues : (double)nodeTag)
196{
197  [self addPreOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
198    [nodesManager restoreDefaultValues:[NSNumber numberWithDouble:nodeTag]];
199  }];
200}
201
202ABI48_0_0RCT_EXPORT_METHOD(dropAnimatedNode : (double)tag)
203{
204  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
205    [nodesManager dropAnimatedNode:[NSNumber numberWithDouble:tag]];
206  }];
207}
208
209ABI48_0_0RCT_EXPORT_METHOD(startListeningToAnimatedNodeValue : (double)tag)
210{
211  __weak id<ABI48_0_0RCTValueAnimatedNodeObserver> valueObserver = self;
212  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
213    [nodesManager startListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag] valueObserver:valueObserver];
214  }];
215}
216
217ABI48_0_0RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue : (double)tag)
218{
219  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
220    [nodesManager stopListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag]];
221  }];
222}
223
224ABI48_0_0RCT_EXPORT_METHOD(addAnimatedEventToView
225                  : (double)viewTag eventName
226                  : (nonnull NSString *)eventName eventMapping
227                  : (ABI48_0_0JS::NativeAnimatedModule::EventMapping &)eventMapping)
228{
229  NSMutableDictionary *eventMappingDict = [NSMutableDictionary new];
230  eventMappingDict[@"nativeEventPath"] = ABI48_0_0RCTConvertVecToArray(eventMapping.nativeEventPath());
231
232  if (eventMapping.animatedValueTag()) {
233    eventMappingDict[@"animatedValueTag"] = @(*eventMapping.animatedValueTag());
234  }
235
236  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
237    [nodesManager addAnimatedEventToView:[NSNumber numberWithDouble:viewTag]
238                               eventName:eventName
239                            eventMapping:eventMappingDict];
240  }];
241}
242
243ABI48_0_0RCT_EXPORT_METHOD(removeAnimatedEventFromView
244                  : (double)viewTag eventName
245                  : (nonnull NSString *)eventName animatedNodeTag
246                  : (double)animatedNodeTag)
247{
248  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
249    [nodesManager removeAnimatedEventFromView:[NSNumber numberWithDouble:viewTag]
250                                    eventName:eventName
251                              animatedNodeTag:[NSNumber numberWithDouble:animatedNodeTag]];
252  }];
253}
254
255ABI48_0_0RCT_EXPORT_METHOD(getValue : (double)nodeTag saveValueCallback : (ABI48_0_0RCTResponseSenderBlock)saveValueCallback)
256{
257  [self addOperationBlock:^(ABI48_0_0RCTNativeAnimatedNodesManager *nodesManager) {
258    [nodesManager getValue:[NSNumber numberWithDouble:nodeTag] saveCallback:saveValueCallback];
259  }];
260}
261
262ABI48_0_0RCT_EXPORT_METHOD(queueAndExecuteBatchedOperations : (NSArray *)operationsAndArgs)
263{
264  // TODO: implement in the future if we want the same optimization here as on Android
265}
266
267#pragma mark-- Batch handling
268
269- (void)addOperationBlock:(AnimatedOperation)operation
270{
271  [_operations addObject:operation];
272}
273
274- (void)addPreOperationBlock:(AnimatedOperation)operation
275{
276  [_preOperations addObject:operation];
277}
278
279- (void)flushOperationQueues
280{
281  if (_preOperations.count == 0 && _operations.count == 0) {
282    return;
283  }
284  NSArray<AnimatedOperation> *preOperations = _preOperations;
285  NSArray<AnimatedOperation> *operations = _operations;
286  _preOperations = [NSMutableArray new];
287  _operations = [NSMutableArray new];
288
289  ABI48_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 - ABI48_0_0RCTSurfacePresenterObserver
301
302- (void)willMountComponentsWithRootTag:(NSInteger)rootTag
303{
304  ABI48_0_0RCTAssertMainQueue();
305  ABI48_0_0RCTExecuteOnUIManagerQueue(^{
306    NSArray<AnimatedOperation> *preOperations = self->_preOperations;
307    self->_preOperations = [NSMutableArray new];
308
309    ABI48_0_0RCTExecuteOnMainQueue(^{
310      for (AnimatedOperation preOperation in preOperations) {
311        preOperation(self->_nodesManager);
312      }
313    });
314  });
315}
316
317- (void)didMountComponentsWithRootTag:(NSInteger)rootTag
318{
319  ABI48_0_0RCTAssertMainQueue();
320  ABI48_0_0RCTExecuteOnUIManagerQueue(^{
321    NSArray<AnimatedOperation> *operations = self->_operations;
322    self->_operations = [NSMutableArray new];
323
324    ABI48_0_0RCTExecuteOnMainQueue(^{
325      for (AnimatedOperation operation in operations) {
326        operation(self->_nodesManager);
327      }
328    });
329  });
330}
331
332#pragma mark-- Events
333
334- (NSArray<NSString *> *)supportedEvents
335{
336  return @[ @"onAnimatedValueUpdate" ];
337}
338
339- (void)animatedNode:(ABI48_0_0RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value
340{
341  [self sendEventWithName:@"onAnimatedValueUpdate" body:@{@"tag" : node.nodeTag, @"value" : @(value)}];
342}
343
344- (void)eventDispatcherWillDispatchEvent:(id<ABI48_0_0RCTEvent>)event
345{
346  // Events can be dispatched from any queue so we have to make sure handleAnimatedEvent
347  // is run from the main queue.
348  ABI48_0_0RCTExecuteOnMainQueue(^{
349    [self->_nodesManager handleAnimatedEvent:event];
350  });
351}
352
353- (std::shared_ptr<ABI48_0_0facebook::ABI48_0_0React::TurboModule>)getTurboModule:
354    (const ABI48_0_0facebook::ABI48_0_0React::ObjCTurboModule::InitParams &)params
355{
356  return std::make_shared<ABI48_0_0facebook::ABI48_0_0React::NativeAnimatedModuleSpecJSI>(params);
357}
358
359@end
360
361Class ABI48_0_0RCTNativeAnimatedTurboModuleCls(void)
362{
363  return ABI48_0_0RCTNativeAnimatedTurboModule.class;
364}
365