Skip to content

Commit

Permalink
Make RCTRedBox customizable
Browse files Browse the repository at this point in the history
Summary:
Adds the ability to add extra buttons and renders them along with the other buttons.

Changelog: [iOS] [Added] - RCTRedBox ability to add extra buttons

Reviewed By: PeteTheHeat

Differential Revision: D17935352

fbshipit-source-id: f8fb28653e535cd2c098566afbc639eb5c196228
  • Loading branch information
Mehdi Mulani authored and facebook-github-bot committed Oct 16, 2019
1 parent ef3f80a commit fc80a51
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 13 deletions.
4 changes: 4 additions & 0 deletions React/Modules/RCTRedBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

@class RCTJSStackFrame;

typedef void (^RCTRedBoxButtonPressHandler)(void);

@interface RCTRedBox : NSObject <RCTBridgeModule>

- (void)registerErrorCustomizer:(id<RCTErrorCustomizer>)errorCustomizer;
Expand All @@ -32,6 +34,8 @@

- (void)dismiss;

- (void)addCustomButton:(NSString *)title onPressHandler:(RCTRedBoxButtonPressHandler)handler;

/** Overrides bridge.bundleURL. Modify on main thread only. You shouldn't need to use this. */
@property (nonatomic, strong) NSURL *overrideBundleURL;

Expand Down
88 changes: 75 additions & 13 deletions React/Modules/RCTRedBox.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#import "RCTRedBoxExtraDataViewController.h"
#import "RCTUtils.h"

#import <objc/runtime.h>

