diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index b3dc014..0532a33 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -152,8 +152,6 @@ C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; }; C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; }; C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9225CC804200CC7490 /* XibLoadable.swift */; }; - C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9525CC80B100CC7490 /* HeaderView.swift */; }; - C48D0C9A25CC888B00CC7490 /* HeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C48D0C9925CC888B00CC7490 /* HeaderView.xib */; }; C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; }; C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; }; C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */; }; @@ -180,7 +178,7 @@ C4B585422770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; }; C4B585442770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; }; C4B585452770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; }; - C4B6091A2853AAD300C95265 /* MiniHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B609192853AAD300C95265 /* MiniHeaderView.swift */; }; + C4B6091A2853AAD300C95265 /* SectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B609192853AAD300C95265 /* SectionHeaderView.swift */; }; C4B6091D2853AB9700C95265 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B6091C2853AB9700C95265 /* ServicesView.swift */; }; C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; }; C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; }; @@ -229,6 +227,8 @@ C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */; }; C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; }; C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; }; + C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E428551F9B006F9937 /* HeaderView.swift */; }; + C4EB53E728553117006F9937 /* ArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E628553117006F9937 /* ArrayExtension.swift */; }; C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; }; C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; }; @@ -255,7 +255,6 @@ C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; }; C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; - C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9525CC80B100CC7490 /* HeaderView.swift */; }; C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; }; C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; }; C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9225CC804200CC7490 /* XibLoadable.swift */; }; @@ -378,8 +377,6 @@ C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = ""; }; C484437A2804BB560041A78A /* ValetProxyScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetProxyScanner.swift; sourceTree = ""; }; C48D0C9225CC804200CC7490 /* XibLoadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibLoadable.swift; sourceTree = ""; }; - C48D0C9525CC80B100CC7490 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; }; - C48D0C9925CC888B00CC7490 /* HeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HeaderView.xib; sourceTree = ""; }; C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionNumber.swift; sourceTree = ""; }; C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = ""; }; C4927F0A27B2DFC200C55AFD /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; @@ -396,7 +393,7 @@ C4B5853B2770FE3900DA4FBE /* Paths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = ""; }; C4B5853C2770FE3900DA4FBE /* Shell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = ""; }; C4B5853D2770FE3900DA4FBE /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; - C4B609192853AAD300C95265 /* MiniHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniHeaderView.swift; sourceTree = ""; }; + C4B609192853AAD300C95265 /* SectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeaderView.swift; sourceTree = ""; }; C4B6091C2853AB9700C95265 /* ServicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesView.swift; sourceTree = ""; }; C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = ""; }; C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = ""; }; @@ -425,6 +422,8 @@ C4E4404527C56F4700D225E1 /* ValetSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetSite.swift; sourceTree = ""; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = ""; }; + C4EB53E428551F9B006F9937 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; }; + C4EB53E628553117006F9937 /* ArrayExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayExtension.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 = ""; }; C4EED88827A48778006D7272 /* InterAppHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterAppHandler.swift; sourceTree = ""; }; @@ -767,8 +766,6 @@ C4F361602836BFD9003598CC /* MainMenu+Actions.swift */, C47331A1247093B7009A0597 /* StatusMenu.swift */, C42800A928452AA10099C999 /* StatusMenu+Items.swift */, - C48D0C9525CC80B100CC7490 /* HeaderView.swift */, - C48D0C9925CC888B00CC7490 /* HeaderView.xib */, ); path = Menu; sourceTree = ""; @@ -873,7 +870,8 @@ children = ( C4B6091C2853AB9700C95265 /* ServicesView.swift */, C4709CA128524B3400088BB8 /* StatsView.swift */, - C4B609192853AAD300C95265 /* MiniHeaderView.swift */, + C4B609192853AAD300C95265 /* SectionHeaderView.swift */, + C4EB53E428551F9B006F9937 /* HeaderView.swift */, ); path = Menu; sourceTree = ""; @@ -1049,6 +1047,7 @@ C48D0C9225CC804200CC7490 /* XibLoadable.swift */, C42759662627662800093CAE /* NSMenuExtension.swift */, C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */, + C4EB53E628553117006F9937 /* ArrayExtension.swift */, ); path = Extensions; sourceTree = ""; @@ -1152,7 +1151,6 @@ C4068CA427B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */, 54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */, C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */, - C48D0C9A25CC888B00CC7490 /* HeaderView.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1215,6 +1213,7 @@ C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */, C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */, C4C0E8EA27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */, + C4EB53E728553117006F9937 /* ArrayExtension.swift in Sources */, 5420395926135DC100FB00FA /* PrefsVC.swift in Sources */, C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, 5489625828312FAD004F647A /* CreatedFromFile.swift in Sources */, @@ -1234,13 +1233,14 @@ C4C0E8DF27F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */, C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */, C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */, + C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */, C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */, C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */, C4B585442770FE3900DA4FBE /* Command.swift in Sources */, C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */, C40C5C9C2846A40600E28255 /* Preset.swift in Sources */, C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */, - C4B6091A2853AAD300C95265 /* MiniHeaderView.swift in Sources */, + C4B6091A2853AAD300C95265 /* SectionHeaderView.swift in Sources */, C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */, C42800AA28452AA10099C999 /* StatusMenu+Items.swift in Sources */, C415D3B72770F294005EF286 /* Actions.swift in Sources */, @@ -1297,7 +1297,6 @@ C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */, C476FF9822B0DD830098105B /* Alert.swift in Sources */, C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */, - C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */, C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */, C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */, C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */, @@ -1410,7 +1409,6 @@ C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */, C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */, C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */, - C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */, C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */, C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */, C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */, @@ -1599,7 +1597,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 810; + CURRENT_PROJECT_VERSION = 900; DEBUG = YES; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; @@ -1609,7 +1607,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = "5.4-dev"; + MARKETING_VERSION = "5.5-dev"; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1626,7 +1624,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 810; + CURRENT_PROJECT_VERSION = 900; DEBUG = NO; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; @@ -1636,7 +1634,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = "5.4-dev"; + MARKETING_VERSION = "5.5-dev"; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme index ada3675..6d711af 100644 --- a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme +++ b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme @@ -69,8 +69,8 @@ diff --git a/phpmon/Common/Core/Actions.swift b/phpmon/Common/Core/Actions.swift index e8aaf5f..f074a36 100644 --- a/phpmon/Common/Core/Actions.swift +++ b/phpmon/Common/Core/Actions.swift @@ -24,7 +24,7 @@ class Actions { brew("services restart dnsmasq", sudo: true) } - public static func stopAllServices() { + public static func stopValetServices() { brew("services stop \(PhpEnv.phpInstall.formula)", sudo: true) brew("services stop nginx", sudo: true) brew("services stop dnsmasq", sudo: true) diff --git a/phpmon/Common/Extensions/ArrayExtension.swift b/phpmon/Common/Extensions/ArrayExtension.swift new file mode 100644 index 0000000..e2f067d --- /dev/null +++ b/phpmon/Common/Extensions/ArrayExtension.swift @@ -0,0 +1,24 @@ +// +// ArrayExtension.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 11/06/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +extension Array { + /** + Sourced from Stack Overflow + https://stackoverflow.com/a/33540708 + */ + func chunked(by distance: Int) -> [[Element]] { + let indicesSequence = stride(from: startIndex, to: endIndex, by: distance) + let array: [[Element]] = indicesSequence.map { + let newIndex = $0.advanced(by: distance) > endIndex ? endIndex : $0.advanced(by: distance) + return Array(self[$0 ..< newIndex]) + } + return array + } +} diff --git a/phpmon/Common/PHP/Homebrew/HomebrewService.swift b/phpmon/Common/PHP/Homebrew/HomebrewService.swift index cd1f02d..3d7a361 100644 --- a/phpmon/Common/PHP/Homebrew/HomebrewService.swift +++ b/phpmon/Common/PHP/Homebrew/HomebrewService.swift @@ -19,25 +19,6 @@ struct HomebrewService: Decodable, Equatable { let log_path: String? let error_log_path: String? - public static func loadAll( - filter: [String] = [PhpEnv.phpInstall.formula, "nginx", "dnsmasq"], - completion: @escaping ([HomebrewService]) -> Void - ) { - DispatchQueue.global(qos: .background).async { - let data = Shell - .pipe("sudo \(Paths.brew) services info --all --json", requiresPath: true) - .data(using: .utf8)! - - let services = try! JSONDecoder() - .decode([HomebrewService].self, from: data) - .filter({ return filter.contains($0.name) }) - - DispatchQueue.main.async { - completion(services) - } - } - } - /** Dummy data for preview purposes. */ diff --git a/phpmon/Domain/App/InterAppHandler.swift b/phpmon/Domain/App/InterAppHandler.swift index 2529441..227fde3 100644 --- a/phpmon/Domain/App/InterAppHandler.swift +++ b/phpmon/Domain/App/InterAppHandler.swift @@ -26,10 +26,10 @@ class InterApp { DomainListVC.show() }), InterApp.Action(command: "services/stop", action: { _ in - MainMenu.shared.stopAllServices() + MainMenu.shared.stopValetServices() }), InterApp.Action(command: "services/restart/all", action: { _ in - MainMenu.shared.restartAllServices() + MainMenu.shared.restartValetServices() }), InterApp.Action(command: "services/restart/nginx", action: { _ in MainMenu.shared.restartNginx() diff --git a/phpmon/Domain/App/ServicesManager.swift b/phpmon/Domain/App/ServicesManager.swift index 5d99b9f..1bd3a2a 100644 --- a/phpmon/Domain/App/ServicesManager.swift +++ b/phpmon/Domain/App/ServicesManager.swift @@ -13,14 +13,55 @@ class ServicesManager: ObservableObject { static var shared = ServicesManager() - @Published var services: [String: HomebrewService] = [:] + @Published var rootServices: [String: HomebrewService] = [:] + @Published var userServices: [String: HomebrewService] = [:] + + public static func loadHomebrewServices() { + let rootServiceNames = [ + PhpEnv.phpInstall.formula, + "nginx", + "dnsmasq" + ] + + DispatchQueue.global(qos: .background).async { + let data = Shell + .pipe("sudo \(Paths.brew) services info --all --json", requiresPath: true) + .data(using: .utf8)! + + let services = try! JSONDecoder() + .decode([HomebrewService].self, from: data) + .filter({ return rootServiceNames.contains($0.name) }) + + DispatchQueue.main.async { + ServicesManager.shared.rootServices = Dictionary( + uniqueKeysWithValues: services.map { ($0.name, $0) } + ) + } + } + + guard let userServiceNames = Preferences.custom.services else { + return + } + + DispatchQueue.global(qos: .background).async { + let data = Shell + .pipe("\(Paths.brew) services info --all --json", requiresPath: true) + .data(using: .utf8)! + + let services = try! JSONDecoder() + .decode([HomebrewService].self, from: data) + .filter({ return userServiceNames.contains($0.name) }) + + DispatchQueue.main.async { + ServicesManager.shared.userServices = Dictionary( + uniqueKeysWithValues: services.map { ($0.name, $0) } + ) + } + } + } func loadData() { - HomebrewService.loadAll { services in - self.services = Dictionary( - uniqueKeysWithValues: services.map { ($0.name, $0) } - ) - } + Self.loadHomebrewServices() } /** @@ -29,7 +70,7 @@ class ServicesManager: ObservableObject { func withDummyServices(_ services: [String: Bool]) -> Self { for (service, enabled) in services { let item = HomebrewService.dummy(named: service, enabled: enabled) - self.services[service] = item + self.rootServices[service] = item } return self diff --git a/phpmon/Domain/Menu/HeaderView.swift b/phpmon/Domain/Menu/HeaderView.swift deleted file mode 100644 index 0f7d23c..0000000 --- a/phpmon/Domain/Menu/HeaderView.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// HeaderView.swift -// PHP Monitor -// -// Created by Nico Verbruggen on 04/02/2021. -// Copyright © 2022 Nico Verbruggen. All rights reserved. -// - -import Foundation -import Cocoa - -class HeaderView: NSView, XibLoadable { - - @IBOutlet weak var textField: NSTextField! - - static func asMenuItem( - text: String, - width: Int? = nil - ) -> NSMenuItem { - let view = Self.createFromXib()! - - view.autoresizingMask = [.width, .height] - - view.textField.stringValue = text.uppercased() - view.textField.sizeToFit() - - view.setFrameSize(CGSize(width: view.textField.frame.width + 40, height: view.frame.height)) - - let item = NSMenuItem() - item.view = view - item.target = self - - return item - } - -} diff --git a/phpmon/Domain/Menu/HeaderView.xib b/phpmon/Domain/Menu/HeaderView.xib deleted file mode 100644 index b0dbbe9..0000000 --- a/phpmon/Domain/Menu/HeaderView.xib +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/phpmon/Domain/Menu/MainMenu+Actions.swift b/phpmon/Domain/Menu/MainMenu+Actions.swift index cb8fe05..a961710 100644 --- a/phpmon/Domain/Menu/MainMenu+Actions.swift +++ b/phpmon/Domain/Menu/MainMenu+Actions.swift @@ -47,7 +47,7 @@ extension MainMenu { } } - @objc func restartAllServices() { + @objc func restartValetServices() { asyncExecution { Actions.restartDnsMasq() Actions.restartPhpFpm() @@ -63,9 +63,9 @@ extension MainMenu { } } - @objc func stopAllServices() { + @objc func stopValetServices() { asyncExecution { - Actions.stopAllServices() + Actions.stopValetServices() } success: { DispatchQueue.main.async { LocalNotification.send( diff --git a/phpmon/Domain/Menu/StatusMenu+Items.swift b/phpmon/Domain/Menu/StatusMenu+Items.swift index f438145..e12ad5c 100644 --- a/phpmon/Domain/Menu/StatusMenu+Items.swift +++ b/phpmon/Domain/Menu/StatusMenu+Items.swift @@ -65,7 +65,12 @@ extension StatusMenu { } func addPresetsMenuItem() { - if Preferences.custom.presets.isEmpty { + guard let presets = Preferences.custom.presets else { + addEmptyPresetHelp() + return + } + + if presets.isEmpty { addEmptyPresetHelp() return } @@ -102,7 +107,7 @@ extension StatusMenu { presetsMenu.addItem(NSMenuItem.separator()) presetsMenu.addItem(HeaderView.asMenuItem(text: "mi_apply_presets_title".localized)) - for preset in Preferences.custom.presets { + for preset in Preferences.custom.presets! { let presetMenuItem = PresetMenuItem( title: preset.getMenuItemText(), action: #selector(MainMenu.togglePreset(sender:)), @@ -132,7 +137,7 @@ extension StatusMenu { presetsMenu.addItem(NSMenuItem.separator()) presetsMenu.addItem(NSMenuItem( title: "mi_profiles_loaded".localized( - Preferences.custom.presets.count + Preferences.custom.presets!.count ), action: nil, keyEquivalent: "") ) @@ -219,12 +224,12 @@ extension StatusMenu { action: #selector(MainMenu.restartNginx), keyEquivalent: "n") ) servicesMenu.addItem( - NSMenuItem(title: "mi_restart_all_services".localized, - action: #selector(MainMenu.restartAllServices), keyEquivalent: "s") + NSMenuItem(title: "mi_restart_valet_services".localized, + action: #selector(MainMenu.restartValetServices), keyEquivalent: "s") ) servicesMenu.addItem( - NSMenuItem(title: "mi_stop_all_services".localized, - action: #selector(MainMenu.stopAllServices), keyEquivalent: "s"), + NSMenuItem(title: "mi_stop_valet_services".localized, + action: #selector(MainMenu.stopValetServices), keyEquivalent: "s"), withKeyModifier: [.command, .shift] ) diff --git a/phpmon/Domain/Preferences/CustomPrefs.swift b/phpmon/Domain/Preferences/CustomPrefs.swift index 03d6db6..6706f2e 100644 --- a/phpmon/Domain/Preferences/CustomPrefs.swift +++ b/phpmon/Domain/Preferences/CustomPrefs.swift @@ -10,10 +10,20 @@ import Foundation struct CustomPrefs: Decodable { let scanApps: [String] - let presets: [Preset] + let presets: [Preset]? + let services: [String]? + + public func hasPresets() -> Bool { + return self.presets != nil && !self.presets!.isEmpty + } + + public func hasServices() -> Bool { + return self.services != nil && !self.services!.isEmpty + } private enum CodingKeys: String, CodingKey { case scanApps = "scan_apps" case presets = "presets" + case services = "services" } } diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index ee7bc44..1071892 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -65,7 +65,7 @@ class Preferences { public init() { Preferences.handleFirstTimeLaunch() cachedPreferences = Self.cache() - customPreferences = CustomPrefs(scanApps: [], presets: []) + customPreferences = CustomPrefs(scanApps: [], presets: [], services: []) loadCustomPreferences() } @@ -228,7 +228,14 @@ class Preferences { ) Log.info("The ~/.config/phpmon/config.json file was successfully parsed.") - Log.info("There are \(customPreferences.presets.count) custom presets.") + + if customPreferences.hasPresets() { + Log.info("There are \(customPreferences.presets!.count) custom presets.") + } + + if customPreferences.hasServices() { + Log.info("There are custom services: \(customPreferences.services!)") + } } catch { Log.warn("The ~/.config/phpmon/config.json file seems to be missing or malformed.") } diff --git a/phpmon/Domain/SwiftUI/Common/SwiftUIHelper.swift b/phpmon/Domain/SwiftUI/Common/SwiftUIHelper.swift index d23e45e..71e7a8c 100644 --- a/phpmon/Domain/SwiftUI/Common/SwiftUIHelper.swift +++ b/phpmon/Domain/SwiftUI/Common/SwiftUIHelper.swift @@ -7,8 +7,18 @@ // import Foundation +import SwiftUI var isRunningSwiftUIPreview: Bool { return ProcessInfo.processInfo .environment["XCODE_RUNNING_FOR_PREVIEWS"] != nil } + +extension Color { + public static var debug: Color = { + if ProcessInfo.processInfo.environment["PAINT_PHPMON_SWIFTUI_VIEWS"] != nil { + return Color.yellow + } + return Color.clear + }() +} diff --git a/phpmon/Domain/SwiftUI/Menu/HeaderView.swift b/phpmon/Domain/SwiftUI/Menu/HeaderView.swift new file mode 100644 index 0000000..cddec55 --- /dev/null +++ b/phpmon/Domain/SwiftUI/Menu/HeaderView.swift @@ -0,0 +1,47 @@ +// +// MiniHeaderView.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 10/06/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import SwiftUI + +struct HeaderView: View { + @State var text: String + + var body: some View { + Text(text.uppercased()) + .font(.system(size: 12)) + .fontWeight(.bold) + .foregroundColor(.secondary) + .multilineTextAlignment(.leading) + .padding(.leading, 14.0) + .frame(maxWidth: .infinity, alignment: .leading) + .background(Color.debug) + } + + // MARK: - NSMenuItem + + static func asMenuItem( + text: String, + width: Int? = nil + ) -> NSMenuItem { + let view = NSHostingView(rootView: Self(text: text)) + view.autoresizingMask = [.width, .height] + view.setFrameSize(CGSize(width: view.frame.width, height: 24)) + + let item = NSMenuItem() + item.view = view + + return item + } +} + +struct HeaderView_Previews: PreviewProvider { + static var previews: some View { + HeaderView(text: "Hello world") + .frame(width: 330.0) + } +} diff --git a/phpmon/Domain/SwiftUI/Menu/MiniHeaderView.swift b/phpmon/Domain/SwiftUI/Menu/SectionHeaderView.swift similarity index 83% rename from phpmon/Domain/SwiftUI/Menu/MiniHeaderView.swift rename to phpmon/Domain/SwiftUI/Menu/SectionHeaderView.swift index 2aba338..821ddac 100644 --- a/phpmon/Domain/SwiftUI/Menu/MiniHeaderView.swift +++ b/phpmon/Domain/SwiftUI/Menu/SectionHeaderView.swift @@ -8,7 +8,8 @@ import SwiftUI -struct MiniHeaderView: View { +struct SectionHeaderView: View { + @State var text: String var body: some View { @@ -16,5 +17,6 @@ struct MiniHeaderView: View { .font(.system(size: 11)) .fontWeight(.medium) .foregroundColor(.secondary) + .background(Color.debug) } } diff --git a/phpmon/Domain/SwiftUI/Menu/ServicesView.swift b/phpmon/Domain/SwiftUI/Menu/ServicesView.swift index 1eaee95..23b3e10 100644 --- a/phpmon/Domain/SwiftUI/Menu/ServicesView.swift +++ b/phpmon/Domain/SwiftUI/Menu/ServicesView.swift @@ -15,31 +15,47 @@ struct ServicesView: View { static func asMenuItem() -> NSMenuItem { let item = NSMenuItem() + var services = [ + PhpEnv.phpInstall.formula, + "nginx", + "dnsmasq" + ] + + if Preferences.custom.hasServices() { + services += Preferences.custom.services! + } + let view = NSHostingView( rootView: Self( manager: ServicesManager.shared, - servicesToDisplay: [ - PhpEnv.phpInstall.formula, - "nginx", - "dnsmasq" - ] + servicesToDisplay: services ) ) - view.frame = CGRect(x: 0, y: 0, width: 330, height: 45) + + view.autoresizingMask = [.width, .height] + let height = CGFloat(45 * services.chunked(by: 3).count) + view.setFrameSize(CGSize(width: view.frame.width, height: height)) item.view = view return item } var body: some View { - HStack(alignment: .firstTextBaseline, spacing: 10) { - ForEach(servicesToDisplay, id: \.self) { service in - VStack(alignment: .center, spacing: 3) { - MiniHeaderView(text: service.uppercased()) - CheckmarkView(serviceName: service) - .environmentObject(manager) - }.frame(minWidth: 0, maxWidth: .infinity) + VStack(alignment: .leading, spacing: 10) { + ForEach(servicesToDisplay.chunked(by: 3), id: \.self) { chunk in + HStack { + ForEach(chunk, id: \.self) { service in + VStack(alignment: .center, spacing: 3) { + SectionHeaderView(text: service.uppercased()) + CheckmarkView(serviceName: service) + .environmentObject(manager) + }.frame(minWidth: 0, maxWidth: .infinity) + } + } } - }.padding(10) + } + .padding(10) + .frame(minWidth: 0, maxWidth: .infinity) + .background(Color.debug) } } @@ -48,28 +64,39 @@ struct CheckmarkView: View { @EnvironmentObject var manager: ServicesManager public func hasAnyServices() -> Bool { - return !manager.services.isEmpty + return !manager.rootServices.isEmpty } - public func active() -> Bool { - guard let service = manager.services[serviceName] else { - return false + public func active() -> Bool? { + if manager.rootServices.keys.contains(serviceName) { + return manager.rootServices[serviceName]!.running } - return service.running + if manager.userServices.keys.contains(serviceName) { + return manager.userServices[serviceName]!.running + } + + return nil } var body: some View { if !hasAnyServices() { - Image(systemName: "questionmark.circle") + Image(systemName: "hourglass.circle") .resizable() .frame(width: 16.0, height: 16.0) .foregroundColor(.secondary) } else { - Image(systemName: active() ? "checkmark.circle" : "exclamationmark.triangle") - .resizable() - .frame(width: 16.0, height: 16.0) - .foregroundColor(active() ? Color("IconColorGreen") : Color("IconColorRed")) + if active() == nil { + Image(systemName: "questionmark.square.dashed") + .resizable() + .frame(width: 16.0, height: 16.0) + .foregroundColor(Color("IconColorRed")) + } else { + Image(systemName: active()! ? "checkmark.circle" : "xmark.circle") + .resizable() + .frame(width: 16.0, height: 16.0) + .foregroundColor(active()! ? Color.primary : Color("IconColorRed")) + } } } } @@ -104,7 +131,8 @@ struct ServicesView_Previews: PreviewProvider { "dnsmasq": true, "mysql": false ]), - servicesToDisplay: ["php", "nginx", "dnsmasq", "mysql"] + servicesToDisplay: ["php", "nginx", "dnsmasq", + "mysql", "redis", "mailhog"] ) .frame(width: 330.0) .previewDisplayName("Dark Mode") diff --git a/phpmon/Domain/SwiftUI/Menu/StatsView.swift b/phpmon/Domain/SwiftUI/Menu/StatsView.swift index 453df73..fdc5301 100644 --- a/phpmon/Domain/SwiftUI/Menu/StatsView.swift +++ b/phpmon/Domain/SwiftUI/Menu/StatsView.swift @@ -19,7 +19,8 @@ struct StatsView: View { maxUploadSize: upload ) ) - view.frame = CGRect(x: 0, y: 0, width: 330, height: 55) + view.autoresizingMask = [.width, .height] + view.setFrameSize(CGSize(width: view.frame.width, height: 55)) item.view = view return item } @@ -31,24 +32,26 @@ struct StatsView: View { var body: some View { HStack(alignment: .firstTextBaseline, spacing: 30) { VStack(alignment: .center, spacing: 3) { - MiniHeaderView(text: "mi_memory_limit".localized.uppercased()) + SectionHeaderView(text: "mi_memory_limit".localized.uppercased()) Text(memoryLimit) .fontWeight(.medium) .font(.system(size: 16)) } VStack(alignment: .center, spacing: 3) { - MiniHeaderView(text: "mi_post_max_size".localized.uppercased()) + SectionHeaderView(text: "mi_post_max_size".localized.uppercased()) Text(maxPostSize) .fontWeight(.medium) .font(.system(size: 16)) } VStack(alignment: .center, spacing: 3) { - MiniHeaderView(text: "mi_upload_max_filesize".localized.uppercased()) + SectionHeaderView(text: "mi_upload_max_filesize".localized.uppercased()) Text(maxUploadSize) .fontWeight(.medium) .font(.system(size: 16)) } - }.padding(10) + } + .padding(10) + .background(Color.debug) } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 5d7841d..277590a 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -23,8 +23,8 @@ "mi_restart_nginx" = "Restart Service: nginx"; "mi_restart_dnsmasq" = "Restart Service: dnsmasq"; "mi_manage_services" = "Manage Services"; -"mi_restart_all_services" = "Restart All Services"; -"mi_stop_all_services" = "Stop All Services"; +"mi_restart_valet_services" = "Restart Valet Services"; +"mi_stop_valet_services" = "Stop Valet Services"; "mi_fix_my_valet" = "Fix My Valet..."; "mi_fix_my_valet_tooltip" = "Something wrong with your Valet installation? Try PHP Monitor’s automatic fixes that’ll get you back up and running in no time!"; @@ -347,7 +347,7 @@ problem manually, using your own Terminal app (this just shows you the output)." "alert.fix_homebrew_permissions_done.title" = "All file and folder permissions for Valet's dependencies have been restored."; "alert.fix_homebrew_permissions_done.subtitle" = "Because of this, all of Valet's services are currently no longer running. You can now interact with Homebrew, but your Valet sites will be unavailable as all services are disabled."; -"alert.fix_homebrew_permissions_done.desc" = "When you are done with Homebrew (after running `brew upgrade`, for example) you should restart PHP Monitor and select \"Restart All Services\" if you want Valet to work again. It is always recommended to restart PHP Monitor whenever you upgrade PHP versions with `brew upgrade`, or things might break."; +"alert.fix_homebrew_permissions_done.desc" = "When you are done with Homebrew (after running `brew upgrade`, for example) you should restart PHP Monitor and select \"Restart Valet Services\" if you want Valet to work again. It is always recommended to restart PHP Monitor whenever you upgrade PHP versions with `brew upgrade`, or things might break."; // PHP FPM Broken "alert.php_fpm_broken.title" = "Your PHP-FPM configuration is not pointing at the Valet socket!";