1 // Copyright 2015-present 650 Industries. All rights reserved. 2 import SafariServices 3 4 @objc(DevMenuInternalModule) 5 public class DevMenuInternalModule: NSObject, RCTBridgeModule { 6 public static func moduleName() -> String! { 7 return "ExpoDevMenuInternal" 8 } 9 10 // Module DevMenuInternalModule requires main queue setup since it overrides `constantsToExport`. 11 public static func requiresMainQueueSetup() -> Bool { 12 return true 13 } 14 15 let manager: DevMenuManager 16 17 public override init() { 18 self.manager = DevMenuManager.shared 19 } 20 21 init(manager: DevMenuManager) { 22 self.manager = manager 23 } 24 25 // MARK: JavaScript API 26 @objc 27 public func constantsToExport() -> [AnyHashable: Any] { 28 #if targetEnvironment(simulator) 29 let doesDeviceSupportKeyCommands = true 30 #else 31 let doesDeviceSupportKeyCommands = false 32 #endif 33 return [ 34 "doesDeviceSupportKeyCommands": doesDeviceSupportKeyCommands, 35 ] 36 } 37 38 @objc 39 func fetchDataSourceAsync(_ dataSourceId: String?, resolve: @escaping RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 40 guard let dataSourceId = dataSourceId else { 41 return reject("ERR_DEVMENU_DATA_SOURCE_FAILED", "DataSource ID not provided.", nil) 42 } 43 44 for dataSource in manager.devMenuDataSources { 45 if dataSource.id == dataSourceId { 46 dataSource.fetchData { data in 47 resolve(data.map { $0.serialize() }) 48 } 49 return 50 } 51 } 52 53 return reject("ERR_DEVMENU_DATA_SOURCE_FAILED", "DataSource \(dataSourceId) not founded.", nil) 54 } 55 56 @objc 57 func dispatchCallableAsync(_ callableId: String?, args: [String: Any]?, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 58 guard let callableId = callableId else { 59 return reject("ERR_DEVMENU_ACTION_FAILED", "Callable ID not provided.", nil) 60 } 61 manager.dispatchCallable(withId: callableId, args: args) 62 resolve(nil) 63 } 64 65 @objc 66 func hideMenu() { 67 manager.hideMenu() 68 } 69 70 @objc 71 func setOnboardingFinished(_ finished: Bool) { 72 DevMenuPreferences.isOnboardingFinished = finished 73 } 74 75 @objc 76 func openDevMenuFromReactNative() { 77 guard let rctDevMenu = manager.currentBridge?.devMenu else { 78 return 79 } 80 81 DispatchQueue.main.async { 82 rctDevMenu.show() 83 } 84 } 85 86 @objc 87 func onScreenChangeAsync(_ currentScreen: String?, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 88 manager.setCurrentScreen(currentScreen) 89 resolve(nil) 90 } 91 } 92