#if RCT_DEV
static BOOL redBoxEnabled = YES;
#else
Expand All @@ -34,6 +36,41 @@ BOOL RCTRedBoxGetEnabled() {

@class RCTRedBoxWindow;

@interface UIButton (RCTRedBox)

@property (nonatomic) RCTRedBoxButtonPressHandler rct_handler;

- (void)rct_addBlock:(RCTRedBoxButtonPressHandler)handler forControlEvents:(UIControlEvents)controlEvents;

@end

@implementation UIButton (RCTRedBox)

- (RCTRedBoxButtonPressHandler)rct_handler
{
return objc_getAssociatedObject(self, @selector(rct_handler));
}

- (void)setRct_handler:(RCTRedBoxButtonPressHandler)rct_handler
{
objc_setAssociatedObject(self, @selector(rct_handler), rct_handler, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)rct_callBlock
{
if (self.rct_handler) {
self.rct_handler();
}
}

- (void)rct_addBlock:(RCTRedBoxButtonPressHandler)handler forControlEvents:(UIControlEvents)controlEvents
{
self.rct_handler = handler;
[self addTarget:self action:@selector(rct_callBlock) forControlEvents:controlEvents];
}

@end

@protocol RCTRedBoxWindowActionDelegate <NSObject>

- (void)redBoxWindow:(RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor:(RCTJSStackFrame *)stackFrame;
Expand All @@ -54,7 +91,7 @@ @implementation RCTRedBoxWindow
int _lastErrorCookie;
}

- (instancetype)initWithFrame:(CGRect)frame
- (instancetype)initWithFrame:(CGRect)frame customButtonTitles:(NSArray<NSString *>*)customButtonTitles customButtonHandlers:(NSArray<RCTRedBoxButtonPressHandler> *)customButtonHandlers
{
_lastErrorCookie = -1;
if ((self = [super initWithFrame:frame])) {
Expand Down Expand Up @@ -100,26 +137,32 @@ - (instancetype)initWithFrame:(CGRect)frame
NSString *extraText = @"Extra Info";
#endif

UIButton *dismissButton = [self redBoxButton:dismissText accessibilityIdentifier:@"redbox-dismiss" selector:@selector(dismiss)];
UIButton *reloadButton = [self redBoxButton:reloadText accessibilityIdentifier:@"redbox-reload" selector:@selector(reload)];
UIButton *copyButton = [self redBoxButton:copyText accessibilityIdentifier:@"redbox-copy" selector:@selector(copyStack)];
UIButton *extraButton = [self redBoxButton:extraText accessibilityIdentifier:@"redbox-extra" selector:@selector(showExtraDataViewController)];
UIButton *dismissButton = [self redBoxButton:dismissText accessibilityIdentifier:@"redbox-dismiss" selector:@selector(dismiss) block:nil];
UIButton *reloadButton = [self redBoxButton:reloadText accessibilityIdentifier:@"redbox-reload" selector:@selector(reload) block:nil];
UIButton *copyButton = [self redBoxButton:copyText accessibilityIdentifier:@"redbox-copy" selector:@selector(copyStack) block:nil];
UIButton *extraButton = [self redBoxButton:extraText accessibilityIdentifier:@"redbox-extra" selector:@selector(showExtraDataViewController) block:nil];

CGFloat buttonWidth = self.bounds.size.width / 4;
CGFloat buttonWidth = self.bounds.size.width / (4 + [customButtonTitles count]);
CGFloat bottomButtonHeight = self.bounds.size.height - buttonHeight - [self bottomSafeViewHeight];

dismissButton.frame = CGRectMake(0, bottomButtonHeight, buttonWidth, buttonHeight);
reloadButton.frame = CGRectMake(buttonWidth, bottomButtonHeight, buttonWidth, buttonHeight);
copyButton.frame = CGRectMake(buttonWidth * 2, bottomButtonHeight, buttonWidth, buttonHeight);
extraButton.frame = CGRectMake(buttonWidth * 3, bottomButtonHeight, buttonWidth, buttonHeight);

UIView *topBorder = [[UIView alloc] initWithFrame:CGRectMake(0, bottomButtonHeight + 1, rootView.frame.size.width, 1)];
topBorder.backgroundColor = [UIColor colorWithRed:0.70 green:0.70 blue:0.70 alpha:1.0];

[rootView addSubview:dismissButton];
[rootView addSubview:reloadButton];
[rootView addSubview:copyButton];
[rootView addSubview:extraButton];

for (NSUInteger i = 0; i < [customButtonTitles count]; i++) {
UIButton *button = [self redBoxButton:customButtonTitles[i] accessibilityIdentifier:@"" selector:nil block:customButtonHandlers[i]];
button.frame = CGRectMake(buttonWidth * (4 + i), bottomButtonHeight, buttonWidth, buttonHeight);
[rootView addSubview:button];
}

UIView *topBorder = [[UIView alloc] initWithFrame:CGRectMake(0, bottomButtonHeight + 1, rootView.frame.size.width, 1)];
topBorder.backgroundColor = [UIColor colorWithRed:0.70 green:0.70 blue:0.70 alpha:1.0];

[rootView addSubview:topBorder];

UIView *bottomSafeView = [UIView new];
Expand All @@ -131,7 +174,7 @@ - (instancetype)initWithFrame:(CGRect)frame
return self;
}

- (UIButton *)redBoxButton:(NSString *)title accessibilityIdentifier:(NSString *)accessibilityIdentifier selector:(SEL)selector
- (UIButton *)redBoxButton:(NSString *)title accessibilityIdentifier:(NSString *)accessibilityIdentifier selector:(SEL)selector block:(RCTRedBoxButtonPressHandler)block
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin;
Expand All @@ -143,7 +186,11 @@ - (UIButton *)redBoxButton:(NSString *)title accessibilityIdentifier:(NSString *
[button setTitle:title forState:UIControlStateNormal];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[button setTitleColor:[UIColor colorWithWhite:1 alpha:0.5] forState:UIControlStateHighlighted];
[button addTarget:self action:selector forControlEvents:UIControlEventTouchUpInside];
if (selector) {
[button addTarget:self action:selector forControlEvents:UIControlEventTouchUpInside];
} else if (block) {
[button rct_addBlock:block forControlEvents:UIControlEventTouchUpInside];
}
return button;
}

Expand Down Expand Up @@ -399,6 +446,8 @@ @implementation RCTRedBox
RCTRedBoxWindow *_window;
NSMutableArray<id<RCTErrorCustomizer>> *_errorCustomizers;
RCTRedBoxExtraDataViewController *_extraDataViewController;
NSMutableArray<NSString *> *_customButtonTitles;
NSMutableArray<RCTRedBoxButtonPressHandler> *_customButtonHandlers;
}

@synthesize bridge = _bridge;
Expand Down Expand Up @@ -524,7 +573,7 @@ - (void)showErrorMessage:(NSString *)message withParsedStack:(NSArray<RCTJSStack
#pragma clang diagnostic pop

if (!self->_window) {
self->_window = [[RCTRedBoxWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self->_window = [[RCTRedBoxWindow alloc] initWithFrame:[UIScreen mainScreen].bounds customButtonTitles:self->_customButtonTitles customButtonHandlers:self->_customButtonHandlers];
self->_window.actionDelegate = self;
}

Expand Down Expand Up @@ -599,6 +648,17 @@ - (void)reloadFromRedBoxWindow:(__unused RCTRedBoxWindow *)redBoxWindow
[self dismiss];
}

- (void)addCustomButton:(NSString *)title onPressHandler:(RCTRedBoxButtonPressHandler)handler
{
if (!_customButtonTitles) {
_customButtonTitles = [NSMutableArray new];
_customButtonHandlers = [NSMutableArray new];
}

[_customButtonTitles addObject:title];
[_customButtonHandlers addObject:handler];
}

@end

@implementation RCTBridge (RCTRedBox)
Expand Down Expand Up @@ -632,6 +692,8 @@ - (void)updateErrorMessage:(NSString *)message withParsedStack:(NSArray<RCTJSSta

- (void)dismiss {}

- (void)addCustomButton:(NSString *)title onPressHandler:(RCTRedBoxButtonPressHandler)handler {}

@end

@implementation RCTBridge (RCTRedBox)
Expand Down

0 comments on commit fc80a51

Please sign in to comment.