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 devMenuItemsnull13 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 jsInspector = DevMenuExtensions.jsInspectorAction(devDelegate.openJSInspector) 37 jsInspector.isAvailable = { bridge.batched.isInspectable } 38 jsInspector.isEnabled = { true } 39 40 let remoteDebug = DevMenuExtensions.remoteDebugAction(devDelegate.toggleRemoteDebugging) 41 remoteDebug.isAvailable = { devSettings.isRemoteDebuggingAvailable } 42 remoteDebug.isEnabled = { devSettings.isDebuggingRemotely } 43 44 let fastRefresh = DevMenuExtensions.fastRefreshAction(devDelegate.toggleFastRefresh) 45 fastRefresh.isAvailable = { devSettings.isHotLoadingAvailable } 46 fastRefresh.isEnabled = { devSettings.isHotLoadingEnabled } 47 48 let perfMonitor = DevMenuExtensions.performanceMonitorAction(devDelegate.togglePerformanceMonitor) 49 perfMonitor.isAvailable = { devDelegate.perfMonitor != nil } 50 perfMonitor.isEnabled = { devSettings.isPerfMonitorShown } 51 52 container.addItem(reload) 53 container.addItem(perfMonitor) 54 container.addItem(inspector) 55 container.addItem(jsInspector) 56 container.addItem(remoteDebug) 57 container.addItem(fastRefresh) 58 59 #endif 60 61 return container 62 } 63 64 @objc devMenuScreensnull65 open func devMenuScreens(_ settings: DevMenuExtensionSettingsProtocol) -> [DevMenuScreen]? { 66 return nil 67 } 68 69 @objc devMenuDataSourcesnull70 open func devMenuDataSources(_ settings: DevMenuExtensionSettingsProtocol) -> [DevMenuDataSourceProtocol]? { 71 return nil 72 } 73 74 // MARK: static helpers 75 76 private static func reloadAction(_ action: @escaping () -> Void) -> DevMenuAction { 77 let reload = DevMenuAction(withId: "reload", action: action) 78 reload.label = { "Reload" } 79 reload.glyphName = { "reload" } 80 reload.importance = DevMenuScreenItem.ImportanceHighest 81 reload.registerKeyCommand(input: "r", modifiers: []) // "r" without modifiers 82 return reload 83 } 84 85 private static func elementInspectorAction(_ action: @escaping () -> Void) -> DevMenuAction { 86 let inspector = DevMenuAction(withId: "inspector", action: action) 87 inspector.label = { inspector.isEnabled() ? "Hide Element Inspector" : "Show Element Inspector" } 88 inspector.glyphName = { "border-style" } 89 inspector.importance = DevMenuScreenItem.ImportanceHigh 90 inspector.registerKeyCommand(input: "i", modifiers: .command) 91 return inspector 92 } 93 94 private static func jsInspectorAction(_ action: @escaping () -> Void) -> DevMenuAction { 95 let jsInspectror = DevMenuAction(withId: "js-inspector", action: action) 96 jsInspectror.label = { "Open JS debugger" } 97 jsInspectror.glyphName = { "language-javascript" } 98 jsInspectror.importance = DevMenuScreenItem.ImportanceLow 99 return jsInspectror 100 } 101 102 private static func remoteDebugAction(_ action: @escaping () -> Void) -> DevMenuAction { 103 let remoteDebug = DevMenuAction(withId: "remote-debug", action: action) 104 remoteDebug.label = { remoteDebug.isAvailable() ? remoteDebug.isEnabled() ? "Stop Remote Debugging" : "Debug Remote JS" : "Remote Debugger Unavailable" } 105 remoteDebug.glyphName = { "remote-desktop" } 106 remoteDebug.importance = DevMenuScreenItem.ImportanceLow 107 return remoteDebug 108 } 109 110 private static func fastRefreshAction(_ action: @escaping () -> Void) -> DevMenuAction { 111 let fastRefresh = DevMenuAction(withId: "fast-refresh", action: action) 112 fastRefresh.label = { fastRefresh.isAvailable() ? fastRefresh.isEnabled() ? "Disable Fast Refresh" : "Enable Fast Refresh" : "Fast Refresh Unavailable" } 113 fastRefresh.glyphName = { "run-fast" } 114 fastRefresh.importance = DevMenuScreenItem.ImportanceLow 115 return fastRefresh 116 } 117 118 private static func performanceMonitorAction(_ action: @escaping () -> Void) -> DevMenuAction { 119 let perfMonitor = DevMenuAction(withId: "performance-monitor", action: action) 120 perfMonitor.label = { perfMonitor.isAvailable() ? perfMonitor.isEnabled() ? "Hide Performance Monitor" : "Show Performance Monitor" : "Performance Monitor Unavailable" } 121 perfMonitor.glyphName = { "speedometer" } 122 perfMonitor.importance = DevMenuScreenItem.ImportanceHigh 123 perfMonitor.registerKeyCommand(input: "p", modifiers: .command) 124 return perfMonitor 125 } 126 checkIfLogBoxIsOpenednull127 private static func checkIfLogBoxIsOpened() -> Bool { 128 return UIApplication.shared.windows.contains { 129 let className = String(describing: type(of: $0)) 130 if className == "RCTLogBoxView" || className == "RCTRedBoxView" { 131 return true 132 } 133 134 return false 135 } 136 } 137 } 138