From 9fe71284935776e76a9d50090440f8b1adddc0bc Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Wed, 3 Jun 2015 16:40:14 -0700 Subject: [PATCH] [ReactNative] Refactor DevMenu items construction Summary: The idea behind this change it to couple together menu item title and handler. The code becomes simpler and easier to maintain, but also makes it possible to extend dev menu in the future. @public Test Plan: All menu items works as before. Changed websocket executor class name and made sure that when the class is missing we get nice error message. --- React/Base/RCTDevMenu.h | 6 ++ React/Base/RCTDevMenu.m | 174 ++++++++++++++++++++++++---------------- 2 files changed, 112 insertions(+), 68 deletions(-) diff --git a/React/Base/RCTDevMenu.h b/React/Base/RCTDevMenu.h index b260fca4a..ed1ff90b8 100644 --- a/React/Base/RCTDevMenu.h +++ b/React/Base/RCTDevMenu.h @@ -51,6 +51,12 @@ */ - (void)reload; +/** + * Add custom item to the development menu. The handler will be called + * when user selects the item. + */ +- (void)addItem:(NSString *)title handler:(dispatch_block_t)handler; + @end /** diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index 18de3a457..8a5f23f9d 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -43,6 +43,28 @@ static NSString *const RCTDevMenuSettingsKey = @"RCTDevMenu"; @end +@interface RCTDevMenuItem : NSObject + +@property (nonatomic, copy) NSString *title; +@property (nonatomic, copy) dispatch_block_t handler; + +- (instancetype)initWithTitle:(NSString *)title handler:(dispatch_block_t)handler; + +@end + +@implementation RCTDevMenuItem + +- (instancetype)initWithTitle:(NSString *)title handler:(dispatch_block_t)handler +{ + if (self = [super init]) { + self.title = title; + self.handler = handler; + } + return self; +} + +@end + @interface RCTDevMenu () @property (nonatomic, strong) Class executorClass; @@ -57,6 +79,8 @@ static NSString *const RCTDevMenuSettingsKey = @"RCTDevMenu"; NSURLSessionDataTask *_updateTask; NSURL *_liveReloadURL; BOOL _jsLoaded; + NSArray *_presentedItems; + NSMutableArray *_extraMenuItems; } @synthesize bridge = _bridge; @@ -94,6 +118,7 @@ RCT_EXPORT_MODULE() _defaults = [NSUserDefaults standardUserDefaults]; _settings = [[NSMutableDictionary alloc] init]; + _extraMenuItems = [NSMutableArray array]; // Delay setup until after Bridge init [self settingsDidChange]; @@ -232,32 +257,82 @@ RCT_EXPORT_MODULE() } } +- (void)addItem:(NSString *)title handler:(dispatch_block_t)handler +{ + [_extraMenuItems addObject:[[RCTDevMenuItem alloc] initWithTitle:title handler:handler]]; +} + +- (NSArray *)menuItems +{ + NSMutableArray *items = [NSMutableArray array]; + + [items addObject:[[RCTDevMenuItem alloc] initWithTitle:@"Reload" handler:^{ + [self reload]; + }]]; + + Class chromeExecutorClass = NSClassFromString(@"RCTWebSocketExecutor"); + if (!chromeExecutorClass) { + [items addObject:[[RCTDevMenuItem alloc] initWithTitle:@"Chrome Debugger Unavailable" handler:^{ + [[[UIAlertView alloc] initWithTitle:@"Chrome Debugger Unavailable" + message:@"You need to include the RCTWebSocket library to enable Chrome debugging" + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil] show]; + }]]; + } else { + BOOL isDebuggingInChrome = _executorClass && _executorClass == chromeExecutorClass; + NSString *debugTitleChrome = isDebuggingInChrome ? @"Disable Chrome Debugging" : @"Debug in Chrome"; + [items addObject:[[RCTDevMenuItem alloc] initWithTitle:debugTitleChrome handler:^{ + self.executorClass = isDebuggingInChrome ? Nil : chromeExecutorClass; + }]]; + } + + Class safariExecutorClass = NSClassFromString(@"RCTWebViewExecutor"); + BOOL isDebuggingInSafari = _executorClass && _executorClass == safariExecutorClass; + NSString *debugTitleSafari = isDebuggingInSafari ? @"Disable Safari Debugging" : @"Debug in Safari"; + [items addObject:[[RCTDevMenuItem alloc] initWithTitle:debugTitleSafari handler:^{ + self.executorClass = isDebuggingInSafari ? Nil : safariExecutorClass; + }]]; + + NSString *fpsMonitor = _showFPS ? @"Hide FPS Monitor" : @"Show FPS Monitor"; + [items addObject:[[RCTDevMenuItem alloc] initWithTitle:fpsMonitor handler:^{ + self.showFPS = !_showFPS; + }]]; + + [items addObject:[[RCTDevMenuItem alloc] initWithTitle:@"Inspect Element" handler:^{ + [_bridge.eventDispatcher sendDeviceEventWithName:@"toggleElementInspector" body:nil]; + }]]; + + if (_liveReloadURL) { + NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload"; + [items addObject:[[RCTDevMenuItem alloc] initWithTitle:liveReloadTitle handler:^{ + self.liveReloadEnabled = !_liveReloadEnabled; + }]]; + + NSString *profilingTitle = RCTProfileIsProfiling() ? @"Stop Profiling" : @"Start Profiling"; + [items addObject:[[RCTDevMenuItem alloc] initWithTitle:profilingTitle handler:^{ + self.profilingEnabled = !_profilingEnabled; + }]]; + } + + [items addObjectsFromArray:_extraMenuItems]; + + return items; +} + RCT_EXPORT_METHOD(show) { if (_actionSheet || !_bridge) { return; } - NSString *debugTitleChrome = _executorClass && _executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Debug in Chrome"; - NSString *debugTitleSafari = _executorClass && _executorClass == NSClassFromString(@"RCTWebViewExecutor") ? @"Disable Safari Debugging" : @"Debug in Safari"; - NSString *fpsMonitor = _showFPS ? @"Hide FPS Monitor" : @"Show FPS Monitor"; + UIActionSheet *actionSheet = [[UIActionSheet alloc] init]; + actionSheet.title = @"React Native: Development"; + actionSheet.delegate = self; - UIActionSheet *actionSheet = - [[UIActionSheet alloc] initWithTitle:@"React Native: Development" - delegate:self - cancelButtonTitle:nil - destructiveButtonTitle:nil - otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, fpsMonitor, nil]; - - [actionSheet addButtonWithTitle:@"Inspect Element"]; - - if (_liveReloadURL) { - - NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload"; - NSString *profilingTitle = RCTProfileIsProfiling() ? @"Stop Profiling" : @"Start Profiling"; - - [actionSheet addButtonWithTitle:liveReloadTitle]; - [actionSheet addButtonWithTitle:profilingTitle]; + NSArray *items = [self menuItems]; + for (RCTDevMenuItem *item in items) { + [actionSheet addButtonWithTitle:item.title]; } [actionSheet addButtonWithTitle:@"Cancel"]; @@ -266,13 +341,7 @@ RCT_EXPORT_METHOD(show) actionSheet.actionSheetStyle = UIBarStyleBlack; [actionSheet showInView:[UIApplication sharedApplication].keyWindow.rootViewController.view]; _actionSheet = actionSheet; -} - -RCT_EXPORT_METHOD(reload) -{ - _jsLoaded = NO; - _liveReloadURL = nil; - [_bridge reload]; + _presentedItems = items; } - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex @@ -282,48 +351,16 @@ RCT_EXPORT_METHOD(reload) return; } - switch (buttonIndex) { - case 0: { - [self reload]; - break; - } - case 1: { - Class cls = NSClassFromString(@"RCTWebSocketExecutor"); - if (!cls) { - [[[UIAlertView alloc] initWithTitle:@"Chrome Debugger Unavailable" - message:@"You need to include the RCTWebSocket library to enable Chrome debugging" - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil] show]; - return; - } - self.executorClass = (_executorClass == cls) ? Nil : cls; - break; - } - case 2: { - Class cls = NSClassFromString(@"RCTWebViewExecutor"); - self.executorClass = (_executorClass == cls) ? Nil : cls; - break; - } - case 3: { - self.showFPS = !_showFPS; - break; - } - case 4: { - [_bridge.eventDispatcher sendDeviceEventWithName:@"toggleElementInspector" body:nil]; - break; - } - case 5: { - self.liveReloadEnabled = !_liveReloadEnabled; - break; - } - case 6: { - self.profilingEnabled = !_profilingEnabled; - break; - } - default: - break; - } + RCTDevMenuItem *item = _presentedItems[buttonIndex]; + item.handler(); + return; +} + +RCT_EXPORT_METHOD(reload) +{ + _jsLoaded = NO; + _liveReloadURL = nil; + [_bridge reload]; } - (void)setShakeToShow:(BOOL)shakeToShow @@ -445,6 +482,7 @@ RCT_EXPORT_METHOD(reload) - (void)show {} - (void)reload {} +- (void)addItem:(NSString *)title handler:(dispatch_block_t)handler {} @end