Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent creation of button react view with the same componentId #5687

Merged
merged 9 commits into from
Nov 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions lib/ios/NSArray+utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>

@interface NSArray (utils)

- (NSArray *)intersect:(NSArray *)array withPropertyName:(NSString *)propertyName;

- (NSArray *)difference:(NSArray *)array withPropertyName:(NSString *)propertyName;

- (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block;

@end
31 changes: 31 additions & 0 deletions lib/ios/NSArray+utils.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#import "NSArray+utils.h"

@implementation NSArray (utils)

- (NSArray *)intersect:(NSArray *)array withPropertyName:(NSString *)propertyName {
NSMutableArray* intersection = [NSMutableArray new];
for (NSObject* object in array) {
NSArray* filteredArray = [self filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%@ == %@", propertyName, object]];
[intersection addObjectsFromArray:filteredArray];
}

return [NSArray arrayWithArray:intersection];
}

- (NSArray *)difference:(NSArray *)array withPropertyName:(NSString *)propertyName {
NSMutableArray* diff = [NSMutableArray arrayWithArray:self];
NSArray* intersection = [self intersect:array withPropertyName:propertyName];
[diff removeObjectsInArray:intersection];

return [NSArray arrayWithArray:diff];
}

- (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block {
NSMutableArray *result = [NSMutableArray arrayWithCapacity:[self count]];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[result addObject:block(obj, idx)];
}];
return result;
}

@end
288 changes: 151 additions & 137 deletions lib/ios/RNNNavigationButtons.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#import "UIImage+insets.h"
#import "UIViewController+LayoutProtocol.h"
#import "RNNFontAttributesCreator.h"
#import "NSArray+utils.h"

@interface RNNNavigationButtons()

Expand All @@ -19,167 +20,180 @@ @interface RNNNavigationButtons()
@implementation RNNNavigationButtons

-(instancetype)initWithViewController:(UIViewController<RNNLayoutProtocol>*)viewController componentRegistry:(id)componentRegistry {
self = [super init];
self.viewController = viewController;
self.componentRegistry = componentRegistry;
return self;
self = [super init];
self.viewController = viewController;
self.componentRegistry = componentRegistry;
return self;
}

- (void)applyLeftButtons:(NSArray *)leftButtons rightButtons:(NSArray *)rightButtons defaultLeftButtonStyle:(RNNButtonOptions *)defaultLeftButtonStyle defaultRightButtonStyle:(RNNButtonOptions *)defaultRightButtonStyle {
_defaultLeftButtonStyle = defaultLeftButtonStyle;
_defaultRightButtonStyle = defaultRightButtonStyle;
if (leftButtons) {
[self setButtons:leftButtons side:@"left" animated:NO defaultStyle:_defaultLeftButtonStyle insets:[self leftButtonInsets:_defaultLeftButtonStyle.iconInsets]];
}
if (rightButtons) {
[self setButtons:rightButtons side:@"right" animated:NO defaultStyle:_defaultRightButtonStyle insets:[self rightButtonInsets:_defaultRightButtonStyle.iconInsets]];
}
_defaultLeftButtonStyle = defaultLeftButtonStyle;
_defaultRightButtonStyle = defaultRightButtonStyle;
if (leftButtons) {
[self setButtons:leftButtons side:@"left" animated:NO defaultStyle:_defaultLeftButtonStyle insets:[self leftButtonInsets:_defaultLeftButtonStyle.iconInsets]];
}
if (rightButtons) {
[self setButtons:rightButtons side:@"right" animated:NO defaultStyle:_defaultRightButtonStyle insets:[self rightButtonInsets:_defaultRightButtonStyle.iconInsets]];
}
}

-(void)setButtons:(NSArray*)buttons side:(NSString*)side animated:(BOOL)animated defaultStyle:(RNNButtonOptions *)defaultStyle insets:(UIEdgeInsets)insets {
NSMutableArray *barButtonItems = [NSMutableArray new];
NSArray* resolvedButtons = [self resolveButtons:buttons];
for (NSDictionary *button in resolvedButtons) {
RNNUIBarButtonItem* barButtonItem = [self buildButton:button defaultStyle:defaultStyle insets:insets];
if(barButtonItem) {
[barButtonItems addObject:barButtonItem];
}
UIColor* color = [self color:[RCTConvert UIColor:button[@"color"]] defaultColor:[defaultStyle.color getWithDefaultValue:nil]];
if (color) {
self.viewController.navigationController.navigationBar.tintColor = color;
}
}

if ([side isEqualToString:@"left"]) {
[self.viewController.navigationItem setLeftBarButtonItems:barButtonItems animated:animated];
}

if ([side isEqualToString:@"right"]) {
[self.viewController.navigationItem setRightBarButtonItems:barButtonItems animated:animated];
}
NSMutableArray *barButtonItems = [NSMutableArray new];
NSArray* resolvedButtons = [self resolveButtons:buttons];
for (NSDictionary *button in resolvedButtons) {
RNNUIBarButtonItem* barButtonItem = [self buildButton:button defaultStyle:defaultStyle insets:insets];
if(barButtonItem) {
[barButtonItems addObject:barButtonItem];
}
UIColor* color = [self color:[RCTConvert UIColor:button[@"color"]] defaultColor:[defaultStyle.color getWithDefaultValue:nil]];
if (color) {
self.viewController.navigationController.navigationBar.tintColor = color;
}
}

if ([side isEqualToString:@"left"]) {
[self clearPreviousButtonViews:barButtonItems oldButtons:self.viewController.navigationItem.leftBarButtonItems];
[self.viewController.navigationItem setLeftBarButtonItems:barButtonItems animated:animated];
}

if ([side isEqualToString:@"right"]) {
[self clearPreviousButtonViews:barButtonItems oldButtons:self.viewController.navigationItem.rightBarButtonItems];
[self.viewController.navigationItem setRightBarButtonItems:barButtonItems animated:animated];
}
}

- (void)clearPreviousButtonViews:(NSArray<UIBarButtonItem *> *)newButtons oldButtons:(NSArray<UIBarButtonItem *> *)oldButtons {
NSArray<UIBarButtonItem *>* removedButtons = [oldButtons difference:newButtons withPropertyName:@"customView"];

for (UIBarButtonItem* buttonItem in removedButtons) {
RNNReactView* reactView = buttonItem.customView;
if ([reactView isKindOfClass:[RNNReactView class]]) {
[_componentRegistry removeChildComponent:reactView.componentId];
}
}
}

- (NSArray *)resolveButtons:(id)buttons {
if ([buttons isKindOfClass:[NSArray class]]) {
return buttons;
} else {
return @[buttons];
}
if ([buttons isKindOfClass:[NSArray class]]) {
return buttons;
} else {
return @[buttons];
}
}

-(RNNUIBarButtonItem*)buildButton: (NSDictionary*)dictionary defaultStyle:(RNNButtonOptions *)defaultStyle insets:(UIEdgeInsets)insets {
NSString* buttonId = dictionary[@"id"];
NSString* title = [self getValue:dictionary[@"text"] withDefault:[defaultStyle.text getWithDefaultValue:nil]];
NSDictionary* component = dictionary[@"component"];
NSString* systemItemName = dictionary[@"systemItem"];
UIColor* color = [self color:[RCTConvert UIColor:dictionary[@"color"]] defaultColor:[defaultStyle.color getWithDefaultValue:nil]];
UIColor* disabledColor = [self color:[RCTConvert UIColor:dictionary[@"disabledColor"]] defaultColor:[defaultStyle.disabledColor getWithDefaultValue:nil]];
if (!buttonId) {
@throw [NSException exceptionWithName:@"NSInvalidArgumentException" reason:[@"button id is not specified " stringByAppendingString:title] userInfo:nil];
}
UIImage* defaultIcon = [defaultStyle.icon getWithDefaultValue:nil];
UIImage* iconImage = [self getValue:dictionary[@"icon"] withDefault:defaultIcon];
if (![iconImage isKindOfClass:[UIImage class]]) {
iconImage = [RCTConvert UIImage:iconImage];
}
if (iconImage) {
iconImage = [iconImage imageWithInsets:insets];
if (color) {
iconImage = [iconImage withTintColor:color];
}
}
RNNUIBarButtonItem *barButtonItem;
if (component) {
RNNComponentOptions* componentOptions = [RNNComponentOptions new];
componentOptions.componentId = [[Text alloc] initWithValue:component[@"componentId"]];
componentOptions.name = [[Text alloc] initWithValue:component[@"name"]];
RNNReactView *view = [_componentRegistry createComponentIfNotExists:componentOptions parentComponentId:self.viewController.layoutInfo.componentId reactViewReadyBlock:nil];
barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withCustomView:view componentRegistry:_componentRegistry];
} else if (iconImage) {
barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withIcon:iconImage];
} else if (title) {
barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withTitle:title];
NSMutableDictionary *buttonTextAttributes = [RCTHelpers textAttributesFromDictionary:dictionary withPrefix:@"button"];
if (buttonTextAttributes.allKeys.count > 0) {
[barButtonItem setTitleTextAttributes:buttonTextAttributes forState:UIControlStateNormal];
}
} else if (systemItemName) {
barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withSystemItem:systemItemName];
} else {
return nil;
}
barButtonItem.target = self.viewController;
barButtonItem.action = @selector(onButtonPress:);
NSNumber *enabled = [self getValue:dictionary[@"enabled"] withDefault:defaultStyle.enabled.getValue];
BOOL enabledBool = enabled ? [enabled boolValue] : YES;
[barButtonItem setEnabled:enabledBool];
NSMutableDictionary* textAttributes = [NSMutableDictionary dictionaryWithDictionary:[RNNFontAttributesCreator createWithFontFamily:dictionary[@"fontFamily"] ?: [defaultStyle.fontFamily getWithDefaultValue:nil] fontSize:dictionary[@"fontSize"] defaultFontSize:[defaultStyle.fontSize getWithDefaultValue:@(17)] fontWeight:dictionary[@"fontWeight"] color:nil defaultColor:nil]];
NSMutableDictionary* disabledTextAttributes = [NSMutableDictionary dictionaryWithDictionary:[RNNFontAttributesCreator createWithFontFamily:dictionary[@"fontFamily"] ?: [defaultStyle.fontFamily getWithDefaultValue:nil] fontSize:dictionary[@"fontSize"] defaultFontSize:[defaultStyle.fontSize getWithDefaultValue:@(17)] fontWeight:dictionary[@"fontWeight"] color:nil defaultColor:nil]];
if (!enabledBool && disabledColor) {
color = disabledColor;
[disabledTextAttributes setObject:disabledColor forKey:NSForegroundColorAttributeName];
}
if (color) {
[textAttributes setObject:color forKey:NSForegroundColorAttributeName];
[barButtonItem setImage:[[iconImage withTintColor:color] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
barButtonItem.tintColor = color;
}

[barButtonItem setTitleTextAttributes:textAttributes forState:UIControlStateNormal];
[barButtonItem setTitleTextAttributes:textAttributes forState:UIControlStateHighlighted];
[barButtonItem setTitleTextAttributes:disabledTextAttributes forState:UIControlStateDisabled];
NSString *testID = dictionary[@"testID"];
if (testID)
{
barButtonItem.accessibilityIdentifier = testID;
}
return barButtonItem;
NSString* buttonId = dictionary[@"id"];
NSString* title = [self getValue:dictionary[@"text"] withDefault:[defaultStyle.text getWithDefaultValue:nil]];
NSDictionary* component = dictionary[@"component"];
NSString* systemItemName = dictionary[@"systemItem"];
UIColor* color = [self color:[RCTConvert UIColor:dictionary[@"color"]] defaultColor:[defaultStyle.color getWithDefaultValue:nil]];
UIColor* disabledColor = [self color:[RCTConvert UIColor:dictionary[@"disabledColor"]] defaultColor:[defaultStyle.disabledColor getWithDefaultValue:nil]];
if (!buttonId) {
@throw [NSException exceptionWithName:@"NSInvalidArgumentException" reason:[@"button id is not specified " stringByAppendingString:title] userInfo:nil];
}
UIImage* defaultIcon = [defaultStyle.icon getWithDefaultValue:nil];
UIImage* iconImage = [self getValue:dictionary[@"icon"] withDefault:defaultIcon];
if (![iconImage isKindOfClass:[UIImage class]]) {
iconImage = [RCTConvert UIImage:iconImage];
}
if (iconImage) {
iconImage = [iconImage imageWithInsets:insets];
if (color) {
iconImage = [iconImage withTintColor:color];
}
}
RNNUIBarButtonItem *barButtonItem;
if (component) {
RNNComponentOptions* componentOptions = [RNNComponentOptions new];
componentOptions.componentId = [[Text alloc] initWithValue:component[@"componentId"]];
componentOptions.name = [[Text alloc] initWithValue:component[@"name"]];
RNNReactView *view = [_componentRegistry createComponentIfNotExists:componentOptions parentComponentId:self.viewController.layoutInfo.componentId reactViewReadyBlock:nil];
barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withCustomView:view];
} else if (iconImage) {
barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withIcon:iconImage];
} else if (title) {
barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withTitle:title];
NSMutableDictionary *buttonTextAttributes = [RCTHelpers textAttributesFromDictionary:dictionary withPrefix:@"button"];
if (buttonTextAttributes.allKeys.count > 0) {
[barButtonItem setTitleTextAttributes:buttonTextAttributes forState:UIControlStateNormal];
}
} else if (systemItemName) {
barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withSystemItem:systemItemName];
} else {
return nil;
}
barButtonItem.target = self.viewController;
barButtonItem.action = @selector(onButtonPress:);
NSNumber *enabled = [self getValue:dictionary[@"enabled"] withDefault:defaultStyle.enabled.getValue];
BOOL enabledBool = enabled ? [enabled boolValue] : YES;
[barButtonItem setEnabled:enabledBool];
NSMutableDictionary* textAttributes = [NSMutableDictionary dictionaryWithDictionary:[RNNFontAttributesCreator createWithFontFamily:dictionary[@"fontFamily"] ?: [defaultStyle.fontFamily getWithDefaultValue:nil] fontSize:dictionary[@"fontSize"] defaultFontSize:[defaultStyle.fontSize getWithDefaultValue:@(17)] fontWeight:dictionary[@"fontWeight"] color:nil defaultColor:nil]];
NSMutableDictionary* disabledTextAttributes = [NSMutableDictionary dictionaryWithDictionary:[RNNFontAttributesCreator createWithFontFamily:dictionary[@"fontFamily"] ?: [defaultStyle.fontFamily getWithDefaultValue:nil] fontSize:dictionary[@"fontSize"] defaultFontSize:[defaultStyle.fontSize getWithDefaultValue:@(17)] fontWeight:dictionary[@"fontWeight"] color:nil defaultColor:nil]];
if (!enabledBool && disabledColor) {
color = disabledColor;
[disabledTextAttributes setObject:disabledColor forKey:NSForegroundColorAttributeName];
}
if (color) {
[textAttributes setObject:color forKey:NSForegroundColorAttributeName];
[barButtonItem setImage:[[iconImage withTintColor:color] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
barButtonItem.tintColor = color;
}
[barButtonItem setTitleTextAttributes:textAttributes forState:UIControlStateNormal];
[barButtonItem setTitleTextAttributes:textAttributes forState:UIControlStateHighlighted];
[barButtonItem setTitleTextAttributes:disabledTextAttributes forState:UIControlStateDisabled];
NSString *testID = dictionary[@"testID"];
if (testID)
{
barButtonItem.accessibilityIdentifier = testID;
}
return barButtonItem;
}

- (UIColor *)color:(UIColor *)color defaultColor:(UIColor *)defaultColor {
if (color) {
return color;
} else if (defaultColor) {
return defaultColor;
}
return nil;
if (color) {
return color;
} else if (defaultColor) {
return defaultColor;
}
return nil;
}

- (id)getValue:(id)value withDefault:(id)defaultValue {
return value ? value : defaultValue;
return value ? value : defaultValue;
}

- (UIEdgeInsets)leftButtonInsets:(RNNInsetsOptions *)defaultInsets {
return UIEdgeInsetsMake([defaultInsets.top getWithDefaultValue:0],
[defaultInsets.left getWithDefaultValue:0],
[defaultInsets.bottom getWithDefaultValue:0],
[defaultInsets.right getWithDefaultValue:15]);
return UIEdgeInsetsMake([defaultInsets.top getWithDefaultValue:0],
[defaultInsets.left getWithDefaultValue:0],
[defaultInsets.bottom getWithDefaultValue:0],
[defaultInsets.right getWithDefaultValue:15]);
}

- (UIEdgeInsets)rightButtonInsets:(RNNInsetsOptions *)defaultInsets {
return UIEdgeInsetsMake([defaultInsets.top getWithDefaultValue:0],
[defaultInsets.left getWithDefaultValue:15],
[defaultInsets.bottom getWithDefaultValue:0],
[defaultInsets.right getWithDefaultValue:0]);
return UIEdgeInsetsMake([defaultInsets.top getWithDefaultValue:0],
[defaultInsets.left getWithDefaultValue:15],
[defaultInsets.bottom getWithDefaultValue:0],
[defaultInsets.right getWithDefaultValue:0]);
}

@end
Loading