182 lines
5.0 KiB
Swift
182 lines
5.0 KiB
Swift
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
|
|
import Foundation
|
|
import UIKit
|
|
import Combine
|
|
|
|
@MainActor
|
|
class DevMenuViewModel: ObservableObject {
|
|
@Published var appInfo: AppInfo?
|
|
@Published var devSettings: DevSettings?
|
|
@Published var registeredCallbacks: [String] = []
|
|
@Published var clipboardMessage: String?
|
|
@Published var hostUrlCopiedMessage: String?
|
|
@Published var isOnboardingFinished: Bool = true
|
|
|
|
private let devMenuManager = DevMenuManager.shared
|
|
private var cancellables = Set<AnyCancellable>()
|
|
|
|
init() {
|
|
loadData()
|
|
checkOnboardingStatus()
|
|
observeRegisteredCallbacks()
|
|
}
|
|
|
|
private func loadData() {
|
|
loadAppInfo()
|
|
loadDevSettings()
|
|
loadRegisteredCallbacks()
|
|
}
|
|
|
|
private func loadAppInfo() {
|
|
let appInfoDict = devMenuManager.getAppInfo()
|
|
|
|
self.appInfo = AppInfo(
|
|
appName: appInfoDict["appName"] as? String ?? "Unknown",
|
|
appVersion: appInfoDict["appVersion"] as? String ?? "Unknown",
|
|
runtimeVersion: appInfoDict["runtimeVersion"] as? String,
|
|
sdkVersion: appInfoDict["sdkVersion"] as? String,
|
|
hostUrl: appInfoDict["hostUrl"] as? String,
|
|
appIcon: appInfoDict["appIcon"] as? String,
|
|
engine: appInfoDict["engine"] as? String
|
|
)
|
|
}
|
|
|
|
private func loadDevSettings() {
|
|
let devSettingsDict = devMenuManager.getDevSettings()
|
|
|
|
self.devSettings = DevSettings(
|
|
isElementInspectorAvailable: devSettingsDict["isElementInspectorAvailable"] as? Bool ?? false,
|
|
isHotLoadingAvailable: devSettingsDict["isHotLoadingAvailable"] as? Bool ?? false,
|
|
isPerfMonitorAvailable: devSettingsDict["isPerfMonitorAvailable"] as? Bool ?? false,
|
|
isJSInspectorAvailable: devSettingsDict["isJSInspectorAvailable"] as? Bool ?? false,
|
|
isHotLoadingEnabled: devSettingsDict["isHotLoadingEnabled"] as? Bool ?? false
|
|
)
|
|
}
|
|
|
|
private func loadRegisteredCallbacks() {
|
|
self.registeredCallbacks = devMenuManager.registeredCallbacks.map { $0.name }
|
|
}
|
|
|
|
func hideMenu() {
|
|
devMenuManager.hideMenu()
|
|
}
|
|
|
|
func reload() {
|
|
devMenuManager.reload()
|
|
devMenuManager.closeMenu()
|
|
}
|
|
|
|
func goHome() {
|
|
devMenuManager.closeMenu()
|
|
if let devLauncherClass = NSClassFromString("EXDevLauncherController") as? NSObject.Type {
|
|
let sharedInstance = devLauncherClass.perform(Selector(("sharedInstance")))?.takeUnretainedValue()
|
|
_ = sharedInstance?.perform(Selector(("navigateToLauncher")))
|
|
}
|
|
}
|
|
|
|
func togglePerformanceMonitor() {
|
|
devMenuManager.togglePerformanceMonitor()
|
|
devMenuManager.closeMenu()
|
|
}
|
|
|
|
func toggleElementInspector() {
|
|
devMenuManager.toggleInspector()
|
|
devMenuManager.closeMenu()
|
|
}
|
|
|
|
func openJSInspector() {
|
|
devMenuManager.openJSInspector()
|
|
devMenuManager.closeMenu()
|
|
}
|
|
|
|
func toggleFastRefresh() {
|
|
devMenuManager.toggleFastRefresh()
|
|
loadDevSettings()
|
|
}
|
|
|
|
func openRNDevMenu() {
|
|
guard let rctDevMenu = devMenuManager.currentBridge?.devMenu else {
|
|
return
|
|
}
|
|
|
|
devMenuManager.closeMenu {
|
|
DevMenuPackagerConnectionHandler.allowRNDevMenuTemporarily()
|
|
rctDevMenu.show()
|
|
}
|
|
}
|
|
|
|
func copyToClipboard(_ content: String) {
|
|
#if !os(tvOS)
|
|
UIPasteboard.general.string = content
|
|
hostUrlCopiedMessage = "Copied!"
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
|
self.hostUrlCopiedMessage = nil
|
|
}
|
|
#endif
|
|
}
|
|
|
|
func copyAppInfo() {
|
|
#if !os(tvOS)
|
|
guard let appInfo = appInfo else {
|
|
return
|
|
}
|
|
|
|
var info: [String: String] = [
|
|
"appName": appInfo.appName,
|
|
"appVersion": appInfo.appVersion
|
|
]
|
|
|
|
if let runtimeVersion = appInfo.runtimeVersion {
|
|
info["runtimeVersion"] = runtimeVersion
|
|
}
|
|
|
|
if let sdkVersion = appInfo.sdkVersion {
|
|
info["sdkVersion"] = sdkVersion
|
|
}
|
|
|
|
let jsonData = try? JSONSerialization.data(withJSONObject: info, options: .prettyPrinted)
|
|
let jsonString = jsonData.flatMap { String(data: $0, encoding: .utf8) } ?? "Unable to serialize app info"
|
|
|
|
UIPasteboard.general.string = jsonString
|
|
clipboardMessage = "Copied to clipboard!"
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
|
self.clipboardMessage = nil
|
|
}
|
|
#endif
|
|
}
|
|
|
|
func fireCallback(_ name: String) {
|
|
guard let callback = devMenuManager.registeredCallbacks.first(where: { $0.name == name }) else {
|
|
return
|
|
}
|
|
|
|
devMenuManager.sendEventToDelegateBridge("registeredCallbackFired", data: name)
|
|
if callback.shouldCollapse {
|
|
devMenuManager.closeMenu()
|
|
}
|
|
}
|
|
|
|
var isDevLauncherInstalled: Bool {
|
|
return NSClassFromString("EXDevLauncherController") != nil
|
|
}
|
|
|
|
private func checkOnboardingStatus() {
|
|
isOnboardingFinished = UserDefaults.standard.bool(forKey: "EXDevMenuIsOnboardingFinished")
|
|
}
|
|
|
|
func finishOnboarding() {
|
|
UserDefaults.standard.set(true, forKey: "EXDevMenuIsOnboardingFinished")
|
|
isOnboardingFinished = true
|
|
}
|
|
|
|
private func observeRegisteredCallbacks() {
|
|
devMenuManager.callbacksPublisher
|
|
.map { $0.map { $0.name } }
|
|
.receive(on: DispatchQueue.main)
|
|
.assign(to: &$registeredCallbacks)
|
|
}
|
|
}
|