diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index c14344b..e040153 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -48,6 +48,8 @@ C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; }; C40B24F227A310770018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; }; C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; }; + C40C5C9C2846A40600E28255 /* Preset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C5C9B2846A40600E28255 /* Preset.swift */; }; + C40C5C9D2846A40600E28255 /* Preset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C5C9B2846A40600E28255 /* Preset.swift */; }; C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; }; C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; }; C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; }; @@ -307,6 +309,7 @@ C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarIcons.swift; sourceTree = ""; }; C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetterAlert.swift; sourceTree = ""; }; C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetterAlertVC.swift; sourceTree = ""; }; + C40C5C9B2846A40600E28255 /* Preset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preset.swift; sourceTree = ""; }; C40C7F1D2772136000DDDCDC /* PhpEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpEnv.swift; sourceTree = ""; }; C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = ""; }; C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; @@ -554,6 +557,14 @@ path = Notice; sourceTree = ""; }; + C40C5C9E2846A42D00E28255 /* Presets */ = { + isa = PBXGroup; + children = ( + C40C5C9B2846A40600E28255 /* Preset.swift */, + ); + path = Presets; + sourceTree = ""; + }; C40C7F1C27720E1400DDDCDC /* Test Files */ = { isa = PBXGroup; children = ( @@ -643,6 +654,7 @@ 5420395726135DB800FB00FA /* Preferences */, C44C198F276E3A380072762D /* Progress */, C4C8E81D276F5686003AC782 /* Watcher */, + C40C5C9E2846A42D00E28255 /* Presets */, C4EE55B027708BB2001DF387 /* SwiftUI */, ); path = Domain; @@ -1212,6 +1224,7 @@ 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 */, C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */, C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */, @@ -1353,6 +1366,7 @@ C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */, C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */, C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */, + C40C5C9D2846A40600E28255 /* Preset.swift in Sources */, C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */, C42800AB28452AA50099C999 /* StatusMenu+Items.swift in Sources */, C40FE738282ABA4F00A302C2 /* AppVersion.swift in Sources */, diff --git a/phpmon/Domain/Menu/MainMenu+Actions.swift b/phpmon/Domain/Menu/MainMenu+Actions.swift index a66c86a..7ee6502 100644 --- a/phpmon/Domain/Menu/MainMenu+Actions.swift +++ b/phpmon/Domain/Menu/MainMenu+Actions.swift @@ -201,10 +201,48 @@ extension MainMenu { PhpEnv.switcher.performSwitch( to: version, completion: { + PhpEnv.shared.currentInstall = ActivePhpInstallation() + App.shared.handlePhpConfigWatcher() PhpEnv.shared.delegate?.switcherDidCompleteSwitch(to: version) } ) } } + // MARK: - Async + + /** + This async-friendly version of the switcher can be invoked elsewhere in the app: + ``` + Task { + await MainMenu.shared.switchToPhp("8.1") + // thing to do after the switch + } + ``` + Since this async function uses `withCheckedContinuation` + any code after will run only after the switcher is done. + */ + func switchToPhp(_ version: String) async { + DispatchQueue.main.async { [self] in + setBusyImage() + PhpEnv.shared.isBusy = true + PhpEnv.shared.delegate = self + PhpEnv.shared.delegate?.switcherDidStartSwitching(to: version) + } + + return await withCheckedContinuation({ continuation in + updatePhpVersionInStatusBar() + rebuild() + PhpEnv.switcher.performSwitch( + to: version, + completion: { + PhpEnv.shared.currentInstall = ActivePhpInstallation() + App.shared.handlePhpConfigWatcher() + PhpEnv.shared.delegate?.switcherDidCompleteSwitch(to: version) + continuation.resume() + } + ) + }) + } + } diff --git a/phpmon/Domain/Menu/MainMenu+Switcher.swift b/phpmon/Domain/Menu/MainMenu+Switcher.swift index 8134a8e..3100d20 100644 --- a/phpmon/Domain/Menu/MainMenu+Switcher.swift +++ b/phpmon/Domain/Menu/MainMenu+Switcher.swift @@ -15,12 +15,6 @@ extension MainMenu { func switcherDidStartSwitching(to version: String) {} func switcherDidCompleteSwitch(to version: String) { - // Update the PHP version - PhpEnv.shared.currentInstall = ActivePhpInstallation() - - // Ensure the config watcher gets reloaded - App.shared.handlePhpConfigWatcher() - // Mark as no longer busy PhpEnv.shared.isBusy = false @@ -56,7 +50,7 @@ extension MainMenu { } } - @MainActor private func suggestFixMyValet(failed version: String) { + private func suggestFixMyValet(failed version: String) { let outcome = BetterAlert() .withInformation( title: "alert.php_switch_failed.title".localized(version), diff --git a/phpmon/Domain/Preferences/CustomPrefs.swift b/phpmon/Domain/Preferences/CustomPrefs.swift index 0fe0c03..03d6db6 100644 --- a/phpmon/Domain/Preferences/CustomPrefs.swift +++ b/phpmon/Domain/Preferences/CustomPrefs.swift @@ -17,79 +17,3 @@ struct CustomPrefs: Decodable { case presets = "presets" } } - -struct Preset: Decodable { - let name: String - let version: String? - let extensions: [String: Bool] - let configuration: [String: String?] - - public enum CodingKeys: String, CodingKey { - case version = "php", - name = "name", - extensions = "extensions", - configuration = "configuration" - } - - public func getMenuItemText() -> String { - var info = extensions.count == 1 - ? "preset.extension".localized(extensions.count) - : "preset.extensions".localized(extensions.count) - info += ", " - info += configuration.count == 1 - ? "preset.preference".localized(configuration.count) - : "preset.preferences".localized(configuration.count) - - if self.version == nil || !PhpEnv.shared.availablePhpVersions.contains(self.version!) { - return "" - + "\(name.stripped)
" - + "" - + info + "" - + "
" - } - - return "" - + "\(name.stripped)
" - + "" - + "Switches to PHP \(version!)
" - + info + "
" - + "
" - } - - public func apply() { - // Apply the PHP version if is considered a valid version - // TODO - - // Apply the configuration changes first - for conf in configuration { - applyConfigurationValue(key: conf.key, value: conf.value ?? "") - } - - // Apply the extension changes in-place afterward - for ext in extensions { - for foundExt in PhpEnv.phpInstall.extensions - where foundExt.name == ext.key && foundExt.enabled != ext.value { - Log.info("Toggling extension \(foundExt.name) in \(foundExt.file)") - foundExt.toggle() - break - } - } - - Actions.restartPhpFpm() - } - - private func applyConfigurationValue(key: String, value: String) { - guard let file = PhpEnv.shared.getConfigFile(forKey: key) else { - return - } - - do { - if file.has(key: key) { - Log.info("Setting config value \(key) in \(file.filePath)") - try file.replace(key: key, value: value) - } - } catch { - Log.err("Setting \(key) to \(value) failed.") - } - } -} diff --git a/phpmon/Domain/Presets/Preset.swift b/phpmon/Domain/Presets/Preset.swift new file mode 100644 index 0000000..62f71b0 --- /dev/null +++ b/phpmon/Domain/Presets/Preset.swift @@ -0,0 +1,116 @@ +// +// Preset.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 31/05/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +struct Preset: Decodable { + let name: String + let version: String? + let extensions: [String: Bool] + let configuration: [String: String?] + + public enum CodingKeys: String, CodingKey { + case version = "php", + name = "name", + extensions = "extensions", + configuration = "configuration" + } + + public func getMenuItemText() -> String { + var info = extensions.count == 1 + ? "preset.extension".localized(extensions.count) + : "preset.extensions".localized(extensions.count) + info += ", " + info += configuration.count == 1 + ? "preset.preference".localized(configuration.count) + : "preset.preferences".localized(configuration.count) + + if self.version == nil { + return "" + + "\(name.stripped)
" + + "" + + info + "" + + "
" + } + + return "" + + "\(name.stripped)
" + + "" + + "Switches to PHP \(version!)
" + + info + "
" + + "
" + } + + public func apply() { + Task { + // Apply the PHP version if is considered a valid version + if self.version != nil { + await switchToPhpVersionIfValid() + } + + // Apply the configuration changes first + for conf in configuration { + applyConfigurationValue(key: conf.key, value: conf.value ?? "") + } + + // Apply the extension changes in-place afterward + for ext in extensions { + for foundExt in PhpEnv.phpInstall.extensions + where foundExt.name == ext.key && foundExt.enabled != ext.value { + Log.info("Toggling extension \(foundExt.name) in \(foundExt.file)") + foundExt.toggle() + break + } + } + + Actions.restartPhpFpm() + } + } + + private func switchToPhpVersionIfValid() async { + if PhpEnv.shared.currentInstall.version.short == self.version! { + Log.info("The version we are supposed to switch to is already active.") + return + } + + if PhpEnv.shared.availablePhpVersions.first(where: { $0 == self.version }) != nil { + await MainMenu.shared.switchToPhp(self.version!) + return + } else { + DispatchQueue.main.async { + BetterAlert() + .withInformation( + title: "PHP version unavailable", + subtitle: "You have specified a PHP version (\(version!)) that is unavailable.", + description: "Please make sure this version of PHP is installed " + + "and you can switch to it in the dropdown. " + + "Currently supported versions include: " + + "\(PhpEnv.shared.availablePhpVersions.joined(separator: ", "))." + ) + .withPrimary(text: "OK") + .show() + } + return + } + } + + private func applyConfigurationValue(key: String, value: String) { + guard let file = PhpEnv.shared.getConfigFile(forKey: key) else { + return + } + + do { + if file.has(key: key) { + Log.info("Setting config value \(key) in \(file.filePath)") + try file.replace(key: key, value: value) + } + } catch { + Log.err("Setting \(key) to \(value) failed.") + } + } +}