diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index d432575..1c7b251 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -125,6 +125,8 @@ C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; }; C44F868E2835BD8D005C353A /* phpmon-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C44F868D2835BD8D005C353A /* phpmon-config.json */; }; C459B4BD27F6093700E9B4B4 /* nginx-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */; }; + C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; }; + C45E76152854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; }; C463E380284930EE00422731 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; }; C463E381284930EE00422731 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; }; C464ADAC275A7A3F003FCD53 /* DomainListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */; }; @@ -360,6 +362,7 @@ C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = ""; }; C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = ""; }; C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-proxy.test"; sourceTree = ""; }; + C45E76132854A65300B4FE0C /* ServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesManager.swift; sourceTree = ""; }; C463E37F284930EE00422731 /* PresetHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetHelper.swift; sourceTree = ""; }; C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListWC.swift; sourceTree = ""; }; C464ADAE275A7A69003FCD53 /* DomainListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListVC.swift; sourceTree = ""; }; @@ -846,6 +849,7 @@ C4D8016522B1584700C6DA1B /* Startup.swift */, C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */, C40FE736282ABA4F00A302C2 /* AppVersion.swift */, + C45E76132854A65300B4FE0C /* ServicesManager.swift */, ); path = App; sourceTree = ""; @@ -1275,6 +1279,7 @@ C46E206D28299B3800D909D6 /* AppUpdateChecker.swift in Sources */, C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */, C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */, + C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */, C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */, C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */, C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, @@ -1444,6 +1449,7 @@ C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */, C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */, C4B585422770FE3900DA4FBE /* Shell.swift in Sources */, + C45E76152854A65300B4FE0C /* ServicesManager.swift in Sources */, C464ADAD275A7A3F003FCD53 /* DomainListWC.swift in Sources */, C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */, C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */, diff --git a/phpmon/Domain/App/App.swift b/phpmon/Domain/App/App.swift index 51bfc89..fcbee6f 100644 --- a/phpmon/Domain/App/App.swift +++ b/phpmon/Domain/App/App.swift @@ -59,6 +59,9 @@ class App { /** List of detected (installed) applications that PHP Monitor can work with. */ var detectedApplications: [Application] = [] + /** The services manager, responsible for figuring out what services are active/inactive. */ + var services = ServicesManager.shared + /** Timer that will periodically reload info about the user's PHP installation. */ var timer: Timer? diff --git a/phpmon/Domain/App/ServicesManager.swift b/phpmon/Domain/App/ServicesManager.swift new file mode 100644 index 0000000..6be3041 --- /dev/null +++ b/phpmon/Domain/App/ServicesManager.swift @@ -0,0 +1,24 @@ +// +// ServicesManager.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 11/06/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation +import SwiftUI + +class ServicesManager: ObservableObject { + + static var shared = ServicesManager() + + @Published var services: [String: HomebrewService] = [:] + + func loadData() { + HomebrewService.loadAll { services in + self.services = Dictionary(uniqueKeysWithValues: services.map { ($0.name, $0) }) + } + } + +} diff --git a/phpmon/Domain/Menu/MainMenu+Async.swift b/phpmon/Domain/Menu/MainMenu+Async.swift index 69d7877..3a9d273 100644 --- a/phpmon/Domain/Menu/MainMenu+Async.swift +++ b/phpmon/Domain/Menu/MainMenu+Async.swift @@ -74,7 +74,7 @@ extension MainMenu { } if behaviours.contains(.broadcastServicesUpdate) { - NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil) + ServicesManager.shared.loadData() } error == nil ? success() : failure(error!) diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 759ae3d..9e08d4c 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -76,7 +76,7 @@ extension MainMenu { // A non-default TLD is not officially supported since Valet 3.2.x Valet.notifyAboutUnsupportedTLD() - NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil) + ServicesManager.shared.loadData() // Schedule a request to fetch the PHP version every 60 seconds DispatchQueue.main.async { [self] in diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 797651a..bf4990e 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -122,7 +122,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate refreshActiveInstallation() refreshIcon() rebuild(async: false) - NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil) + ServicesManager.shared.loadData() } /** Reloads the menu in the background, using `asyncExecution`. */ @@ -197,7 +197,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate func menuWillOpen(_ menu: NSMenu) { // Make sure the shortcut key does not trigger this when the menu is open App.shared.shortcutHotkey?.isPaused = true - NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil) + ServicesManager.shared.loadData() } func menuDidClose(_ menu: NSMenu) { diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 5da271e..d2eeee7 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -34,7 +34,7 @@ class StatusMenu: NSMenu { self.addSwitchToPhpMenuItems() self.addItem(NSMenuItem.separator()) - self.addItem(ServicesView.asMenuItem()) + self.addItem(NXServicesView.asMenuItem()) self.addItem(NSMenuItem.separator()) } diff --git a/phpmon/Domain/SwiftUI/Menu/NXServicesView.swift b/phpmon/Domain/SwiftUI/Menu/NXServicesView.swift index 0e9feab..b631a42 100644 --- a/phpmon/Domain/SwiftUI/Menu/NXServicesView.swift +++ b/phpmon/Domain/SwiftUI/Menu/NXServicesView.swift @@ -11,31 +11,69 @@ import Foundation import SwiftUI struct NXServicesView: View { + @ObservedObject var serviceManager: ServicesManager - var services: [String] = ["php", "nginx", "dnsmasq"] + @State var serviceNames: [String] = { + return [ + PhpEnv.phpInstall.formula, + "nginx", + "dnsmasq" + ] + }() - static func asMenuItem(memory: String, post: String, upload: String) -> NSMenuItem { + static func asMenuItem() -> NSMenuItem { let item = NSMenuItem() - let view = NSHostingView(rootView: Self()) + let view = NSHostingView(rootView: Self(serviceManager: ServicesManager.shared)) view.frame = CGRect(x: 0, y: 0, width: 330, height: 55) item.view = view return item } var body: some View { - HStack(alignment: .firstTextBaseline, spacing: 30) { - VStack(alignment: .center, spacing: 3) { - MiniHeaderView(text: "hello".uppercased()) - Text("") - .fontWeight(.medium) - .font(.system(size: 16)) + HStack(alignment: .firstTextBaseline, spacing: 10) { + ForEach(serviceNames, id: \.self) { service in + VStack(alignment: .center, spacing: 3) { + MiniHeaderView(text: service.uppercased()) + CheckmarkView(serviceName: service).environmentObject(serviceManager) + }.frame(width: 90) } }.padding(10) } } -struct NXServicesView_Previews: PreviewProvider { - static var previews: some View { - NXServicesView() +struct CheckmarkView: View { + @State var serviceName: String + @EnvironmentObject var serviceManager: ServicesManager + + public func hasAnyServices() -> Bool { + return !serviceManager.services.isEmpty + } + + public func active() -> Bool { + guard let service = serviceManager.services[serviceName] else { + return false + } + + return service.running + } + + var body: some View { + if !hasAnyServices() { + Image(systemName: "questionmark.circle") + .resizable() + .frame(width: 16.0, height: 16.0) + .foregroundColor(.black) + } else { + Image(systemName: "checkmark.circle") + .resizable() + .frame(width: 16.0, height: 16.0) + .foregroundColor(active() ? Color.black : Color("IconColorRed")) + } + } +} + +struct NXServicesView_Previews: PreviewProvider { + static var previews: some View { + NXServicesView(serviceManager: ServicesManager.shared) } }