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