From f75bfc9c4a05028b8423c3c354ed476d5e805c3a Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 24 Jan 2022 01:16:17 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Initial=20version=20of=20#72?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 20 +++ phpmon-common/Core/Events.swift | 15 ++ .../ServiceLoading.imageset/Contents.json | 24 +++ .../question-circle.svg | 1 + .../ServiceOff.imageset/Contents.json | 24 +++ .../ServiceOff.imageset/times-circle.svg | 1 + .../ServiceOn.imageset/Contents.json | 24 +++ .../ServiceOn.imageset/check-circle.svg | 1 + phpmon/Domain/Core/App.swift | 1 - phpmon/Domain/Core/AppDelegate.swift | 2 +- phpmon/Domain/Helpers/Async.swift | 20 +++ phpmon/Domain/Menu/MainMenu+Startup.swift | 4 + phpmon/Domain/Menu/MainMenu.swift | 9 +- phpmon/Domain/Menu/ServicesView.swift | 78 +++++++++ phpmon/Domain/Menu/ServicesView.xib | 150 ++++++++++++++++++ phpmon/Domain/Menu/StatsView.xib | 16 +- phpmon/Domain/Menu/StatusMenu.swift | 12 +- phpmon/Localizable.strings | 2 +- 18 files changed, 380 insertions(+), 24 deletions(-) create mode 100644 phpmon-common/Core/Events.swift create mode 100644 phpmon/Assets.xcassets/ServiceLoading.imageset/Contents.json create mode 100644 phpmon/Assets.xcassets/ServiceLoading.imageset/question-circle.svg create mode 100644 phpmon/Assets.xcassets/ServiceOff.imageset/Contents.json create mode 100644 phpmon/Assets.xcassets/ServiceOff.imageset/times-circle.svg create mode 100644 phpmon/Assets.xcassets/ServiceOn.imageset/Contents.json create mode 100644 phpmon/Assets.xcassets/ServiceOn.imageset/check-circle.svg create mode 100644 phpmon/Domain/Helpers/Async.swift create mode 100644 phpmon/Domain/Menu/ServicesView.swift create mode 100644 phpmon/Domain/Menu/ServicesView.xib diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 7dbc8cb..565324f 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -140,6 +140,12 @@ C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; }; C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; }; C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; }; + C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4EC1E65279DE0380010F296 /* ServicesView.xib */; }; + C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; }; + C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; }; + C4EC1E6E279DF87A0010F296 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; }; + C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; }; + C4EC1E74279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; }; C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; }; C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; }; @@ -288,6 +294,10 @@ C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalSwitcher.swift; sourceTree = ""; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = ""; }; + C4EC1E65279DE0380010F296 /* ServicesView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ServicesView.xib; sourceTree = ""; }; + C4EC1E67279DE0540010F296 /* ServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServicesView.swift; sourceTree = ""; }; + C4EC1E6C279DF87A0010F296 /* Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Async.swift; sourceTree = ""; }; + C4EC1E72279DFCF40010F296 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = ""; }; C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; C4EE55A627708B9E001DF387 /* PMHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMHeaderView.swift; sourceTree = ""; }; C4EE55A727708B9E001DF387 /* Preview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preview.swift; sourceTree = ""; }; @@ -395,6 +405,7 @@ children = ( C415D3B62770F294005EF286 /* Actions.swift */, C4EE188322D3386B00E126E5 /* Constants.swift */, + C4EC1E72279DFCF40010F296 /* Events.swift */, C4B5853D2770FE3900DA4FBE /* Command.swift */, C4B5853B2770FE3900DA4FBE /* Paths.swift */, C4B5853C2770FE3900DA4FBE /* Shell.swift */, @@ -510,6 +521,8 @@ C48D0C9925CC888B00CC7490 /* HeaderView.xib */, C48D0CA225CC992000CC7490 /* StatsView.swift */, C48D0C8F25CC7FD000CC7490 /* StatsView.xib */, + C4EC1E67279DE0540010F296 /* ServicesView.swift */, + C4EC1E65279DE0380010F296 /* ServicesView.xib */, ); path = Menu; sourceTree = ""; @@ -525,6 +538,7 @@ C4CCBA6B275C567B008C7055 /* PMWindowController.swift */, 54AB03252763858F00A29D5F /* Timer.swift */, C4B5635D276AB09000F12CCB /* VersionExtractor.swift */, + C4EC1E6C279DF87A0010F296 /* Async.swift */, ); path = Helpers; sourceTree = ""; @@ -796,6 +810,7 @@ 54FCFD26276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */, C473319F2470923A009A0597 /* Localizable.strings in Resources */, C4F30B07278E195800755FCE /* brew-services.json in Resources */, + C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */, 54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */, C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */, C48D0C9A25CC888B00CC7490 /* HeaderView.xib in Resources */, @@ -831,9 +846,11 @@ C4B585432770FE3900DA4FBE /* Shell.swift in Sources */, C4D9ADC1277610E1007277F4 /* PhpSwitcher.swift in Sources */, C4F30B05278E16BA00755FCE /* HomebrewService.swift in Sources */, + C4EC1E6E279DF87A0010F296 /* Async.swift in Sources */, C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */, C4B585462770FE3900DA4FBE /* Command.swift in Sources */, C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */, + C4EC1E74279DFCF40010F296 /* Events.swift in Sources */, C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */, C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */, C417DC76277614690015E6EE /* Helpers.swift in Sources */, @@ -860,6 +877,7 @@ C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */, C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, 54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */, + C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */, C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */, C41E871A2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */, C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */, @@ -893,6 +911,8 @@ C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C42759672627662800093CAE /* NSMenuExtension.swift in Sources */, C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */, + C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */, + C4EC1E73279DFCF40010F296 /* Events.swift in Sources */, C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */, C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */, C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */, diff --git a/phpmon-common/Core/Events.swift b/phpmon-common/Core/Events.swift new file mode 100644 index 0000000..fbc803b --- /dev/null +++ b/phpmon-common/Core/Events.swift @@ -0,0 +1,15 @@ +// +// Events.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 23/01/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class Events { + + static let ServicesUpdated = Notification.Name("ServicesUpdated") + +} diff --git a/phpmon/Assets.xcassets/ServiceLoading.imageset/Contents.json b/phpmon/Assets.xcassets/ServiceLoading.imageset/Contents.json new file mode 100644 index 0000000..b7c29f9 --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceLoading.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "question-circle.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/phpmon/Assets.xcassets/ServiceLoading.imageset/question-circle.svg b/phpmon/Assets.xcassets/ServiceLoading.imageset/question-circle.svg new file mode 100644 index 0000000..df1f089 --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceLoading.imageset/question-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/phpmon/Assets.xcassets/ServiceOff.imageset/Contents.json b/phpmon/Assets.xcassets/ServiceOff.imageset/Contents.json new file mode 100644 index 0000000..5aba3d7 --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceOff.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "times-circle.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/phpmon/Assets.xcassets/ServiceOff.imageset/times-circle.svg b/phpmon/Assets.xcassets/ServiceOff.imageset/times-circle.svg new file mode 100644 index 0000000..b5d5eba --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceOff.imageset/times-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/phpmon/Assets.xcassets/ServiceOn.imageset/Contents.json b/phpmon/Assets.xcassets/ServiceOn.imageset/Contents.json new file mode 100644 index 0000000..e3569a7 --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceOn.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "check-circle.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/phpmon/Assets.xcassets/ServiceOn.imageset/check-circle.svg b/phpmon/Assets.xcassets/ServiceOn.imageset/check-circle.svg new file mode 100644 index 0000000..5673a79 --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceOn.imageset/check-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/phpmon/Domain/Core/App.swift b/phpmon/Domain/Core/App.swift index 450bc84..c41fbd2 100644 --- a/phpmon/Domain/Core/App.swift +++ b/phpmon/Domain/Core/App.swift @@ -43,7 +43,6 @@ class App: PhpSwitcherDelegate { /** Timer that will periodically reload info about the user's PHP installation. */ var timer: Timer? - // MARK: - Global Hotkey diff --git a/phpmon/Domain/Core/AppDelegate.swift b/phpmon/Domain/Core/AppDelegate.swift index 3183636..3b50045 100644 --- a/phpmon/Domain/Core/AppDelegate.swift +++ b/phpmon/Domain/Core/AppDelegate.swift @@ -59,7 +59,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele When the application initializes, create all singletons. */ override init() { - logger.verbosity = .info + logger.verbosity = .performance Log.info("==================================") Log.info("PHP MONITOR by Nico Verbruggen") Log.info("Version \(App.version)") diff --git a/phpmon/Domain/Helpers/Async.swift b/phpmon/Domain/Helpers/Async.swift new file mode 100644 index 0000000..d427974 --- /dev/null +++ b/phpmon/Domain/Helpers/Async.swift @@ -0,0 +1,20 @@ +// +// Async.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 23/01/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +public func runAsync(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {}) +{ + DispatchQueue.global(qos: .userInitiated).async { + execute() + + DispatchQueue.main.async { + completion() + } + } +} diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 573684a..d418d7e 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -70,8 +70,12 @@ extension MainMenu { // Attempt to find out more info about Valet Log.info("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version)") + Valet.shared.validateVersion() Valet.shared.startPreloadingSites() + + NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil) + Log.info("PHP Monitor is ready to serve!") // Schedule a request to fetch the PHP version every 60 seconds diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index c9d9f1f..b515d4a 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -100,13 +100,12 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { PhpEnv.shared.isBusy = true setBusyImage() DispatchQueue.global(qos: .userInitiated).async { [unowned self] in - rebuild() execute() PhpEnv.shared.isBusy = false DispatchQueue.main.async { [self] in updatePhpVersionInStatusBar() - rebuild() + NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil) completion() } } @@ -156,12 +155,6 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { waitAndExecute { // This automatically reloads the menu Log.info("Reloading information about the PHP installation...") - } completion: { - // Add a slight delay to make sure it loads the new menu - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - // Open the menu again - MainMenu.shared.statusItem.button?.performClick(nil) - } } } diff --git a/phpmon/Domain/Menu/ServicesView.swift b/phpmon/Domain/Menu/ServicesView.swift new file mode 100644 index 0000000..a759a5b --- /dev/null +++ b/phpmon/Domain/Menu/ServicesView.swift @@ -0,0 +1,78 @@ +// +// StatsView.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 04/02/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation +import Cocoa + +class ServicesView: NSView, XibLoadable { + + @IBOutlet weak var imageViewPhp: NSImageView! + @IBOutlet weak var imageViewNginx: NSImageView! + @IBOutlet weak var imageViewDnsmasq: NSImageView! + + @IBOutlet weak var textFieldPhp: NSTextField! + + var services: [String: HomebrewService] = [:] + + static func asMenuItem() -> NSMenuItem { + let view = Self.createFromXib() + let item = NSMenuItem() + item.view = view + item.target = self + NotificationCenter.default.addObserver( + view!, selector: #selector(self.updateInformation), + name: Events.ServicesUpdated, + object: nil + ) + return item + } + + override func viewWillDraw() { + super.viewWillDraw() + self.loadData() + } + + @objc func updateInformation() { + self.loadData() + } + + func loadData() { + runAsync { + let servicesList = try! JSONDecoder().decode( + [HomebrewService].self, + from: Shell.pipe( + "sudo \(Paths.brew) services info --all --json", + requiresPath: true + ).data(using: .utf8)! + ).filter({ service in + return [PhpEnv.phpInstall.formula, "nginx", "dnsmasq"].contains(service.name) + }) + + self.services = Dictionary(uniqueKeysWithValues: servicesList.map{ ($0.name, $0) }) + } completion: { + self.textFieldPhp.stringValue = PhpEnv.phpInstall.formula.uppercased() + self.applyServiceStyling(PhpEnv.phpInstall.formula, self.imageViewPhp) + self.applyServiceStyling("nginx", self.imageViewNginx) + self.applyServiceStyling("dnsmasq", self.imageViewDnsmasq) + } + } + + func applyServiceStyling(_ serviceName: String, _ imageView: NSImageView) { + if services[serviceName] != nil && services[serviceName]!.running { + imageView.image = NSImage(named: "ServiceOn") + imageView.contentTintColor = NSColor.black + } else { + imageView.image = NSImage(named: "ServiceOff") + imageView.contentTintColor = NSColor.init(red: 246/255, green: 71/255, blue: 71/255, alpha: 1.0) + } + } + + deinit { + NotificationCenter.default.removeObserver(self, name: Events.ServicesUpdated, object: nil) + } +} diff --git a/phpmon/Domain/Menu/ServicesView.xib b/phpmon/Domain/Menu/ServicesView.xib new file mode 100644 index 0000000..c1371ff --- /dev/null +++ b/phpmon/Domain/Menu/ServicesView.xib @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpmon/Domain/Menu/StatsView.xib b/phpmon/Domain/Menu/StatsView.xib index 1ad4bd9..fd6bca0 100644 --- a/phpmon/Domain/Menu/StatsView.xib +++ b/phpmon/Domain/Menu/StatsView.xib @@ -1,8 +1,8 @@ - + - + @@ -10,11 +10,11 @@ - + - + @@ -75,10 +75,10 @@ - + - + @@ -86,7 +86,7 @@ - + @@ -138,7 +138,7 @@ - + diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 0a34b91..ed780c4 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -32,10 +32,13 @@ class StatusMenu : NSMenu { self.addSwitchToPhpMenuItems() self.addItem(NSMenuItem.separator()) + + self.addItem(ServicesView.asMenuItem()) + self.addItem(NSMenuItem.separator()) } - func addServicesMenuItems() { - let services = NSMenuItem(title: "mi_toolkit".localized, action: nil, keyEquivalent: "") + func addOtherMenuItems() { + let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "") let servicesMenu = NSMenu() servicesMenu.addItem(NSMenuItem(title: "mi_help".localized, action: nil, keyEquivalent: "")) @@ -122,10 +125,9 @@ class StatusMenu : NSMenu { shortcutKey += 1 } + // Other self.addItem(NSMenuItem.separator()) - - self.addServicesMenuItems() - + self.addOtherMenuItems() } private func addSwitchToPhpMenuItems() { diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 198d6ae..bd3f063 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -37,7 +37,7 @@ "mi_manual_actions" = "Manual Actions"; "mi_services" = "Services"; "mi_help" = "First Aid"; -"mi_toolkit" = "Toolkit"; +"mi_other" = "First Aid & Services"; "mi_composer" = "Composer"; "mi_valet_config" = "Locate Valet folder (.config/valet)";