diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 16dad67..c09bb12 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -672,6 +672,10 @@ C4D4CB3829C109CF00DB9F93 /* InternalSwitcher+Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D4CB3629C109CF00DB9F93 /* InternalSwitcher+Valet.swift */; }; C4D4CB3929C109CF00DB9F93 /* InternalSwitcher+Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D4CB3629C109CF00DB9F93 /* InternalSwitcher+Valet.swift */; }; C4D4CB3A29C109CF00DB9F93 /* InternalSwitcher+Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D4CB3629C109CF00DB9F93 /* InternalSwitcher+Valet.swift */; }; + C4D5576429C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5576329C77CC5001A44CD /* PhpVersionManagerWindowController.swift */; }; + C4D5576529C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5576329C77CC5001A44CD /* PhpVersionManagerWindowController.swift */; }; + C4D5576629C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5576329C77CC5001A44CD /* PhpVersionManagerWindowController.swift */; }; + C4D5576729C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5576329C77CC5001A44CD /* PhpVersionManagerWindowController.swift */; }; C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */; }; C4D5CFCB27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */; }; C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; @@ -1001,6 +1005,7 @@ C4D36614291160A1006BD146 /* WIP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WIP.swift; sourceTree = ""; }; C4D36619291173EA006BD146 /* DictionaryExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DictionaryExtension.swift; sourceTree = ""; }; C4D4CB3629C109CF00DB9F93 /* InternalSwitcher+Valet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InternalSwitcher+Valet.swift"; sourceTree = ""; }; + C4D5576329C77CC5001A44CD /* PhpVersionManagerWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionManagerWindowController.swift; sourceTree = ""; }; C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationFile.swift; sourceTree = ""; }; C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = ""; }; C4D89BC52783C99400A02B68 /* ComposerJson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerJson.swift; sourceTree = ""; }; @@ -1355,6 +1360,7 @@ children = ( C43931C429C4BD610069165B /* PhpFormulaeManager.swift */, C48DDD0C29C75C9E00D032D9 /* BlockingOverlayView.swift */, + C4D5576329C77CC5001A44CD /* PhpVersionManagerWindowController.swift */, ); path = PhpManager; sourceTree = ""; @@ -2240,6 +2246,7 @@ C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */, C4D9F24B280B69E100DCD39A /* AddProxyVC.swift in Sources */, C45B914E295608E300F4EC78 /* ValetServicesManager.swift in Sources */, + C4D5576429C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */, C4E49DED28F764A00026AC4E /* TestableCommand.swift in Sources */, C4A6957628D23EE300A14CF8 /* EnvironmentManager.swift in Sources */, C41E871A2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */, @@ -2438,6 +2445,7 @@ C4E2E86628FC2F1B003B070C /* XCPMApplication.swift in Sources */, C471E85F28F9BB650021E251 /* DomainListVC+Actions.swift in Sources */, C490E3B429BC9FEA006D2DE6 /* ProgressWindowView.swift in Sources */, + C4D5576629C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */, C471E86028F9BB650021E251 /* SelectionVC.swift in Sources */, C471E86128F9BB650021E251 /* AddSiteVC.swift in Sources */, C471E86228F9BB650021E251 /* AddProxyVC.swift in Sources */, @@ -2613,6 +2621,7 @@ C4E2E86A28FC3002003B070C /* Utility.swift in Sources */, C471E8BF28F9BB8F0021E251 /* DomainListWindowController.swift in Sources */, C471E8C028F9BB8F0021E251 /* DomainListVC.swift in Sources */, + C4D5576729C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */, C471E8C128F9BB8F0021E251 /* DomainListVC+ContextMenu.swift in Sources */, C4BF56AE2949381100379603 /* FakeValetInteractor.swift in Sources */, C471E8C228F9BB8F0021E251 /* DomainListVC+Actions.swift in Sources */, @@ -2805,6 +2814,7 @@ C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */, C463E381284930EE00422731 /* PresetHelper.swift in Sources */, C46FA98C2822F08F00D78807 /* PhpConfigurationTest.swift in Sources */, + C4D5576529C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */, C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */, C4E49DEB28F7643D0026AC4E /* CommandProtocol.swift in Sources */, C4F2E4382752F08D0020E974 /* BrewDiagnostics.swift in Sources */, diff --git a/phpmon/Common/PHP/PhpVersionInstaller.swift b/phpmon/Common/PHP/PhpVersionInstaller.swift index 20b7470..84973ad 100644 --- a/phpmon/Common/PHP/PhpVersionInstaller.swift +++ b/phpmon/Common/PHP/PhpVersionInstaller.swift @@ -17,6 +17,7 @@ public enum PhpInstallAction { } public class PhpVersionInstaller { + // TODO: Remove public static var installables = [ // "8.2": "php", "8.1": "php@8.1", diff --git a/phpmon/Domain/App/App.swift b/phpmon/Domain/App/App.swift index 8adc4c0..1803555 100644 --- a/phpmon/Domain/App/App.swift +++ b/phpmon/Domain/App/App.swift @@ -80,6 +80,9 @@ class App { /** The window controller of the warnings window. */ var warningsWindowController: WarningsWindowController? + /** The window controller of the warnings window. */ + var versionManagerWindowController: PhpVersionManagerWindowController? + /** List of detected (installed) applications that PHP Monitor can work with. */ var detectedApplications: [Application] = [] diff --git a/phpmon/Domain/Integrations/Homebrew/Brew.swift b/phpmon/Domain/Integrations/Homebrew/Brew.swift index 565bd86..51d2a68 100644 --- a/phpmon/Domain/Integrations/Homebrew/Brew.swift +++ b/phpmon/Domain/Integrations/Homebrew/Brew.swift @@ -29,6 +29,18 @@ class Brew { } } + public static var phpVersionFormulae = [ + "8.2": "php@8.2", + "8.1": "php@8.1", + "8.0": "php@8.0", + "7.4": "shivammathur/php/php@7.4", + "7.3": "shivammathur/php/php@7.3", + "7.2": "shivammathur/php/php@7.2", + "7.1": "shivammathur/php/php@7.1", + "7.0": "shivammathur/php/php@7.0", + "5.6": "shivammathur/php/php@5.6", + ] + public func getPhpVersions() async -> [BrewFormula] { let command = """ \(Paths.brew) update >/dev/null && \ @@ -38,12 +50,6 @@ class Brew { let rawJsonText = await Shell.pipe(command).out .data(using: .utf8)! - let installed = PhpEnv.shared.cachedPhpInstallations.map { key, value in - return (key, value.versionNumber.text) - } - - let phpAlias = PhpEnv.brewPhpAlias - let outdated = try? JSONDecoder().decode( OutdatedFormulae.self, from: rawJsonText @@ -51,15 +57,24 @@ class Brew { formula.name.starts(with: "php") }) - return installed.map { (version, fullVersion) in - return BrewFormula( - name: version != phpAlias ? "php@\(version)" : "php", - displayName: version, - installedVersion: fullVersion, - upgradeVersion: outdated?.first(where: { formula in - return formula.installed_versions.contains(fullVersion) + return Self.phpVersionFormulae.map { (version, formula) in + let fullVersion = PhpEnv.shared.cachedPhpInstallations[version]?.versionNumber.text + var upgradeVersion: String? = nil + + if let version = fullVersion { + upgradeVersion = outdated?.first(where: { formula in + return formula.installed_versions.contains(version) })?.current_version + } + + return BrewFormula( + name: formula, + displayName: "PHP \(version)", + installedVersion: fullVersion, + upgradeVersion: upgradeVersion ) + }.sorted { a, b in + a.displayName > b.displayName } } } diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 133bf4a..8e2d181 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -190,6 +190,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate DomainListVC.show() } + @objc func openPhpVersionManager() { + PhpVersionManagerWindowController.show() + } + @objc func openDonate() { NSWorkspace.shared.open(Constants.Urls.DonationPage) } diff --git a/phpmon/Domain/Menu/StatusMenu+Items.swift b/phpmon/Domain/Menu/StatusMenu+Items.swift index 90ada4d..b8bc49b 100644 --- a/phpmon/Domain/Menu/StatusMenu+Items.swift +++ b/phpmon/Domain/Menu/StatusMenu+Items.swift @@ -91,33 +91,6 @@ extension StatusMenu { addItem(menuItem) } - // TODO: This is a fixed list... - - addItem(NSMenuItem.separator()) - addItem(HeaderView.asMenuItem(text: "Experimental")) - for result in PhpVersionInstaller.availableActions { - let title = result.action == .install - ? "Install PHP \(result.version)..." - : "Remove PHP \(result.version)..." - - let action: Selector? = result.action == .install - ? #selector(MainMenu.installPhpVersion(sender:)) - : #selector(MainMenu.removePhpVersion(sender:)) - - if result.version == PhpEnv.brewPhpAlias { - continue - } - - let menuItem = PhpMenuItem( - title: title, - action: action, - keyEquivalent: "" - ) - - menuItem.version = result.version - addItem(menuItem) - } - if !PhpEnv.shared.incompatiblePhpVersions.isEmpty { addItem(NSMenuItem.separator()) addItem(NSMenuItem( @@ -164,6 +137,9 @@ extension StatusMenu { func addConfigurationMenuItems() { addItems([ HeaderView.asMenuItem(text: "mi_configuration".localized), + NSMenuItem(title: "mi_php_version_manager".localized, + action: #selector(MainMenu.openPhpVersionManager), + keyEquivalent: "m"), NSMenuItem(title: "mi_php_config".localized, action: #selector(MainMenu.openActiveConfigFolder), keyEquivalent: "c"), diff --git a/phpmon/Domain/SwiftUI/PhpManager/PhpFormulaeManager.swift b/phpmon/Domain/SwiftUI/PhpManager/PhpFormulaeManager.swift index 9f9bf5c..872f25d 100644 --- a/phpmon/Domain/SwiftUI/PhpManager/PhpFormulaeManager.swift +++ b/phpmon/Domain/SwiftUI/PhpManager/PhpFormulaeManager.swift @@ -1,5 +1,5 @@ // -// PhpFormulaeManager.swift +// PhpFormulaeView.swift // PHP Monitor // // Created by Nico Verbruggen on 17/03/2023. @@ -9,67 +9,81 @@ import Foundation import SwiftUI -struct PhpFormulaeManager: View { +struct PhpFormulaeView: View { @State var formulae: [BrewFormula] @State var busy: Bool = true @State var title: String = "Doing a thing" @State var description: String = "Preparing..." + init(formulae: [BrewFormula], busy: Bool = true, title: String = "", description: String = "") { + self.formulae = formulae + self.busy = busy + self.title = title + self.description = description + + Task { @MainActor in + let items = await Brew.shared.getPhpVersions() + print(items) + } + } + var body: some View { BlockingOverlayView(busy: busy, title: title, text: description) { - List(Array(formulae.enumerated()), id: \.1.name) { (index, formula) in - HStack { - Image(systemName: formula.icon) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 16, height: 16) - .foregroundColor(formula.iconColor) - .padding(.horizontal, 5) - VStack(alignment: .leading) { - Text(formula.displayName).bold() + VStack { + List(Array(formulae.enumerated()), id: \.1.name) { (index, formula) in + HStack { + Image(systemName: formula.icon) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 16, height: 16) + .foregroundColor(formula.iconColor) + .padding(.horizontal, 5) + VStack(alignment: .leading) { + Text(formula.displayName).bold() - if formula.isInstalled && formula.hasUpgrade { - Text("\(formula.installedVersion!) installed, \(formula.upgradeVersion!) available.") - .font(.system(size: 11)) - .foregroundColor(.gray) - } else if formula.isInstalled && formula.installedVersion != nil { - Text("Latest version is currently installed.").font(.system(size: 11)) - .foregroundColor(.gray) + if formula.isInstalled && formula.hasUpgrade { + Text("\(formula.installedVersion!) installed, \(formula.upgradeVersion!) available.") + .font(.system(size: 11)) + .foregroundColor(.gray) + } else if formula.isInstalled && formula.installedVersion != nil { + Text("Latest version is currently installed.").font(.system(size: 11)) + .foregroundColor(.gray) + } else { + Text("This version can be installed.") + .font(.system(size: 11)) + .foregroundColor(.gray) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + if formula.isInstalled { + Button("Uninstall") { + // handle uninstall action here + } } else { - Text("This version can be installed.") - .font(.system(size: 11)) - .foregroundColor(.gray) - } - } - .frame(maxWidth: .infinity, alignment: .leading) - if formula.isInstalled { - Button("Uninstall") { - // handle uninstall action here - } - } else { - Button("Install") { - // handle install action here - } - } - if formula.hasUpgrade { - Button("Update") { - // handle uninstall action here + Button("Install") { + // handle install action here + } + } + if formula.hasUpgrade { + Button("Update") { + // handle uninstall action here + } } } + .listRowBackground(index % 2 == 0 + ? Color.gray.opacity(0) + : Color.gray.opacity(0.08) + ) + .padding(.vertical, 10) } - .listRowBackground(index % 2 == 0 - ? Color.gray.opacity(0) - : Color.gray.opacity(0.08) - ) - .padding(.vertical, 10) } }.frame(width: 500, height: 500) } } -struct PhpFormulaeManager_Previews: PreviewProvider { +struct PhpFormulaeView_Previews: PreviewProvider { static var previews: some View { - PhpFormulaeManager(formulae: [ + PhpFormulaeView(formulae: [ BrewFormula( name: "php", displayName: "PHP 8.2", diff --git a/phpmon/Domain/SwiftUI/PhpManager/PhpVersionManagerWindowController.swift b/phpmon/Domain/SwiftUI/PhpManager/PhpVersionManagerWindowController.swift new file mode 100644 index 0000000..a53bec3 --- /dev/null +++ b/phpmon/Domain/SwiftUI/PhpManager/PhpVersionManagerWindowController.swift @@ -0,0 +1,54 @@ +// +// PhpVersionManagerWindowController.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 19/03/2023. +// Copyright © 2023 Nico Verbruggen. All rights reserved. +// + +import Foundation +import Cocoa +import SwiftUI + +class PhpVersionManagerWindowController: PMWindowController { + + // MARK: - Window Identifier + + var view: PhpFormulaeView! + + override var windowName: String { + return "PhpFormulaeView" + } + + public static func create(delegate: NSWindowDelegate?) { + let windowController = Self() + windowController.window = NSWindow() + windowController.view = PhpFormulaeView( + formulae: [], + busy: true, + title: "Loading PHP versions", + description: "Loading available PHP versions..." + ) + + guard let window = windowController.window else { return } + window.title = "" + window.styleMask = [.titled, .closable, .miniaturizable] + window.titlebarAppearsTransparent = true + window.delegate = delegate ?? windowController + window.contentView = NSHostingView(rootView: windowController.view) + window.setContentSize(NSSize(width: 600, height: 480)) + + App.shared.versionManagerWindowController = windowController + } + + public static func show(delegate: NSWindowDelegate? = nil) { + if App.shared.versionManagerWindowController == nil { + Self.create(delegate: delegate) + } + + App.shared.versionManagerWindowController?.showWindow(self) + App.shared.versionManagerWindowController?.window?.setCenterPosition(offsetY: 70) + + NSApp.activate(ignoringOtherApps: true) + } +} diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 1a64351..d5256fe 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -20,6 +20,7 @@ "mi_no_php_linked" = "No PHP version linked!"; "mi_fix_php_link" = "Fix Automatically..."; "mi_no_php_linked_explain" = "What's This?"; +"mi_php_version_manager" = "Install & Upgrade PHP Versions..."; "mi_diagnostics" = "Diagnostics"; "mi_active_services" = "Active Services";