1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2026-03-28 23:00:07 +01:00

♻️ Rework interapp handling, ensure preferences UI observes changes

This commit is contained in:
2026-02-06 19:10:38 +01:00
parent 228d85a861
commit 54d1ef10c5
9 changed files with 70 additions and 10 deletions

View File

@@ -11,5 +11,6 @@ import Foundation
class Events { class Events {
static let ServicesUpdated = Notification.Name("ServicesUpdated") static let ServicesUpdated = Notification.Name("ServicesUpdated")
static let PreferencesUpdated = Notification.Name("PreferencesUpdated")
} }

View File

@@ -105,6 +105,9 @@ class App {
/** The window controller of the PHP extension manager window. */ /** The window controller of the PHP extension manager window. */
var phpExtensionManagerWindowController: PhpExtensionManagerWindowController? var phpExtensionManagerWindowController: PhpExtensionManagerWindowController?
/** URL that was received before the app finished booting. Will be processed once the startup procedure completes. */
var deferredURL: URL?
/** List of detected (installed) applications that PHP Monitor can work with. */ /** List of detected (installed) applications that PHP Monitor can work with. */
var detectedApplications: [Application] = [] var detectedApplications: [Application] = []

View File

@@ -17,17 +17,40 @@ extension AppDelegate {
application URL. You can use the `phpmon://` protocol to communicate with the app. application URL. You can use the `phpmon://` protocol to communicate with the app.
At this time you can trigger the site list using Alfred (or some other application) At this time you can trigger the site list using Alfred (or some other application)
by opening the following URL: `phpmon://list`. by opening the following URL: `phpmon://list`. Various other commands are also made
available via the `InterApp` class, which is where all commands and their different
interactions are declared.
Please note that PHP Monitor needs to be running in the background for this to work. Please note that PHP Monitor needs to be running in the background for this to work,
or the app will launch and the command will be deferred until the app is ready.
*/ */
@MainActor func application(_ application: NSApplication, open urls: [URL]) { @MainActor func application(_ application: NSApplication, open urls: [URL]) {
guard Startup.hasFinishedBooting else {
return deferURLs(urls)
}
handleURLs(urls)
}
@MainActor func deferURLs(_ urls: [URL]) {
Log.info("App has not finished booting, deferring phpmon:// command...")
App.shared.deferredURL = urls.first
}
@MainActor func handleURLs(_ urls: [URL]) {
if !Preferences.isEnabled(.allowProtocolForIntegrations) { if !Preferences.isEnabled(.allowProtocolForIntegrations) {
if UserDefaults.standard.bool(forKey: PersistentAppState.didPromptForIntegrations.rawValue) {
// First, we'll check if we already prompted for integrations.
// If we have, we will not respond to the commands at all.
if UserDefaults.standard.bool(
forKey: PersistentAppState.didPromptForIntegrations.rawValue
) {
Log.info("Acting on commands via phpmon:// has been disabled.") Log.info("Acting on commands via phpmon:// has been disabled.")
return return
} }
// However, if that isn't the case, we will prompt the user about integrations,
// which will give the user a chance to approve the command regardless.
Log.info("Acting on commands via phpmon:// has been disabled. Prompting user...") Log.info("Acting on commands via phpmon:// has been disabled. Prompting user...")
if !promptToEnableIntegrations() { if !promptToEnableIntegrations() {
return return
@@ -58,7 +81,7 @@ extension AppDelegate {
return false return false
} }
Preferences.update(.allowProtocolForIntegrations, value: true) Preferences.update(.allowProtocolForIntegrations, value: true, notify: true)
return true return true
} }

View File

@@ -124,6 +124,12 @@ extension Startup {
Startup.hasFinishedBooting = true Startup.hasFinishedBooting = true
Log.info("PHP Monitor is ready to serve!") Log.info("PHP Monitor is ready to serve!")
// Process the last URL that arrived during startup
if let url = App.shared.deferredURL {
AppDelegate.instance.handleURLs([url])
App.shared.deferredURL = nil
}
// Enable the main menu item // Enable the main menu item
MainMenu.shared.statusItem.button?.isEnabled = true MainMenu.shared.statusItem.button?.isEnabled = true

View File

@@ -24,7 +24,7 @@ extension Valet {
) )
.withPrimary(text: "generic.ok".localized) .withPrimary(text: "generic.ok".localized)
.withTertiary(text: "alert.do_not_tell_again".localized, action: { alert in .withTertiary(text: "alert.do_not_tell_again".localized, action: { alert in
Preferences.update(.warnAboutNonStandardTLD, value: false) Preferences.update(.warnAboutNonStandardTLD, value: false, notify: true)
alert.close(with: .alertThirdButtonReturn) alert.close(with: .alertThirdButtonReturn)
}) })
.show(urgency: .urgentRequestAttention) .show(urgency: .urgentRequestAttention)

View File

@@ -168,7 +168,7 @@ class Preferences {
}) })
} }
static func update(_ preference: PreferenceName, value: Any?) { static func update(_ preference: PreferenceName, value: Any?, notify: Bool = false) {
if value == nil { if value == nil {
UserDefaults.standard.removeObject(forKey: preference.rawValue) UserDefaults.standard.removeObject(forKey: preference.rawValue)
} else { } else {
@@ -178,5 +178,9 @@ class Preferences {
// Update the preferences cache in memory! // Update the preferences cache in memory!
App.shared.container.preferences.cachedPreferences = Preferences.cache() App.shared.container.preferences.cachedPreferences = Preferences.cache()
if notify {
NotificationCenter.default.post(name: Events.PreferencesUpdated, object: nil)
}
} }
} }

View File

@@ -65,6 +65,15 @@ class CheckboxPreferenceBehavior: CheckboxPreferenceViewBehavior {
self.preference = preference self.preference = preference
self.button = button self.button = button
self.button.state = Preferences.isEnabled(self.preference) ? .on : .off self.button.state = Preferences.isEnabled(self.preference) ? .on : .off
NotificationCenter.default.addObserver(
self, selector: #selector(refreshState),
name: Events.PreferencesUpdated, object: nil
)
}
@objc func refreshState() {
self.button.state = Preferences.isEnabled(self.preference) ? .on : .off
} }
public func toggled(checked: Bool) { public func toggled(checked: Bool) {

View File

@@ -73,9 +73,23 @@ class SelectPreferenceView: NSView, XibLoadable {
view.preference = preference view.preference = preference
view.action = action view.action = action
NotificationCenter.default.addObserver(
view, selector: #selector(view.refreshState),
name: Events.PreferencesUpdated, object: nil
)
return view return view
} }
@objc func refreshState() {
let value = Preferences.preferences[preference] as! String
self.options.enumerated().forEach { option in
if option.element.value == value {
self.popupButton.selectItem(at: option.offset)
}
}
}
@IBAction func valueChanged(_ sender: Any) { @IBAction func valueChanged(_ sender: Any) {
let index = self.popupButton.indexOfSelectedItem let index = self.popupButton.indexOfSelectedItem
Preferences.update(self.preference, value: self.options[index].value) Preferences.update(self.preference, value: self.options[index].value)

View File

@@ -954,8 +954,8 @@ PHP Monitor will tell Valet to unsecure and re-secure all expired domains for yo
// THIRD-PARTY INTEGRATIONS // THIRD-PARTY INTEGRATIONS
"alert.enable_integrations.title" = "An external application is trying to communicate with PHP Monitor"; "alert.enable_integrations.title" = "An external application is trying to communicate with PHP Monitor. Do you want third-party applications to be able to communicate with PHP Monitor?";
"alert.enable_integrations.subtitle" = "Do you want to allow third-party integrations (like Alfred or Raycast) to control PHP Monitor via the `phpmon://` protocol?\n\nThis notice appears because PHP Monitor just received an external command. If you triggered this intentionally, using a third-party app like Alfred or Raycast, it is safe to allow this.\n\nYou can change this setting later in Preferences."; "alert.enable_integrations.subtitle" = "This notice appears because PHP Monitor just received an external command, and this feature is disabled by default. If you triggered this intentionally, using a third-party app like Alfred or Raycast, it is normally safe to allow this.\n\nYou can change this setting later in Preferences, you will be asked this question only once.";
"alert.enable_integrations.desc" = "If you did not trigger this via Alfred or Raycast, there may be another application trying to control PHP Monitor. In that case, I recommend keeping this integration turned off, unless you are fine with another third party app controlling PHP Monitor for you, which could present a security risk."; "alert.enable_integrations.desc" = "If you did not trigger this via Alfred or Raycast, there may be another application trying to control PHP Monitor.\n\nIn such a case, I recommend keeping this integration turned off, unless you are fine with another third party app controlling PHP Monitor for you, which could present a potential security risk.";
"alert.enable_integrations.ok" = "Allow"; "alert.enable_integrations.ok" = "Allow Integrations";
"alert.enable_integrations.cancel" = "Don't Allow"; "alert.enable_integrations.cancel" = "Don't Allow";