1 // Copyright 2015-present 650 Industries. All rights reserved. 2 3 import EXDevMenuInterface 4 5 @objc(DevMenuExtensions) 6 open class DevMenuExtensions: NSObject, DevMenuExtensionProtocol { 7 @objc 8 open var bridge: RCTBridge? 9 10 // MARK: DevMenuExtensionProtocol 11 12 @objc 13 open func devMenuItems(_ settings: DevMenuExtensionSettingsProtocol) -> DevMenuItemsContainerProtocol? { 14 if !settings.wasRunOnDevelopmentBridge() { 15 return nil 16 } 17 18 guard let bridge = bridge else { 19 return nil 20 } 21 22 let devDelegate = DevMenuDevOptionsDelegate(forBridge: bridge) 23 guard let devSettings = devDelegate.devSettings else { 24 return nil 25 } 26 27 let container = DevMenuItemsContainer() 28 29 let reload = DevMenuExtensions.reloadAction(devDelegate.reload) 30 reload.isAvailable = { !DevMenuExtensions.checkIfLogBoxIsOpened() } 31 32 let inspector = DevMenuExtensions.elementInspectorAction(devDelegate.toggleElementInsector) 33 inspector.isEnabled = { devSettings.isElementInspectorShown } 34 35 #if DEBUG 36 let remoteDebug = DevMenuExtensions.remoteDebugAction(devDelegate.toggleRemoteDebugging) 37 remoteDebug.isAvailable = { devSettings.isRemoteDebuggingAvailable } 38 remoteDebug.isEnabled = { devSettings.isDebuggingRemotely } 39 40 let fastRefresh = DevMenuExtensions.fastRefreshAction(devDelegate.toggleFastRefresh) 41 fastRefresh.isAvailable = { devSettings.isHotLoadingAvailable } 42 fastRefresh.isEnabled = { devSettings.isHotLoadingEnabled } 43 44 let perfMonitor = DevMenuExtensions.performanceMonitorAction(devDelegate.togglePerformanceMonitor) 45 perfMonitor.isAvailable = { devDelegate.perfMonitor != nil } 46 perfMonitor.isEnabled = { devSettings.isPerfMonitorShown } 47 48 container.addItem(reload) 49 container.addItem(perfMonitor) 50 container.addItem(inspector) 51 container.addItem(remoteDebug) 52 container.addItem(fastRefresh) 53 54 #endif 55 56 return container 57 } 58 59 @objc 60 open func devMenuScreens(_ settings: DevMenuExtensionSettingsProtocol) -> [DevMenuScreen]? { 61 return nil 62 } 63 64 @objc 65 open func devMenuDataSources(_ settings: DevMenuExtensionSettingsProtocol) -> [DevMenuDataSourceProtocol]? { 66 return nil 67 } 68 69 // MARK: static helpers 70 71 private static func reloadAction(_ action: @escaping () -> Void) -> DevMenuAction { 72 let reload = DevMenuAction(withId: "reload", action: action) 73 reload.label = { "Reload" } 74 reload.glyphName = { "reload" } 75 reload.importance = DevMenuScreenItem.ImportanceHighest 76 reload.registerKeyCommand(input: "r", modifiers: []) // "r" without modifiers 77 return reload 78 } 79 80 private static func elementInspectorAction(_ action: @escaping () -> Void) -> DevMenuAction { 81 let inspector = DevMenuAction(withId: "inspector", action: action) 82 inspector.label = { inspector.isEnabled() ? "Hide Element Inspector" : "Show Element Inspector" } 83 inspector.glyphName = { "border-style" } 84 inspector.importance = DevMenuScreenItem.ImportanceHigh 85 inspector.registerKeyCommand(input: "i", modifiers: .command) 86 return inspector 87 } 88 89 private static func remoteDebugAction(_ action: @escaping () -> Void) -> DevMenuAction { 90 let remoteDebug = DevMenuAction(withId: "remote-debug", action: action) 91 remoteDebug.label = { remoteDebug.isAvailable() ? remoteDebug.isEnabled() ? "Stop Remote Debugging" : "Debug Remote JS" : "Remote Debugger Unavailable" } 92 remoteDebug.glyphName = { "remote-desktop" } 93 remoteDebug.importance = DevMenuScreenItem.ImportanceLow 94 return remoteDebug 95 } 96 97 private static func fastRefreshAction(_ action: @escaping () -> Void) -> DevMenuAction { 98 let fastRefresh = DevMenuAction(withId: "fast-refresh", action: action) 99 fastRefresh.label = { fastRefresh.isAvailable() ? fastRefresh.isEnabled() ? "Disable Fast Refresh" : "Enable Fast Refresh" : "Fast Refresh Unavailable" } 100 fastRefresh.glyphName = { "run-fast" } 101 fastRefresh.importance = DevMenuScreenItem.ImportanceLow 102 return fastRefresh 103 } 104 105 private static func performanceMonitorAction(_ action: @escaping () -> Void) -> DevMenuAction { 106 let perfMonitor = DevMenuAction(withId: "performance-monitor", action: action) 107 perfMonitor.label = { perfMonitor.isAvailable() ? perfMonitor.isEnabled() ? "Hide Performance Monitor" : "Show Performance Monitor" : "Performance Monitor Unavailable" } 108 perfMonitor.glyphName = { "speedometer" } 109 perfMonitor.importance = DevMenuScreenItem.ImportanceHigh 110 perfMonitor.registerKeyCommand(input: "p", modifiers: .command) 111 return perfMonitor 112 } 113 114 private static func checkIfLogBoxIsOpened() -> Bool { 115 return UIApplication.shared.windows.contains { 116 let className = String(describing: type(of: $0)) 117 if className == "RCTLogBoxView" || className == "RCTRedBoxView" { 118 return true 119 } 120 121 return false 122 } 123 } 124 } 125