This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
440 lines
14 KiB
440 lines
14 KiB
/** |
|
* Copyright (c) 2015-present, Facebook, Inc. |
|
* |
|
* This source code is licensed under the MIT license found in the |
|
* LICENSE file in the root directory of this source tree. |
|
*/ |
|
|
|
#import "RCTNativeAnimatedNodesManager.h" |
|
|
|
#import <React/RCTConvert.h> |
|
|
|
#import "RCTAdditionAnimatedNode.h" |
|
#import "RCTAnimatedNode.h" |
|
#import "RCTAnimationDriver.h" |
|
#import "RCTDiffClampAnimatedNode.h" |
|
#import "RCTDivisionAnimatedNode.h" |
|
#import "RCTEventAnimation.h" |
|
#import "RCTFrameAnimation.h" |
|
#import "RCTDecayAnimation.h" |
|
#import "RCTInterpolationAnimatedNode.h" |
|
#import "RCTModuloAnimatedNode.h" |
|
#import "RCTMultiplicationAnimatedNode.h" |
|
#import "RCTPropsAnimatedNode.h" |
|
#import "RCTSpringAnimation.h" |
|
#import "RCTStyleAnimatedNode.h" |
|
#import "RCTTransformAnimatedNode.h" |
|
#import "RCTValueAnimatedNode.h" |
|
#import "RCTTrackingAnimatedNode.h" |
|
|
|
@implementation RCTNativeAnimatedNodesManager |
|
{ |
|
__weak RCTUIManager *_uiManager; |
|
NSMutableDictionary<NSNumber *, RCTAnimatedNode *> *_animationNodes; |
|
// Mapping of a view tag and an event name to a list of event animation drivers. 99% of the time |
|
// there will be only one driver per mapping so all code code should be optimized around that. |
|
NSMutableDictionary<NSString *, NSMutableArray<RCTEventAnimation *> *> *_eventDrivers; |
|
NSMutableSet<id<RCTAnimationDriver>> *_activeAnimations; |
|
CADisplayLink *_displayLink; |
|
} |
|
|
|
- (instancetype)initWithUIManager:(nonnull RCTUIManager *)uiManager |
|
{ |
|
if ((self = [super init])) { |
|
_uiManager = uiManager; |
|
_animationNodes = [NSMutableDictionary new]; |
|
_eventDrivers = [NSMutableDictionary new]; |
|
_activeAnimations = [NSMutableSet new]; |
|
} |
|
return self; |
|
} |
|
|
|
#pragma mark -- Graph |
|
|
|
- (void)createAnimatedNode:(nonnull NSNumber *)tag |
|
config:(NSDictionary<NSString *, id> *)config |
|
{ |
|
static NSDictionary *map; |
|
static dispatch_once_t mapToken; |
|
dispatch_once(&mapToken, ^{ |
|
map = @{@"style" : [RCTStyleAnimatedNode class], |
|
@"value" : [RCTValueAnimatedNode class], |
|
@"props" : [RCTPropsAnimatedNode class], |
|
@"interpolation" : [RCTInterpolationAnimatedNode class], |
|
@"addition" : [RCTAdditionAnimatedNode class], |
|
@"diffclamp": [RCTDiffClampAnimatedNode class], |
|
@"division" : [RCTDivisionAnimatedNode class], |
|
@"multiplication" : [RCTMultiplicationAnimatedNode class], |
|
@"modulus" : [RCTModuloAnimatedNode class], |
|
@"transform" : [RCTTransformAnimatedNode class], |
|
@"tracking" : [RCTTrackingAnimatedNode class]}; |
|
}); |
|
|
|
NSString *nodeType = [RCTConvert NSString:config[@"type"]]; |
|
|
|
Class nodeClass = map[nodeType]; |
|
if (!nodeClass) { |
|
RCTLogError(@"Animated node type %@ not supported natively", nodeType); |
|
return; |
|
} |
|
|
|
RCTAnimatedNode *node = [[nodeClass alloc] initWithTag:tag config:config]; |
|
node.manager = self; |
|
_animationNodes[tag] = node; |
|
[node setNeedsUpdate]; |
|
} |
|
|
|
- (void)connectAnimatedNodes:(nonnull NSNumber *)parentTag |
|
childTag:(nonnull NSNumber *)childTag |
|
{ |
|
RCTAssertParam(parentTag); |
|
RCTAssertParam(childTag); |
|
|
|
RCTAnimatedNode *parentNode = _animationNodes[parentTag]; |
|
RCTAnimatedNode *childNode = _animationNodes[childTag]; |
|
|
|
RCTAssertParam(parentNode); |
|
RCTAssertParam(childNode); |
|
|
|
[parentNode addChild:childNode]; |
|
[childNode setNeedsUpdate]; |
|
} |
|
|
|
- (void)disconnectAnimatedNodes:(nonnull NSNumber *)parentTag |
|
childTag:(nonnull NSNumber *)childTag |
|
{ |
|
RCTAssertParam(parentTag); |
|
RCTAssertParam(childTag); |
|
|
|
RCTAnimatedNode *parentNode = _animationNodes[parentTag]; |
|
RCTAnimatedNode *childNode = _animationNodes[childTag]; |
|
|
|
RCTAssertParam(parentNode); |
|
RCTAssertParam(childNode); |
|
|
|
[parentNode removeChild:childNode]; |
|
[childNode setNeedsUpdate]; |
|
} |
|
|
|
- (void)connectAnimatedNodeToView:(nonnull NSNumber *)nodeTag |
|
viewTag:(nonnull NSNumber *)viewTag |
|
viewName:(nonnull NSString *)viewName |
|
{ |
|
RCTAnimatedNode *node = _animationNodes[nodeTag]; |
|
if ([node isKindOfClass:[RCTPropsAnimatedNode class]]) { |
|
[(RCTPropsAnimatedNode *)node connectToView:viewTag viewName:viewName uiManager:_uiManager]; |
|
} |
|
[node setNeedsUpdate]; |
|
} |
|
|
|
- (void)disconnectAnimatedNodeFromView:(nonnull NSNumber *)nodeTag |
|
viewTag:(nonnull NSNumber *)viewTag |
|
{ |
|
RCTAnimatedNode *node = _animationNodes[nodeTag]; |
|
if ([node isKindOfClass:[RCTPropsAnimatedNode class]]) { |
|
[(RCTPropsAnimatedNode *)node disconnectFromView:viewTag]; |
|
} |
|
} |
|
|
|
- (void)restoreDefaultValues:(nonnull NSNumber *)nodeTag |
|
{ |
|
RCTAnimatedNode *node = _animationNodes[nodeTag]; |
|
// Restoring default values needs to happen before UIManager operations so it is |
|
// possible the node hasn't been created yet if it is being connected and |
|
// disconnected in the same batch. In that case we don't need to restore |
|
// default values since it will never actually update the view. |
|
if (node == nil) { |
|
return; |
|
} |
|
if (![node isKindOfClass:[RCTPropsAnimatedNode class]]) { |
|
RCTLogError(@"Not a props node."); |
|
} |
|
[(RCTPropsAnimatedNode *)node restoreDefaultValues]; |
|
} |
|
|
|
- (void)dropAnimatedNode:(nonnull NSNumber *)tag |
|
{ |
|
RCTAnimatedNode *node = _animationNodes[tag]; |
|
if (node) { |
|
[node detachNode]; |
|
[_animationNodes removeObjectForKey:tag]; |
|
} |
|
} |
|
|
|
#pragma mark -- Mutations |
|
|
|
- (void)setAnimatedNodeValue:(nonnull NSNumber *)nodeTag |
|
value:(nonnull NSNumber *)value |
|
{ |
|
RCTAnimatedNode *node = _animationNodes[nodeTag]; |
|
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) { |
|
RCTLogError(@"Not a value node."); |
|
return; |
|
} |
|
[self stopAnimationsForNode:node]; |
|
|
|
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node; |
|
valueNode.value = value.floatValue; |
|
[valueNode setNeedsUpdate]; |
|
} |
|
|
|
- (void)setAnimatedNodeOffset:(nonnull NSNumber *)nodeTag |
|
offset:(nonnull NSNumber *)offset |
|
{ |
|
RCTAnimatedNode *node = _animationNodes[nodeTag]; |
|
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) { |
|
RCTLogError(@"Not a value node."); |
|
return; |
|
} |
|
|
|
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node; |
|
[valueNode setOffset:offset.floatValue]; |
|
[valueNode setNeedsUpdate]; |
|
} |
|
|
|
- (void)flattenAnimatedNodeOffset:(nonnull NSNumber *)nodeTag |
|
{ |
|
RCTAnimatedNode *node = _animationNodes[nodeTag]; |
|
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) { |
|
RCTLogError(@"Not a value node."); |
|
return; |
|
} |
|
|
|
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node; |
|
[valueNode flattenOffset]; |
|
} |
|
|
|
- (void)extractAnimatedNodeOffset:(nonnull NSNumber *)nodeTag |
|
{ |
|
RCTAnimatedNode *node = _animationNodes[nodeTag]; |
|
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) { |
|
RCTLogError(@"Not a value node."); |
|
return; |
|
} |
|
|
|
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node; |
|
[valueNode extractOffset]; |
|
} |
|
|
|
#pragma mark -- Drivers |
|
|
|
- (void)startAnimatingNode:(nonnull NSNumber *)animationId |
|
nodeTag:(nonnull NSNumber *)nodeTag |
|
config:(NSDictionary<NSString *, id> *)config |
|
endCallback:(RCTResponseSenderBlock)callBack |
|
{ |
|
// check if the animation has already started |
|
for (id<RCTAnimationDriver> driver in _activeAnimations) { |
|
if ([driver.animationId isEqual:animationId]) { |
|
// if the animation is running, we restart it with an updated configuration |
|
[driver resetAnimationConfig:config]; |
|
return; |
|
} |
|
} |
|
|
|
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)_animationNodes[nodeTag]; |
|
|
|
NSString *type = config[@"type"]; |
|
id<RCTAnimationDriver> animationDriver; |
|
|
|
if ([type isEqual:@"frames"]) { |
|
animationDriver = [[RCTFrameAnimation alloc] initWithId:animationId |
|
config:config |
|
forNode:valueNode |
|
callBack:callBack]; |
|
|
|
} else if ([type isEqual:@"spring"]) { |
|
animationDriver = [[RCTSpringAnimation alloc] initWithId:animationId |
|
config:config |
|
forNode:valueNode |
|
callBack:callBack]; |
|
|
|
} else if ([type isEqual:@"decay"]) { |
|
animationDriver = [[RCTDecayAnimation alloc] initWithId:animationId |
|
config:config |
|
forNode:valueNode |
|
callBack:callBack]; |
|
} else { |
|
RCTLogError(@"Unsupported animation type: %@", config[@"type"]); |
|
return; |
|
} |
|
|
|
[_activeAnimations addObject:animationDriver]; |
|
[animationDriver startAnimation]; |
|
[self startAnimationLoopIfNeeded]; |
|
} |
|
|
|
- (void)stopAnimation:(nonnull NSNumber *)animationId |
|
{ |
|
for (id<RCTAnimationDriver> driver in _activeAnimations) { |
|
if ([driver.animationId isEqual:animationId]) { |
|
[driver stopAnimation]; |
|
[_activeAnimations removeObject:driver]; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
- (void)stopAnimationsForNode:(nonnull RCTAnimatedNode *)node |
|
{ |
|
NSMutableArray<id<RCTAnimationDriver>> *discarded = [NSMutableArray new]; |
|
for (id<RCTAnimationDriver> driver in _activeAnimations) { |
|
if ([driver.valueNode isEqual:node]) { |
|
[discarded addObject:driver]; |
|
} |
|
} |
|
for (id<RCTAnimationDriver> driver in discarded) { |
|
[driver stopAnimation]; |
|
[_activeAnimations removeObject:driver]; |
|
} |
|
} |
|
|
|
#pragma mark -- Events |
|
|
|
- (void)addAnimatedEventToView:(nonnull NSNumber *)viewTag |
|
eventName:(nonnull NSString *)eventName |
|
eventMapping:(NSDictionary<NSString *, id> *)eventMapping |
|
{ |
|
NSNumber *nodeTag = [RCTConvert NSNumber:eventMapping[@"animatedValueTag"]]; |
|
RCTAnimatedNode *node = _animationNodes[nodeTag]; |
|
|
|
if (!node) { |
|
RCTLogError(@"Animated node with tag %@ does not exists", nodeTag); |
|
return; |
|
} |
|
|
|
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) { |
|
RCTLogError(@"Animated node connected to event should be of type RCTValueAnimatedNode"); |
|
return; |
|
} |
|
|
|
NSArray<NSString *> *eventPath = [RCTConvert NSStringArray:eventMapping[@"nativeEventPath"]]; |
|
|
|
RCTEventAnimation *driver = |
|
[[RCTEventAnimation alloc] initWithEventPath:eventPath valueNode:(RCTValueAnimatedNode *)node]; |
|
|
|
NSString *key = [NSString stringWithFormat:@"%@%@", viewTag, eventName]; |
|
if (_eventDrivers[key] != nil) { |
|
[_eventDrivers[key] addObject:driver]; |
|
} else { |
|
NSMutableArray<RCTEventAnimation *> *drivers = [NSMutableArray new]; |
|
[drivers addObject:driver]; |
|
_eventDrivers[key] = drivers; |
|
} |
|
} |
|
|
|
- (void)removeAnimatedEventFromView:(nonnull NSNumber *)viewTag |
|
eventName:(nonnull NSString *)eventName |
|
animatedNodeTag:(nonnull NSNumber *)animatedNodeTag |
|
{ |
|
NSString *key = [NSString stringWithFormat:@"%@%@", viewTag, eventName]; |
|
if (_eventDrivers[key] != nil) { |
|
if (_eventDrivers[key].count == 1) { |
|
[_eventDrivers removeObjectForKey:key]; |
|
} else { |
|
NSMutableArray<RCTEventAnimation *> *driversForKey = _eventDrivers[key]; |
|
for (NSUInteger i = 0; i < driversForKey.count; i++) { |
|
if (driversForKey[i].valueNode.nodeTag == animatedNodeTag) { |
|
[driversForKey removeObjectAtIndex:i]; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
- (void)handleAnimatedEvent:(id<RCTEvent>)event |
|
{ |
|
if (_eventDrivers.count == 0) { |
|
return; |
|
} |
|
|
|
NSString *key = [NSString stringWithFormat:@"%@%@", event.viewTag, event.eventName]; |
|
NSMutableArray<RCTEventAnimation *> *driversForKey = _eventDrivers[key]; |
|
if (driversForKey) { |
|
for (RCTEventAnimation *driver in driversForKey) { |
|
[self stopAnimationsForNode:driver.valueNode]; |
|
[driver updateWithEvent:event]; |
|
} |
|
|
|
[self updateAnimations]; |
|
} |
|
} |
|
|
|
#pragma mark -- Listeners |
|
|
|
- (void)startListeningToAnimatedNodeValue:(nonnull NSNumber *)tag |
|
valueObserver:(id<RCTValueAnimatedNodeObserver>)valueObserver |
|
{ |
|
RCTAnimatedNode *node = _animationNodes[tag]; |
|
if ([node isKindOfClass:[RCTValueAnimatedNode class]]) { |
|
((RCTValueAnimatedNode *)node).valueObserver = valueObserver; |
|
} |
|
} |
|
|
|
- (void)stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag |
|
{ |
|
RCTAnimatedNode *node = _animationNodes[tag]; |
|
if ([node isKindOfClass:[RCTValueAnimatedNode class]]) { |
|
((RCTValueAnimatedNode *)node).valueObserver = nil; |
|
} |
|
} |
|
|
|
|
|
#pragma mark -- Animation Loop |
|
|
|
- (void)startAnimationLoopIfNeeded |
|
{ |
|
if (!_displayLink && _activeAnimations.count > 0) { |
|
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(stepAnimations:)]; |
|
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; |
|
} |
|
} |
|
|
|
- (void)stopAnimationLoopIfNeeded |
|
{ |
|
if (_activeAnimations.count == 0) { |
|
[self stopAnimationLoop]; |
|
} |
|
} |
|
|
|
- (void)stopAnimationLoop |
|
{ |
|
if (_displayLink) { |
|
[_displayLink invalidate]; |
|
_displayLink = nil; |
|
} |
|
} |
|
|
|
- (void)stepAnimations:(CADisplayLink *)displaylink |
|
{ |
|
NSTimeInterval time = displaylink.timestamp; |
|
for (id<RCTAnimationDriver> animationDriver in _activeAnimations) { |
|
[animationDriver stepAnimationWithTime:time]; |
|
} |
|
|
|
[self updateAnimations]; |
|
|
|
for (id<RCTAnimationDriver> animationDriver in [_activeAnimations copy]) { |
|
if (animationDriver.animationHasFinished) { |
|
[animationDriver stopAnimation]; |
|
[_activeAnimations removeObject:animationDriver]; |
|
} |
|
} |
|
|
|
[self stopAnimationLoopIfNeeded]; |
|
} |
|
|
|
|
|
#pragma mark -- Updates |
|
|
|
- (void)updateAnimations |
|
{ |
|
[_animationNodes enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, RCTAnimatedNode *node, BOOL *stop) { |
|
if (node.needsUpdate) { |
|
[node updateNodeIfNecessary]; |
|
} |
|
}]; |
|
} |
|
|
|
@end
|
|
|