1 // Copyright 2015-present 650 Industries. All rights reserved.
2 
3 import ExpoModulesCore
4 import SafariServices
5 import React
6 
7 public class DevMenuInternalModule: Module {
8   public func definition() -> ModuleDefinition {
9     Name("ExpoDevMenuInternal")
10 
11     // MARK: JavaScript API
12     Constants {
13       #if targetEnvironment(simulator)
14       let doesDeviceSupportKeyCommands = true
15       #else
16       let doesDeviceSupportKeyCommands = false
17       #endif
18       return [
19         "doesDeviceSupportKeyCommands": doesDeviceSupportKeyCommands
20       ]
21     }
22 
23     AsyncFunction("fetchDataSourceAsync") { (dataSourceId: String, promise: Promise) in
24       for dataSource in DevMenuManager.shared.devMenuDataSources where dataSource.id == dataSourceId {
25         dataSource.fetchData { data in
26           promise.resolve(data.map { $0.serialize() })
27         }
28         return
29       }
30 
31       throw Exception(name: "ERR_DEVMENU_DATA_SOURCE_FAILED", description: "DataSource \(dataSourceId) not found.")
32     }
33 
34     AsyncFunction("dispatchCallableAsync") { (callableId: String, args: [String: Any]?) in
35       DevMenuManager.shared.dispatchCallable(withId: callableId, args: args)
36     }
37 
38     AsyncFunction("loadFontsAsync") {
39       DevMenuManager.shared.loadFonts()
40     }
41 
42     AsyncFunction("hideMenu") {
43       DevMenuManager.shared.hideMenu()
44     }
45 
46     AsyncFunction("closeMenu") {
47       DevMenuManager.shared.closeMenu()
48     }
49 
50     AsyncFunction("setOnboardingFinished") { (finished: Bool) in
51       DevMenuPreferences.isOnboardingFinished = finished
52     }
53 
54     AsyncFunction("openDevMenuFromReactNative") {
55       guard let rctDevMenu = DevMenuManager.shared.currentBridge?.devMenu else {
56         return
57       }
58 
59       DispatchQueue.main.async {
60         rctDevMenu.show()
61       }
62     }
63 
64     AsyncFunction("onScreenChangeAsync") { (currentScreen: String?) in
65       DevMenuManager.shared.setCurrentScreen(currentScreen)
66     }
67 
68     AsyncFunction("fireCallback") { (name: String) in
69       guard let callback = DevMenuManager.shared.registeredCallbacks.first(where: { $0.name == name }) else {
70         throw Exception(name: "ERR_DEVMENU_ACTION_FAILED", description: "\(name) is not a registered callback")
71       }
72 
73       DevMenuManager.shared.sendEventToDelegateBridge("registeredCallbackFired", data: name)
74       if callback.shouldCollapse {
75         DevMenuManager.shared.closeMenu()
76       }
77     }
78 
79     AsyncFunction("copyToClipboardAsync") { (content: String) in
80       let clipboard = UIPasteboard.general
81       clipboard.string = content as String
82     }
83   }
84 }
85