Skip to content

Commit fc80a51

Browse files
Mehdi Mulanifacebook-github-bot
Mehdi Mulani
authored andcommitted
Make RCTRedBox customizable
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
1 parent ef3f80a commit fc80a51

File tree

2 files changed

+79
-13
lines changed

2 files changed

+79
-13
lines changed

React/Modules/RCTRedBox.h

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
@class RCTJSStackFrame;
1515

16+
typedef void (^RCTRedBoxButtonPressHandler)(void);
17+
1618
@interface RCTRedBox : NSObject <RCTBridgeModule>
1719

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

3335
- (void)dismiss;
3436

37+
- (void)addCustomButton:(NSString *)title onPressHandler:(RCTRedBoxButtonPressHandler)handler;
38+
3539
/** Overrides bridge.bundleURL. Modify on main thread only. You shouldn't need to use this. */
3640
@property (nonatomic, strong) NSURL *overrideBundleURL;
3741

React/Modules/RCTRedBox.m

+75-13
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#import "RCTRedBoxExtraDataViewController.h"
1717
#import "RCTUtils.h"
1818

19+
#import <objc/runtime.h>
20+
1921
#if RCT_DEV
2022
static BOOL redBoxEnabled = YES;
2123
#else
@@ -34,6 +36,41 @@ BOOL RCTRedBoxGetEnabled() {
3436

3537
@class RCTRedBoxWindow;
3638

39+
@interface UIButton (RCTRedBox)
40+
41+
@property (nonatomic) RCTRedBoxButtonPressHandler rct_handler;
42+
43+
- (void)rct_addBlock:(RCTRedBoxButtonPressHandler)handler forControlEvents:(UIControlEvents)controlEvents;
44+
45+
@end
46+
47+
@implementation UIButton (RCTRedBox)
48+
49+
- (RCTRedBoxButtonPressHandler)rct_handler
50+
{
51+
return objc_getAssociatedObject(self, @selector(rct_handler));
52+
}
53+
54+
- (void)setRct_handler:(RCTRedBoxButtonPressHandler)rct_handler
55+
{
56+
objc_setAssociatedObject(self, @selector(rct_handler), rct_handler, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
57+
}
58+
59+
- (void)rct_callBlock
60+
{
61+
if (self.rct_handler) {
62+
self.rct_handler();
63+
}
64+
}
65+
66+
- (void)rct_addBlock:(RCTRedBoxButtonPressHandler)handler forControlEvents:(UIControlEvents)controlEvents
67+
{
68+
self.rct_handler = handler;
69+
[self addTarget:self action:@selector(rct_callBlock) forControlEvents:controlEvents];
70+
}
71+
72+
@end
73+
3774
@protocol RCTRedBoxWindowActionDelegate <NSObject>
3875

3976
- (void)redBoxWindow:(RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor:(RCTJSStackFrame *)stackFrame;
@@ -54,7 +91,7 @@ @implementation RCTRedBoxWindow
5491
int _lastErrorCookie;
5592
}
5693

57-
- (instancetype)initWithFrame:(CGRect)frame
94+
- (instancetype)initWithFrame:(CGRect)frame customButtonTitles:(NSArray<NSString *>*)customButtonTitles customButtonHandlers:(NSArray<RCTRedBoxButtonPressHandler> *)customButtonHandlers
5895
{
5996
_lastErrorCookie = -1;
6097
if ((self = [super initWithFrame:frame])) {
@@ -100,26 +137,32 @@ - (instancetype)initWithFrame:(CGRect)frame
100137
NSString *extraText = @"Extra Info";
101138
#endif
102139

103-
UIButton *dismissButton = [self redBoxButton:dismissText accessibilityIdentifier:@"redbox-dismiss" selector:@selector(dismiss)];
104-
UIButton *reloadButton = [self redBoxButton:reloadText accessibilityIdentifier:@"redbox-reload" selector:@selector(reload)];
105-
UIButton *copyButton = [self redBoxButton:copyText accessibilityIdentifier:@"redbox-copy" selector:@selector(copyStack)];
106-
UIButton *extraButton = [self redBoxButton:extraText accessibilityIdentifier:@"redbox-extra" selector:@selector(showExtraDataViewController)];
140+
UIButton *dismissButton = [self redBoxButton:dismissText accessibilityIdentifier:@"redbox-dismiss" selector:@selector(dismiss) block:nil];
141+
UIButton *reloadButton = [self redBoxButton:reloadText accessibilityIdentifier:@"redbox-reload" selector:@selector(reload) block:nil];
142+
UIButton *copyButton = [self redBoxButton:copyText accessibilityIdentifier:@"redbox-copy" selector:@selector(copyStack) block:nil];
143+
UIButton *extraButton = [self redBoxButton:extraText accessibilityIdentifier:@"redbox-extra" selector:@selector(showExtraDataViewController) block:nil];
107144

108-
CGFloat buttonWidth = self.bounds.size.width / 4;
145+
CGFloat buttonWidth = self.bounds.size.width / (4 + [customButtonTitles count]);
109146
CGFloat bottomButtonHeight = self.bounds.size.height - buttonHeight - [self bottomSafeViewHeight];
110-
111147
dismissButton.frame = CGRectMake(0, bottomButtonHeight, buttonWidth, buttonHeight);
112148
reloadButton.frame = CGRectMake(buttonWidth, bottomButtonHeight, buttonWidth, buttonHeight);
113149
copyButton.frame = CGRectMake(buttonWidth * 2, bottomButtonHeight, buttonWidth, buttonHeight);
114150
extraButton.frame = CGRectMake(buttonWidth * 3, bottomButtonHeight, buttonWidth, buttonHeight);
115151

116-
UIView *topBorder = [[UIView alloc] initWithFrame:CGRectMake(0, bottomButtonHeight + 1, rootView.frame.size.width, 1)];
117-
topBorder.backgroundColor = [UIColor colorWithRed:0.70 green:0.70 blue:0.70 alpha:1.0];
118-
119152
[rootView addSubview:dismissButton];
120153
[rootView addSubview:reloadButton];
121154
[rootView addSubview:copyButton];
122155
[rootView addSubview:extraButton];
156+
157+
for (NSUInteger i = 0; i < [customButtonTitles count]; i++) {
158+
UIButton *button = [self redBoxButton:customButtonTitles[i] accessibilityIdentifier:@"" selector:nil block:customButtonHandlers[i]];
159+
button.frame = CGRectMake(buttonWidth * (4 + i), bottomButtonHeight, buttonWidth, buttonHeight);
160+
[rootView addSubview:button];
161+
}
162+
163+
UIView *topBorder = [[UIView alloc] initWithFrame:CGRectMake(0, bottomButtonHeight + 1, rootView.frame.size.width, 1)];
164+
topBorder.backgroundColor = [UIColor colorWithRed:0.70 green:0.70 blue:0.70 alpha:1.0];
165+
123166
[rootView addSubview:topBorder];
124167

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

134-
- (UIButton *)redBoxButton:(NSString *)title accessibilityIdentifier:(NSString *)accessibilityIdentifier selector:(SEL)selector
177+
- (UIButton *)redBoxButton:(NSString *)title accessibilityIdentifier:(NSString *)accessibilityIdentifier selector:(SEL)selector block:(RCTRedBoxButtonPressHandler)block
135178
{
136179
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
137180
button.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin;
@@ -143,7 +186,11 @@ - (UIButton *)redBoxButton:(NSString *)title accessibilityIdentifier:(NSString *
143186
[button setTitle:title forState:UIControlStateNormal];
144187
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
145188
[button setTitleColor:[UIColor colorWithWhite:1 alpha:0.5] forState:UIControlStateHighlighted];
146-
[button addTarget:self action:selector forControlEvents:UIControlEventTouchUpInside];
189+
if (selector) {
190+
[button addTarget:self action:selector forControlEvents:UIControlEventTouchUpInside];
191+
} else if (block) {
192+
[button rct_addBlock:block forControlEvents:UIControlEventTouchUpInside];
193+
}
147194
return button;
148195
}
149196

@@ -399,6 +446,8 @@ @implementation RCTRedBox
399446
RCTRedBoxWindow *_window;
400447
NSMutableArray<id<RCTErrorCustomizer>> *_errorCustomizers;
401448
RCTRedBoxExtraDataViewController *_extraDataViewController;
449+
NSMutableArray<NSString *> *_customButtonTitles;
450+
NSMutableArray<RCTRedBoxButtonPressHandler> *_customButtonHandlers;
402451
}
403452

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

526575
if (!self->_window) {
527-
self->_window = [[RCTRedBoxWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
576+
self->_window = [[RCTRedBoxWindow alloc] initWithFrame:[UIScreen mainScreen].bounds customButtonTitles:self->_customButtonTitles customButtonHandlers:self->_customButtonHandlers];
528577
self->_window.actionDelegate = self;
529578
}
530579

@@ -599,6 +648,17 @@ - (void)reloadFromRedBoxWindow:(__unused RCTRedBoxWindow *)redBoxWindow
599648
[self dismiss];
600649
}
601650

651+
- (void)addCustomButton:(NSString *)title onPressHandler:(RCTRedBoxButtonPressHandler)handler
652+
{
653+
if (!_customButtonTitles) {
654+
_customButtonTitles = [NSMutableArray new];
655+
_customButtonHandlers = [NSMutableArray new];
656+
}
657+
658+
[_customButtonTitles addObject:title];
659+
[_customButtonHandlers addObject:handler];
660+
}
661+
602662
@end
603663

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

633693
- (void)dismiss {}
634694

695+
- (void)addCustomButton:(NSString *)title onPressHandler:(RCTRedBoxButtonPressHandler)handler {}
696+
635697
@end
636698

637699
@implementation RCTBridge (RCTRedBox)

0 commit comments

Comments
 (0)