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 {
definitionnull8   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       DevMenuManager.shared.closeMenu {
60         DispatchQueue.main.async {
61           rctDevMenu.show()
62         }
63       }
64     }
65 
66     AsyncFunction("onScreenChangeAsync") { (currentScreen: String?) in
67       DevMenuManager.shared.setCurrentScreen(currentScreen)
68     }
69 
70     AsyncFunction("fireCallback") { (name: String) in
71       guard let callback = DevMenuManager.shared.registeredCallbacks.first(where: { $0.name == name }) else {
72         throw Exception(name: "ERR_DEVMENU_ACTION_FAILED", description: "\(name) is not a registered callback")
73       }
74 
75       DevMenuManager.shared.sendEventToDelegateBridge("registeredCallbackFired", data: name)
76       if callback.shouldCollapse {
77         DevMenuManager.shared.closeMenu()
78       }
79     }
80 
81     AsyncFunction("copyToClipboardAsync") { (content: String) in
82       let clipboard = UIPasteboard.general
83       clipboard.string = content as String
84     }
85   }
86 }
87