diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index f38c0fb..eff0c4f 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 5420395926135DC100FB00FA /* GeneralPreferencesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* GeneralPreferencesVC.swift */; }; + 5420395926135DC100FB00FA /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; }; 5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; }; 5489625828312FAD004F647A /* CreatedFromFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5489625728312FAD004F647A /* CreatedFromFile.swift */; }; 5489625928313231004F647A /* CreatedFromFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5489625728312FAD004F647A /* CreatedFromFile.swift */; }; @@ -143,7 +143,7 @@ C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; }; C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; }; C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; }; - C481F79726164A78004FBCFF /* GeneralPreferencesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* GeneralPreferencesVC.swift */; }; + C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; }; C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; }; C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; }; C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; }; @@ -288,7 +288,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 5420395826135DC100FB00FA /* GeneralPreferencesVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPreferencesVC.swift; sourceTree = ""; }; + 5420395826135DC100FB00FA /* PrefsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsVC.swift; sourceTree = ""; }; 5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; 5489625728312FAD004F647A /* CreatedFromFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatedFromFile.swift; sourceTree = ""; }; 54A18D3F282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-secure-proxy-custom-tld.test"; sourceTree = ""; }; @@ -474,7 +474,7 @@ isa = PBXGroup; children = ( C4998F092617633900B2526E /* PrefsWC.swift */, - 5420395826135DC100FB00FA /* GeneralPreferencesVC.swift */, + 5420395826135DC100FB00FA /* PrefsVC.swift */, 5420395E2613607600FB00FA /* Preferences.swift */, C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */, C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */, @@ -1202,7 +1202,7 @@ C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */, C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */, C4C0E8EA27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */, - 5420395926135DC100FB00FA /* GeneralPreferencesVC.swift in Sources */, + 5420395926135DC100FB00FA /* PrefsVC.swift in Sources */, C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, 5489625828312FAD004F647A /* CreatedFromFile.swift in Sources */, C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */, @@ -1368,7 +1368,7 @@ C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */, C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */, - C481F79726164A78004FBCFF /* GeneralPreferencesVC.swift in Sources */, + C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */, C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */, C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */, C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */, diff --git a/phpmon/Common/Helpers/LocalNotification.swift b/phpmon/Common/Helpers/LocalNotification.swift index 51ca179..91ffa4b 100644 --- a/phpmon/Common/Helpers/LocalNotification.swift +++ b/phpmon/Common/Helpers/LocalNotification.swift @@ -10,7 +10,11 @@ import UserNotifications class LocalNotification { - public static func send(title: String, subtitle: String) { + public static func send(title: String, subtitle: String, preference: PreferenceName) { + if !Preferences.isEnabled(preference) { + return + } + let content = UNMutableNotificationContent() content.title = title content.body = subtitle diff --git a/phpmon/Domain/App/Base.lproj/Main.storyboard b/phpmon/Domain/App/Base.lproj/Main.storyboard index fd246f2..7994d8d 100644 --- a/phpmon/Domain/App/Base.lproj/Main.storyboard +++ b/phpmon/Domain/App/Base.lproj/Main.storyboard @@ -378,7 +378,7 @@ - + diff --git a/phpmon/Domain/DomainList/DomainListVC+Actions.swift b/phpmon/Domain/DomainList/DomainListVC+Actions.swift index 61b01f6..e2d2628 100644 --- a/phpmon/Domain/DomainList/DomainListVC+Actions.swift +++ b/phpmon/Domain/DomainList/DomainListVC+Actions.swift @@ -69,7 +69,8 @@ extension DomainListVC { .localized( "\(selectedSite.name).\(Valet.shared.config.tld)", newState - ) + ), + preference: .notifyAboutSecureToggle ) } diff --git a/phpmon/Domain/Integrations/Composer/ComposerWindow.swift b/phpmon/Domain/Integrations/Composer/ComposerWindow.swift index 560ff57..f9413e0 100644 --- a/phpmon/Domain/Integrations/Composer/ComposerWindow.swift +++ b/phpmon/Domain/Integrations/Composer/ComposerWindow.swift @@ -83,7 +83,8 @@ class ComposerWindow { if shouldNotify { LocalNotification.send( title: "alert.composer_success.title".localized, - subtitle: "alert.composer_success.info".localized + subtitle: "alert.composer_success.info".localized, + preference: .notifyAboutGlobalComposerStatus ) } window = nil diff --git a/phpmon/Domain/Menu/MainMenu+Actions.swift b/phpmon/Domain/Menu/MainMenu+Actions.swift index 071cd57..625f03d 100644 --- a/phpmon/Domain/Menu/MainMenu+Actions.swift +++ b/phpmon/Domain/Menu/MainMenu+Actions.swift @@ -56,7 +56,8 @@ extension MainMenu { DispatchQueue.main.async { LocalNotification.send( title: "notification.services_restarted".localized, - subtitle: "notification.services_restarted_desc".localized + subtitle: "notification.services_restarted_desc".localized, + preference: .notifyAboutServices ) } } @@ -69,7 +70,8 @@ extension MainMenu { DispatchQueue.main.async { LocalNotification.send( title: "notification.services_stopped".localized, - subtitle: "notification.services_stopped_desc".localized + subtitle: "notification.services_stopped_desc".localized, + preference: .notifyAboutServices ) } } @@ -165,8 +167,8 @@ extension MainMenu { ) ) .withPrimary(text: "alert.revert_description.ok".localized, action: { alert in - self.performRollback() alert.close(with: .OK) + self.performRollback() }) .withSecondary(text: "alert.revert_description.cancel".localized) .show() diff --git a/phpmon/Domain/Menu/MainMenu+Switcher.swift b/phpmon/Domain/Menu/MainMenu+Switcher.swift index 3100d20..57feaee 100644 --- a/phpmon/Domain/Menu/MainMenu+Switcher.swift +++ b/phpmon/Domain/Menu/MainMenu+Switcher.swift @@ -77,7 +77,8 @@ extension MainMenu { private func notifyAboutVersionChange(to version: String) { LocalNotification.send( title: String(format: "notification.version_changed_title".localized, version), - subtitle: String(format: "notification.version_changed_desc".localized, version) + subtitle: String(format: "notification.version_changed_desc".localized, version), + preference: .notifyAboutVersionChange ) PhpEnv.phpInstall.notifyAboutBrokenPhpFpm() diff --git a/phpmon/Domain/Preferences/GeneralPreferencesVC.swift b/phpmon/Domain/Preferences/GeneralPreferencesVC.swift deleted file mode 100644 index e181da9..0000000 --- a/phpmon/Domain/Preferences/GeneralPreferencesVC.swift +++ /dev/null @@ -1,156 +0,0 @@ -// -// GeneralPreferencesVC.swift -// PHP Monitor -// -// Created by Nico Verbruggen on 30/03/2021. -// Copyright © 2022 Nico Verbruggen. All rights reserved. -// - -import Cocoa -import Carbon - -class GenericPreferenceVC: NSViewController { - - // MARK: - Content - - @IBOutlet weak var stackView: NSStackView! - - var views: [NSView] = [] - - override func viewDidLoad() { - super.viewDidLoad() - self.views.forEach({ self.stackView.addArrangedSubview($0) }) - } - - // MARK: - Deinitialization - - deinit { - Log.perf("PrefsVC deallocated") - } - -} - -class GeneralPreferencesVC: GenericPreferenceVC { - - // MARK: - Icon and title - - var icon: String = "gear" - - // MARK: - Lifecycle - - public static func fromStoryboard() -> GenericPreferenceVC { - let vc = NSStoryboard(name: "Main", bundle: nil) - .instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC - - vc.views = [ - getDynamicIconPreferenceView(), - getIconOptionsPreferenceView(), - getIconDensityPreferenceView(), - getAutoRestartPreferenceView(), - getAutomaticComposerUpdatePreferenceView(), - // getShortcutPreferenceView(), - getIntegrationsPreferenceView(), - getAutomaticUpdateCheckPreferenceView() - ] - - return vc - } - - private static func getDynamicIconPreferenceView() -> NSView { - return CheckboxPreferenceView.make( - sectionText: "prefs.dynamic_icon".localized, - descriptionText: "prefs.dynamic_icon_desc".localized, - checkboxText: "prefs.dynamic_icon_title".localized, - preference: .shouldDisplayDynamicIcon, - action: { - MainMenu.shared.refreshIcon() - } - ) - } - - private static func getIconOptionsPreferenceView() -> NSView { - return SelectPreferenceView.make( - sectionText: "", - descriptionText: "prefs.icon_options_desc".localized, - options: MenuBarIcon.allCases.map({ return $0.rawValue }), - localizationPrefix: "prefs.icon_options", - preference: .iconTypeToDisplay, - action: { - MainMenu.shared.refreshIcon() - } - ) - } - - private static func getIconDensityPreferenceView() -> NSView { - return CheckboxPreferenceView.make( - sectionText: "prefs.info_density".localized, - descriptionText: "prefs.display_full_php_version_desc".localized, - checkboxText: "prefs.display_full_php_version".localized, - preference: .fullPhpVersionDynamicIcon, - action: { - MainMenu.shared.refreshIcon() - MainMenu.shared.rebuild() - } - ) - } - - private static func getAutoRestartPreferenceView() -> NSView { - return CheckboxPreferenceView.make( - sectionText: "prefs.services".localized, - descriptionText: "prefs.auto_restart_services_desc".localized, - checkboxText: "prefs.auto_restart_services_title".localized, - preference: .autoServiceRestartAfterExtensionToggle, - action: {} - ) - } - - private static func getAutomaticComposerUpdatePreferenceView() -> NSView { - CheckboxPreferenceView.make( - sectionText: "prefs.switcher".localized, - descriptionText: "prefs.auto_composer_update_desc".localized, - checkboxText: "prefs.auto_composer_update_title".localized, - preference: .autoComposerGlobalUpdateAfterSwitch, - action: {} - ) - } - - /* - private static func getShortcutPreferenceView() -> NSView { - return HotkeyPreferenceView.make( - sectionText: "prefs.global_shortcut".localized, - descriptionText: "prefs.shortcut_desc".localized, - self - ) - } - */ - - private static func getIntegrationsPreferenceView() -> NSView { - return CheckboxPreferenceView.make( - sectionText: "prefs.integrations".localized, - descriptionText: "prefs.open_protocol_desc".localized, - checkboxText: "prefs.open_protocol_title".localized, - preference: .allowProtocolForIntegrations, - action: {} - ) - } - - private static func getAutomaticUpdateCheckPreferenceView() -> NSView { - return CheckboxPreferenceView.make( - sectionText: "prefs.updates".localized, - descriptionText: "prefs.automatic_update_check_desc".localized, - checkboxText: "prefs.automatic_update_check_title".localized, - preference: .automaticBackgroundUpdateCheck, - action: {} - ) - } - - // MARK: - Listening for hotkey delegate - - var listeningForHotkeyView: HotkeyPreferenceView? - - override func viewWillDisappear() { - if listeningForHotkeyView !== nil { - listeningForHotkeyView = nil - } - } -} diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index e2b009d..ee7bc44 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -12,15 +12,28 @@ import Foundation These are the keys used for every preference in the app. */ enum PreferenceName: String { + // FIRST-TIME LAUNCH case wasLaunchedBefore = "launched_before" - case shouldDisplayDynamicIcon = "use_dynamic_icon" - case iconTypeToDisplay = "icon_type_to_display" - case fullPhpVersionDynamicIcon = "full_php_in_menu_bar" + + // GENERAL case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle" case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch" case allowProtocolForIntegrations = "allow_protocol_for_integrations" case globalHotkey = "global_hotkey" case automaticBackgroundUpdateCheck = "backgroundUpdateCheck" + + // APPEARANCE + case shouldDisplayDynamicIcon = "use_dynamic_icon" + case iconTypeToDisplay = "icon_type_to_display" + case fullPhpVersionDynamicIcon = "full_php_in_menu_bar" + + // NOTIFICATIONS + case notifyAboutVersionChange = "notify_about_version_change" + case notifyAboutPhpFpmRestart = "notify_about_php_fpm_restart" + case notifyAboutServices = "notify_about_services_restart" + case notifyAboutPresets = "notify_about_presets" + case notifyAboutSecureToggle = "notify_about_secure_toggle" + case notifyAboutGlobalComposerStatus = "notify_about_composer_status" } /** @@ -70,14 +83,25 @@ class Preferences { */ static func handleFirstTimeLaunch() { UserDefaults.standard.register(defaults: [ - /// Preferences - PreferenceName.shouldDisplayDynamicIcon.rawValue: true, - PreferenceName.iconTypeToDisplay.rawValue: MenuBarIcon.iconPhp.rawValue, - PreferenceName.fullPhpVersionDynamicIcon.rawValue: false, + /// Preferences: General PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true, PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false, PreferenceName.allowProtocolForIntegrations.rawValue: true, PreferenceName.automaticBackgroundUpdateCheck.rawValue: true, + + /// Preferences: Appearance + PreferenceName.shouldDisplayDynamicIcon.rawValue: true, + PreferenceName.iconTypeToDisplay.rawValue: MenuBarIcon.iconPhp.rawValue, + PreferenceName.fullPhpVersionDynamicIcon.rawValue: false, + + /// Preferences: Notifications + PreferenceName.notifyAboutVersionChange.rawValue: true, + PreferenceName.notifyAboutPhpFpmRestart.rawValue: true, + PreferenceName.notifyAboutServices.rawValue: true, + PreferenceName.notifyAboutPresets.rawValue: true, + PreferenceName.notifyAboutSecureToggle.rawValue: true, + PreferenceName.notifyAboutGlobalComposerStatus.rawValue: true, + /// Stats InternalStats.switchCount.rawValue: 0, InternalStats.launchCount.rawValue: 0, @@ -137,7 +161,8 @@ class Preferences { private static func cache() -> [PreferenceName: Any] { return [ // Part 1: Always Booleans - .shouldDisplayDynamicIcon: UserDefaults.standard.bool( + .shouldDisplayDynamicIcon: + UserDefaults.standard.bool( forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any, .fullPhpVersionDynamicIcon: UserDefaults.standard.bool( forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any, @@ -150,6 +175,19 @@ class Preferences { .automaticBackgroundUpdateCheck: UserDefaults.standard.bool( forKey: PreferenceName.automaticBackgroundUpdateCheck.rawValue) as Any, + .notifyAboutVersionChange: UserDefaults.standard.bool( + forKey: PreferenceName.notifyAboutVersionChange.rawValue) as Any, + .notifyAboutPhpFpmRestart: UserDefaults.standard.bool( + forKey: PreferenceName.notifyAboutPhpFpmRestart.rawValue) as Any, + .notifyAboutServices: UserDefaults.standard.bool( + forKey: PreferenceName.notifyAboutServices.rawValue) as Any, + .notifyAboutPresets: UserDefaults.standard.bool( + forKey: PreferenceName.notifyAboutPresets.rawValue) as Any, + .notifyAboutSecureToggle: UserDefaults.standard.bool( + forKey: PreferenceName.notifyAboutSecureToggle.rawValue) as Any, + .notifyAboutGlobalComposerStatus: UserDefaults.standard.bool( + forKey: PreferenceName.notifyAboutGlobalComposerStatus.rawValue) as Any, + // Part 2: Always Strings .globalHotkey: UserDefaults.standard.string( forKey: PreferenceName.globalHotkey.rawValue) as Any, diff --git a/phpmon/Domain/Preferences/PrefsVC.swift b/phpmon/Domain/Preferences/PrefsVC.swift new file mode 100644 index 0000000..71a83c9 --- /dev/null +++ b/phpmon/Domain/Preferences/PrefsVC.swift @@ -0,0 +1,244 @@ +// +// PrefsVC.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 30/03/2021. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Cocoa +import Carbon + +class GenericPreferenceVC: NSViewController { + + // MARK: - Content + + @IBOutlet weak var stackView: NSStackView! + + var views: [NSView] = [] + + override func viewDidLoad() { + super.viewDidLoad() + self.views.forEach({ self.stackView.addArrangedSubview($0) }) + } + + // MARK: - Deinitialization + + deinit { + Log.perf("PrefsVC deallocated") + } + + func getDynamicIconPV() -> NSView { + return CheckboxPreferenceView.make( + sectionText: "prefs.dynamic_icon".localized, + descriptionText: "prefs.dynamic_icon_desc".localized, + checkboxText: "prefs.dynamic_icon_title".localized, + preference: .shouldDisplayDynamicIcon, + action: { + MainMenu.shared.refreshIcon() + } + ) + } + + func getIconOptionsPV() -> NSView { + return SelectPreferenceView.make( + sectionText: "", + descriptionText: "prefs.icon_options_desc".localized, + options: MenuBarIcon.allCases.map({ return $0.rawValue }), + localizationPrefix: "prefs.icon_options", + preference: .iconTypeToDisplay, + action: { + MainMenu.shared.refreshIcon() + } + ) + } + + func getIconDensityPV() -> NSView { + return CheckboxPreferenceView.make( + sectionText: "prefs.info_density".localized, + descriptionText: "prefs.display_full_php_version_desc".localized, + checkboxText: "prefs.display_full_php_version".localized, + preference: .fullPhpVersionDynamicIcon, + action: { + MainMenu.shared.refreshIcon() + MainMenu.shared.rebuild() + } + ) + } + + func getAutoRestartPV() -> NSView { + return CheckboxPreferenceView.make( + sectionText: "prefs.services".localized, + descriptionText: "prefs.auto_restart_services_desc".localized, + checkboxText: "prefs.auto_restart_services_title".localized, + preference: .autoServiceRestartAfterExtensionToggle, + action: {} + ) + } + + func getAutomaticComposerUpdatePV() -> NSView { + CheckboxPreferenceView.make( + sectionText: "prefs.switcher".localized, + descriptionText: "prefs.auto_composer_update_desc".localized, + checkboxText: "prefs.auto_composer_update_title".localized, + preference: .autoComposerGlobalUpdateAfterSwitch, + action: {} + ) + } + + func getShortcutPV() -> NSView { + return HotkeyPreferenceView.make( + sectionText: "prefs.global_shortcut".localized, + descriptionText: "prefs.shortcut_desc".localized, + self + ) + } + + func getIntegrationsPV() -> NSView { + return CheckboxPreferenceView.make( + sectionText: "prefs.integrations".localized, + descriptionText: "prefs.open_protocol_desc".localized, + checkboxText: "prefs.open_protocol_title".localized, + preference: .allowProtocolForIntegrations, + action: {} + ) + } + + func getAutomaticUpdateCheckPV() -> NSView { + return CheckboxPreferenceView.make( + sectionText: "prefs.updates".localized, + descriptionText: "prefs.automatic_update_check_desc".localized, + checkboxText: "prefs.automatic_update_check_title".localized, + preference: .automaticBackgroundUpdateCheck, + action: {} + ) + } + + func getNotifyAboutVersionChangePV() -> NSView { + return CheckboxPreferenceView.make( + sectionText: "prefs.notifications".localized, + descriptionText: "prefs.notify_about_version_change_desc".localized, + checkboxText: "prefs.notify_about_version_change".localized, + preference: .notifyAboutVersionChange, + action: {} + ) + } + + func getNotifyAboutPhpFpmChangePV() -> NSView { + return CheckboxPreferenceView.make( + sectionText: "", + descriptionText: "prefs.notify_about_php_fpm_change_desc".localized, + checkboxText: "prefs.notify_about_php_fpm_change".localized, + preference: .notifyAboutPhpFpmRestart, + action: {} + ) + } + + func getNotifyAboutServicesPV() -> NSView { + return CheckboxPreferenceView.make( + sectionText: "", + descriptionText: "prefs.notify_about_services_desc".localized, + checkboxText: "prefs.notify_about_services".localized, + preference: .notifyAboutServices, + action: {} + ) + } + + func getNotifyAboutPresetsPV() -> NSView { + return CheckboxPreferenceView.make( + sectionText: "", + descriptionText: "prefs.notify_about_presets_desc".localized, + checkboxText: "prefs.notify_about_presets".localized, + preference: .notifyAboutPresets, + action: {} + ) + } + + func getNotifyAboutSecureTogglePV() -> NSView { + return CheckboxPreferenceView.make( + sectionText: "", + descriptionText: "prefs.notify_about_secure_status_desc".localized, + checkboxText: "prefs.notify_about_secure_status".localized, + preference: .notifyAboutSecureToggle, + action: {} + ) + } + + func getNotifyAboutGlobalComposerStatusPV() -> NSView { + return CheckboxPreferenceView.make( + sectionText: "", + descriptionText: "prefs.notify_about_composer_success_desc".localized, + checkboxText: "prefs.notify_about_composer_success".localized, + preference: .notifyAboutGlobalComposerStatus, + action: {} + ) + } + + // MARK: - Listening for hotkey delegate + + var listeningForHotkeyView: HotkeyPreferenceView? + + override func viewWillDisappear() { + if listeningForHotkeyView !== nil { + listeningForHotkeyView = nil + } + } +} + +class GeneralPreferencesVC: GenericPreferenceVC { + + // MARK: - Lifecycle + + public static func fromStoryboard() -> GenericPreferenceVC { + let vc = NSStoryboard(name: "Main", bundle: nil) + .instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC + + vc.views = [ + vc.getAutoRestartPV(), + vc.getAutomaticComposerUpdatePV(), + vc.getShortcutPV(), + vc.getIntegrationsPV(), + vc.getAutomaticUpdateCheckPV() + ] + + return vc + } +} + +class NotificationPreferencesVC: GenericPreferenceVC { + + public static func fromStoryboard() -> GenericPreferenceVC { + let vc = NSStoryboard(name: "Main", bundle: nil) + .instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC + + vc.views = [ + vc.getNotifyAboutVersionChangePV(), + vc.getNotifyAboutPresetsPV(), + vc.getNotifyAboutSecureTogglePV(), + vc.getNotifyAboutGlobalComposerStatusPV(), + vc.getNotifyAboutServicesPV(), + vc.getNotifyAboutPhpFpmChangePV() + ] + + return vc + } + +} + +class AppearancePreferencesVC: GenericPreferenceVC { + + // MARK: - Lifecycle + + public static func fromStoryboard() -> GenericPreferenceVC { + let vc = NSStoryboard(name: "Main", bundle: nil) + .instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC + + vc.views = [ + vc.getDynamicIconPV(), + vc.getIconOptionsPV(), + vc.getIconDensityPV() + ] + + return vc + } +} diff --git a/phpmon/Domain/Preferences/PrefsWC.swift b/phpmon/Domain/Preferences/PrefsWC.swift index de1b641..dfd080b 100644 --- a/phpmon/Domain/Preferences/PrefsWC.swift +++ b/phpmon/Domain/Preferences/PrefsWC.swift @@ -38,6 +38,8 @@ class PrefsWC: PMWindowController { } public static func show(delegate: NSWindowDelegate? = nil) { + var justCreated = false + if App.shared.preferencesWindowController == nil { Self.create(delegate: delegate) @@ -60,9 +62,16 @@ class PrefsWC: PMWindowController { width: tabVC.view.frame.size.width, height: tabVC.view.frame.size.height ) + + justCreated = true } App.shared.preferencesWindowController?.showWindow(self) + + if justCreated { + App.shared.preferencesWindowController?.positionWindowInTopLeftCorner() + } + NSApp.activate(ignoringOtherApps: true) } @@ -77,7 +86,17 @@ class PrefsWC: PMWindowController { PrefTabView( viewController: GeneralPreferencesVC.fromStoryboard(), label: "General", - icon: "gear" + icon: "gearshape" + ), + PrefTabView( + viewController: AppearancePreferencesVC.fromStoryboard(), + label: "Appearance", + icon: "paintbrush" + ), + PrefTabView( + viewController: NotificationPreferencesVC.fromStoryboard(), + label: "Notifications", + icon: "bell.badge" ) ] }() @@ -87,18 +106,24 @@ class PrefsWC: PMWindowController { override func keyDown(with event: NSEvent) { super.keyDown(with: event) - /* - if let vc = contentViewController as? PrefsVC { + guard let tabVC = self.contentViewController as? NSTabViewController else { + return + } + + guard let selected = tabVC.tabViewItems[tabVC.selectedTabViewItemIndex].viewController else { + return + } + + if let vc = selected as? GenericPreferenceVC { if vc.listeningForHotkeyView != nil { if event.keyCode == Keys.Escape || event.keyCode == Keys.Space { Log.info("A blacklisted key was pressed, canceling listen!") - vc.listeningForHotkeyView = nil + vc.listeningForHotkeyView!.unregister(nil) } else { vc.listeningForHotkeyView!.updateShortcut(event) } } } - */ } } diff --git a/phpmon/Domain/Preferences/Views/HotkeyPreferenceView.swift b/phpmon/Domain/Preferences/Views/HotkeyPreferenceView.swift index 93a3288..51ab865 100644 --- a/phpmon/Domain/Preferences/Views/HotkeyPreferenceView.swift +++ b/phpmon/Domain/Preferences/Views/HotkeyPreferenceView.swift @@ -11,8 +11,7 @@ import Cocoa class HotkeyPreferenceView: NSView, XibLoadable { - #warning("Refactor so this applies to any given preferences VC") - weak var delegate: GeneralPreferencesVC? + weak var delegate: GenericPreferenceVC? @IBOutlet weak var labelSection: NSTextField! @IBOutlet weak var labelDescription: NSTextField! @@ -20,7 +19,7 @@ class HotkeyPreferenceView: NSView, XibLoadable { @IBOutlet weak var buttonSetShortcut: NSButton! @IBOutlet weak var buttonClearShortcut: NSButton! - static func make(sectionText: String, descriptionText: String, _ prefsVC: GeneralPreferencesVC) -> NSView { + static func make(sectionText: String, descriptionText: String, _ prefsVC: GenericPreferenceVC) -> NSView { let view = Self.createFromXib()! view.labelSection.stringValue = sectionText view.labelDescription.stringValue = descriptionText diff --git a/phpmon/Domain/Presets/Preset.swift b/phpmon/Domain/Presets/Preset.swift index 340aa09..e278510 100644 --- a/phpmon/Domain/Presets/Preset.swift +++ b/phpmon/Domain/Presets/Preset.swift @@ -8,7 +8,7 @@ import Foundation -struct Preset: Codable { +struct Preset: Codable, Equatable { let name: String let version: String? let extensions: [String: Bool] @@ -69,6 +69,9 @@ struct Preset: Codable { */ public func apply() { Task { + // Was this a rollback? + let wasRollback = (self.name == "AutomaticRevertSnapshot") + // Save the preset that would revert this preset self.persistRevert() @@ -101,6 +104,21 @@ struct Preset: Codable { // Restart PHP FPM process (also reloads menu, which will show the preset rollback) Actions.restartPhpFpm() + + // Show the correct notification + if wasRollback { + LocalNotification.send( + title: "notification.preset_reverted_title".localized, + subtitle: "notification.preset_reverted_desc".localized, + preference: .notifyAboutPresets + ) + } else { + LocalNotification.send( + title: "notification.preset_applied_title".localized, + subtitle: "notification.preset_applied_desc".localized(self.name), + preference: .notifyAboutPresets + ) + } } } @@ -178,7 +196,7 @@ struct Preset: Codable { public var revertSnapshot: Preset { return Preset( - name: "Revert", + name: "AutomaticRevertSnapshot", version: diffVersion(), extensions: diffExtensions(), configuration: diffConfiguration() diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 630389c..e002bc3 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -193,6 +193,7 @@ "prefs.switcher" = "Switcher:"; "prefs.integrations" = "Integrations:"; "prefs.updates" = "Updates:"; +"prefs.notifications" = "Notifications:"; "prefs.icon_options.php" = "Display PHP Icon"; "prefs.icon_options.elephant" = "Display Elephant Icon"; @@ -223,6 +224,24 @@ "prefs.shortcut_clear" = "Clear"; "prefs.shortcut_desc" = "If a shortcut combination is set up, you can toggle PHP Monitor wherever you are by pressing the key combination you chose. (Cancel choosing a shortcut by pressing the spacebar.)"; +"prefs.notify_about_version_change_desc" = "Displays a notification whenever the active PHP version changes."; +"prefs.notify_about_version_change" = "Notify about PHP version switch"; + +"prefs.notify_about_php_fpm_change_desc" = "Displays a notification whenever the active PHP-FPM process has restarted due to a configuration change."; +"prefs.notify_about_php_fpm_change" = "Notify about PHP-FPM restart"; + +"prefs.notify_about_services_desc" = "Displays a notification whenever any of the Homebrew services (installed and configured by Valet) have been restarted or stopped."; +"prefs.notify_about_services" = "Notify about services status"; + +"prefs.notify_about_presets_desc" = "Displays a notification whenever a preset has been successfully applied or reverted."; +"prefs.notify_about_presets" = "Notify about applied presets"; + +"prefs.notify_about_secure_status_desc" = "Displays a notification when a domain has been secured or unsecured."; +"prefs.notify_about_secure_status" = "Notify about secure/unsecure status"; + +"prefs.notify_about_composer_success_desc" = "Displays a notification when the global composer configuration was successfully updated."; +"prefs.notify_about_composer_success" = "Notify about global composer update"; + // NOTIFICATIONS "notification.version_changed_title" = "PHP %@ now active"; @@ -237,6 +256,12 @@ "notification.services_restarted" = "Valet services restarted"; "notification.services_restarted_desc" = "All services have been successfully restarted."; +"notification.preset_applied_title" = "Preset applied"; +"notification.preset_applied_desc" = "The preset '%@' has been successfully applied."; + +"notification.preset_reverted_title" = "Preset reverted"; +"notification.preset_reverted_desc" = "The last preset you applied has been undone. Your previous configuration is now active."; + // Composer Update "alert.composer_missing.title" = "Composer not found!"; "alert.composer_missing.subtitle" = "PHP Monitor could not find Composer. Make sure that Composer is installed and try again.";