From c5fd43312f675da15582ec17939da3563bea3135 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 18 Jun 2022 17:59:43 +0200 Subject: [PATCH 01/43] =?UTF-8?q?=F0=9F=8F=97=20Use=20@MainActor=20for=20B?= =?UTF-8?q?etterAlert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Common/Helpers/LocalNotification.swift | 2 +- phpmon/Domain/App/InterAppHandler.swift | 14 ++++++---- .../Composer/ComposerWindow.swift | 6 ++-- phpmon/Domain/Menu/MainMenu+Actions.swift | 28 ++++++++----------- phpmon/Domain/Menu/MainMenu+Async.swift | 4 +-- phpmon/Domain/Menu/MainMenu+FixMyValet.swift | 8 +++--- phpmon/Domain/Menu/MainMenu+Switcher.swift | 16 ++++++----- phpmon/Domain/Notice/BetterAlert.swift | 4 +-- phpmon/Domain/Presets/Preset.swift | 4 +-- 9 files changed, 44 insertions(+), 42 deletions(-) diff --git a/phpmon/Common/Helpers/LocalNotification.swift b/phpmon/Common/Helpers/LocalNotification.swift index 91ffa4b..0c61e40 100644 --- a/phpmon/Common/Helpers/LocalNotification.swift +++ b/phpmon/Common/Helpers/LocalNotification.swift @@ -10,7 +10,7 @@ import UserNotifications class LocalNotification { - public static func send(title: String, subtitle: String, preference: PreferenceName) { + @MainActor public static func send(title: String, subtitle: String, preference: PreferenceName) { if !Preferences.isEnabled(preference) { return } diff --git a/phpmon/Domain/App/InterAppHandler.swift b/phpmon/Domain/App/InterAppHandler.swift index 227fde3..73e6477 100644 --- a/phpmon/Domain/App/InterAppHandler.swift +++ b/phpmon/Domain/App/InterAppHandler.swift @@ -56,12 +56,14 @@ class InterApp { if PhpEnv.shared.availablePhpVersions.contains(version) { MainMenu.shared.switchToPhpVersion(version) } else { - BetterAlert().withInformation( - title: "alert.php_switch_unavailable.title".localized, - subtitle: "alert.php_switch_unavailable.subtitle".localized(version) - ).withPrimary( - text: "alert.php_switch_unavailable.ok".localized - ).show() + DispatchQueue.main.async { + BetterAlert().withInformation( + title: "alert.php_switch_unavailable.title".localized, + subtitle: "alert.php_switch_unavailable.subtitle".localized(version) + ).withPrimary( + text: "alert.php_switch_unavailable.ok".localized + ).show() + } } }) ]} diff --git a/phpmon/Domain/Integrations/Composer/ComposerWindow.swift b/phpmon/Domain/Integrations/Composer/ComposerWindow.swift index f9413e0..9f9f084 100644 --- a/phpmon/Domain/Integrations/Composer/ComposerWindow.swift +++ b/phpmon/Domain/Integrations/Composer/ComposerWindow.swift @@ -25,7 +25,9 @@ class ComposerWindow { Paths.shared.detectBinaryPaths() if Paths.composer == nil { - presentMissingAlert() + DispatchQueue.main.async { + self.presentMissingAlert() + } return } @@ -116,7 +118,7 @@ class ComposerWindow { // MARK: Alert - private func presentMissingAlert() { + @MainActor private func presentMissingAlert() { BetterAlert() .withInformation( title: "alert.composer_missing.title".localized, diff --git a/phpmon/Domain/Menu/MainMenu+Actions.swift b/phpmon/Domain/Menu/MainMenu+Actions.swift index c7dc77e..6e2819f 100644 --- a/phpmon/Domain/Menu/MainMenu+Actions.swift +++ b/phpmon/Domain/Menu/MainMenu+Actions.swift @@ -53,13 +53,11 @@ extension MainMenu { Actions.restartPhpFpm() Actions.restartNginx() } success: { - DispatchQueue.main.async { - LocalNotification.send( - title: "notification.services_restarted".localized, - subtitle: "notification.services_restarted_desc".localized, - preference: .notifyAboutServices - ) - } + LocalNotification.send( + title: "notification.services_restarted".localized, + subtitle: "notification.services_restarted_desc".localized, + preference: .notifyAboutServices + ) } } @@ -67,13 +65,11 @@ extension MainMenu { asyncExecution { Actions.stopValetServices() } success: { - DispatchQueue.main.async { - LocalNotification.send( - title: "notification.services_stopped".localized, - subtitle: "notification.services_stopped_desc".localized, - preference: .notifyAboutServices - ) - } + LocalNotification.send( + title: "notification.services_stopped".localized, + subtitle: "notification.services_stopped_desc".localized, + preference: .notifyAboutServices + ) } } @@ -155,7 +151,7 @@ extension MainMenu { } } - @objc func rollbackPreset() { + @MainActor @objc func rollbackPreset() { guard let preset = PresetHelper.rollbackPreset else { return } @@ -180,7 +176,7 @@ extension MainMenu { } } - @objc func showPresetHelp() { + @MainActor @objc func showPresetHelp() { BetterAlert().withInformation( title: "preset_help_title".localized, subtitle: "preset_help_info".localized, diff --git a/phpmon/Domain/Menu/MainMenu+Async.swift b/phpmon/Domain/Menu/MainMenu+Async.swift index 3a9d273..11aea36 100644 --- a/phpmon/Domain/Menu/MainMenu+Async.swift +++ b/phpmon/Domain/Menu/MainMenu+Async.swift @@ -36,8 +36,8 @@ extension MainMenu { */ func asyncExecution( _ execute: @escaping () throws -> Void, - success: @escaping () -> Void = {}, - failure: @escaping (Error) -> Void = { _ in }, + success: @MainActor @escaping () -> Void = {}, + failure: @MainActor @escaping (Error) -> Void = { _ in }, behaviours: [AsyncBehaviour] = [ .setsBusyUI, .reloadsPhpInstallation, diff --git a/phpmon/Domain/Menu/MainMenu+FixMyValet.swift b/phpmon/Domain/Menu/MainMenu+FixMyValet.swift index aff2fea..00bd5a6 100644 --- a/phpmon/Domain/Menu/MainMenu+FixMyValet.swift +++ b/phpmon/Domain/Menu/MainMenu+FixMyValet.swift @@ -11,7 +11,7 @@ import AppKit extension MainMenu { - @objc func fixMyValet() { + @MainActor @objc func fixMyValet() { let previousVersion = PhpEnv.phpInstall.version.short if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) { @@ -42,7 +42,7 @@ extension MainMenu { } } - private func presentAlertForMissingFormula() { + @MainActor private func presentAlertForMissingFormula() { BetterAlert() .withInformation( title: "alert.php_formula_missing.title".localized, @@ -52,7 +52,7 @@ extension MainMenu { .show() } - private func presentAlertForSameVersion() { + @MainActor private func presentAlertForSameVersion() { BetterAlert() .withInformation( title: "alert.fix_my_valet_done.title".localized, @@ -63,7 +63,7 @@ extension MainMenu { .show() } - private func presentAlertForDifferentVersion(version: String) { + @MainActor private func presentAlertForDifferentVersion(version: String) { BetterAlert() .withInformation( title: "alert.fix_my_valet_done.title".localized, diff --git a/phpmon/Domain/Menu/MainMenu+Switcher.swift b/phpmon/Domain/Menu/MainMenu+Switcher.swift index 57feaee..75caef0 100644 --- a/phpmon/Domain/Menu/MainMenu+Switcher.swift +++ b/phpmon/Domain/Menu/MainMenu+Switcher.swift @@ -50,7 +50,7 @@ extension MainMenu { } } - private func suggestFixMyValet(failed version: String) { + @MainActor private func suggestFixMyValet(failed version: String) { let outcome = BetterAlert() .withInformation( title: "alert.php_switch_failed.title".localized(version), @@ -75,12 +75,14 @@ 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), - preference: .notifyAboutVersionChange - ) + DispatchQueue.main.async { + LocalNotification.send( + title: String(format: "notification.version_changed_title".localized, version), + subtitle: String(format: "notification.version_changed_desc".localized, version), + preference: .notifyAboutVersionChange + ) - PhpEnv.phpInstall.notifyAboutBrokenPhpFpm() + PhpEnv.phpInstall.notifyAboutBrokenPhpFpm() + } } } diff --git a/phpmon/Domain/Notice/BetterAlert.swift b/phpmon/Domain/Notice/BetterAlert.swift index b198040..c42dad8 100644 --- a/phpmon/Domain/Notice/BetterAlert.swift +++ b/phpmon/Domain/Notice/BetterAlert.swift @@ -102,14 +102,14 @@ class BetterAlert { /** Shows the modal and does not return anything. */ - public func show() { + @MainActor public func show() { _ = self.runModal() } /** Shows the modal for a particular error. */ - public static func show(for error: Error & AlertableError) { + @MainActor public static func show(for error: Error & AlertableError) { let key = error.getErrorMessageKey() return BetterAlert().withInformation( title: "\(key).title".localized, diff --git a/phpmon/Domain/Presets/Preset.swift b/phpmon/Domain/Presets/Preset.swift index e278510..e2d9794 100644 --- a/phpmon/Domain/Presets/Preset.swift +++ b/phpmon/Domain/Presets/Preset.swift @@ -107,13 +107,13 @@ struct Preset: Codable, Equatable { // Show the correct notification if wasRollback { - LocalNotification.send( + await LocalNotification.send( title: "notification.preset_reverted_title".localized, subtitle: "notification.preset_reverted_desc".localized, preference: .notifyAboutPresets ) } else { - LocalNotification.send( + await LocalNotification.send( title: "notification.preset_applied_title".localized, subtitle: "notification.preset_applied_desc".localized(self.name), preference: .notifyAboutPresets From 25a7dded737ccc210cbbabb113a5ea8cc0ec0f59 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 19 Jun 2022 13:08:15 +0200 Subject: [PATCH 02/43] =?UTF-8?q?=F0=9F=90=9B=20Fix=20typo=20when=20PHP=20?= =?UTF-8?q?switch=20failed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Menu/MainMenu+Switcher.swift | 3 ++- phpmon/Localizable.strings | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/phpmon/Domain/Menu/MainMenu+Switcher.swift b/phpmon/Domain/Menu/MainMenu+Switcher.swift index 75caef0..0dad902 100644 --- a/phpmon/Domain/Menu/MainMenu+Switcher.swift +++ b/phpmon/Domain/Menu/MainMenu+Switcher.swift @@ -54,7 +54,8 @@ extension MainMenu { let outcome = BetterAlert() .withInformation( title: "alert.php_switch_failed.title".localized(version), - subtitle: "alert.php_switch_failed.info".localized(version) + subtitle: "alert.php_switch_failed.info".localized(version), + description: "alert.php_switch_failed.desc".localized() ) .withPrimary(text: "alert.php_switch_failed.confirm".localized) .withSecondary(text: "alert.php_switch_failed.cancel".localized) diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 284804b..c477b17 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -318,7 +318,8 @@ problem manually, using your own Terminal app (this just shows you the output)." // Suggest Fix My Valet "alert.php_switch_failed.title" = "Switching to PHP %@ seems to have failed."; -"alert.php_switch_failed.info" = "PHP Monitor has detected that PHP %@ is not active after completing its switching procedure. You can try to run \"Fix My Valet\" and switch again after that. Do you want to try \"Fix My Valet\"?"; +"alert.php_switch_failed.info" = "PHP Monitor has detected that PHP %@ is not active after completing its switching procedure. You can try to run \"Fix My Valet\" and switch again after that. Do you want to try this fix?"; +"alert.php_switch_failed.desc" = "First off, you should try \"Fix My Valet\" if you haven't tried it yet. If PHP Monitor remains unable to change the active PHP version even after that, you may need to upgrade Valet and the Homebrew packages on your system. You can do this by running `brew update && brew upgrade` as well as upgrading Valet by running `composer global update && valet install`."; "alert.php_switch_failed.confirm" = "Yes, run \"Fix My Valet\""; "alert.php_switch_failed.cancel" = "Do Not Run"; From cf03727dc0b383713e6a616cced74713add4e9ae Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 19 Jun 2022 13:08:54 +0200 Subject: [PATCH 03/43] =?UTF-8?q?=F0=9F=8F=97=20Use=20@MainActor=20on=20Ma?= =?UTF-8?q?inMenu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Menu/MainMenu.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index bf4990e..fe85771 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -21,7 +21,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate /** The status bar item with variable length. */ - let statusItem = NSStatusBar.system.statusItem( + @MainActor let statusItem = NSStatusBar.system.statusItem( withLength: NSStatusItem.variableLength ) @@ -77,7 +77,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate /** Sets the status bar image based on a version string. */ - func setStatusBarImage(version: String) { + @MainActor func setStatusBarImage(version: String) { setStatusBar( image: (Preferences.preferences[.iconTypeToDisplay] as! String != MenuBarIcon.noIcon.rawValue) ? MenuBarImageGenerator.textToImageWithIcon(text: version) @@ -89,7 +89,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate Sets the status bar image, based on the provided NSImage. The image will be used as a template image. */ - func setStatusBar(image: NSImage) { + @MainActor func setStatusBar(image: NSImage) { if let button = statusItem.button { image.isTemplate = true button.image = image From cac7048d92d23f29ab15aeb27b6afd8340ebc1ac Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 8 Jul 2022 20:44:40 +0200 Subject: [PATCH 04/43] =?UTF-8?q?=F0=9F=91=8C=20Use=20MainActor=20for=20me?= =?UTF-8?q?thods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Menu/MainMenu+Switcher.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpmon/Domain/Menu/MainMenu+Switcher.swift b/phpmon/Domain/Menu/MainMenu+Switcher.swift index b504b6c..66c61d2 100644 --- a/phpmon/Domain/Menu/MainMenu+Switcher.swift +++ b/phpmon/Domain/Menu/MainMenu+Switcher.swift @@ -52,7 +52,7 @@ extension MainMenu { } } - private func checkForPlatformIssues() { + @MainActor private func checkForPlatformIssues() { if Valet.shared.hasPlatformIssues() { Log.info("Composer platform issue(s) detected.") self.suggestFixMyComposer() @@ -74,7 +74,7 @@ extension MainMenu { } } - private func suggestFixMyComposer() { + @MainActor private func suggestFixMyComposer() { BetterAlert().withInformation( title: "alert.global_composer_platform_issues.title".localized, subtitle: "alert.global_composer_platform_issues.subtitle".localized, From 6e3bb1d322842607ac42cf46355082732f645730 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 8 Jul 2022 21:13:46 +0200 Subject: [PATCH 05/43] =?UTF-8?q?=E2=9C=A8=20Added=20onboarding=20sample?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 12 +++++ phpmon/Domain/SwiftUI/Menu/ServicesView.swift | 4 +- .../SwiftUI/Onboarding/OnboardingView.swift | 50 +++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 332d8f2..18644f8 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -226,6 +226,7 @@ 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 */; }; + C4E9D2C02878B336008FFDAD /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E9D2BF2878B336008FFDAD /* OnboardingView.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 */; }; @@ -420,6 +421,7 @@ 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 = ""; }; + C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; 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 = ""; }; @@ -1005,9 +1007,18 @@ path = Switcher; sourceTree = ""; }; + C4E9D2BE2878B32D008FFDAD /* Onboarding */ = { + isa = PBXGroup; + children = ( + C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */, + ); + path = Onboarding; + sourceTree = ""; + }; C4EE55B027708BB2001DF387 /* SwiftUI */ = { isa = PBXGroup; children = ( + C4E9D2BE2878B32D008FFDAD /* Onboarding */, C4B609182853AAA700C95265 /* Domains */, C4B609172853AA9E00C95265 /* Menu */, C4B609162853AA9A00C95265 /* Common */, @@ -1228,6 +1239,7 @@ C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */, C4C0E8DF27F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */, C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */, + C4E9D2C02878B336008FFDAD /* OnboardingView.swift in Sources */, C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */, C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */, C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */, diff --git a/phpmon/Domain/SwiftUI/Menu/ServicesView.swift b/phpmon/Domain/SwiftUI/Menu/ServicesView.swift index cfe8d5d..db87a04 100644 --- a/phpmon/Domain/SwiftUI/Menu/ServicesView.swift +++ b/phpmon/Domain/SwiftUI/Menu/ServicesView.swift @@ -56,7 +56,9 @@ struct ServicesView: View { }.frame(minWidth: 0, maxWidth: .infinity) } else { // Empty cell - VStack { EmptyView() }.frame(minWidth: 0, maxWidth: .infinity) + VStack { + EmptyView() + }.frame(minWidth: 0, maxWidth: .infinity) } } } diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift new file mode 100644 index 0000000..1003b90 --- /dev/null +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift @@ -0,0 +1,50 @@ +// +// OnboardingView.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 08/07/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import SwiftUI + +struct OnboardingView: View { + var body: some View { + HStack(alignment: .top) { + Image(nsImage: NSApp.applicationIconImage) + .resizable() + .padding() + .frame(width: 120, height: 120) + VStack(alignment: .leading) { + Text("Welcome to PHP Monitor!") + .font(.title) + .bold() + .padding(.bottom, 5) + Text("If you're seeing this message, then the app has successfully started without any issues. That's honestly the hardest part — from now on I hope it's smooth sailing for you.") + .padding(.bottom) + VStack(alignment: .leading) { + Text("Switch PHP versions").font(.headline) + Text("Manage your domains").font(.headline) + Text("Domain-specific PHP version isolation").font(.headline) + Text("Find your configuration files").font(.headline) + } + Text("I hope you find the app as useful as I do. Enjoy, and if you can, please consider supporting the app. Thank you!") + .padding(.top) + .padding(.bottom) + VStack(alignment: .leading) { + Button("Get Started") { + // + } + } + }.frame(maxWidth: .infinity) + }.padding(20) + } +} + +struct OnboardingView_Previews: PreviewProvider { + static var previews: some View { + OnboardingView().frame( + width: 600 + ) + } +} From 8a8d32cb5d9432c25c32184a2e22306eef193c7e Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 13 Jul 2022 11:27:32 +0200 Subject: [PATCH 06/43] =?UTF-8?q?=F0=9F=8D=B1=20Tweaked=20onboarding=20scr?= =?UTF-8?q?een?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Assets.xcassets/Tour/Contents.json | 6 ++ .../Tour/Tour.Domains.imageset/Contents.json | 21 +++++ .../Tour/Tour.Domains.imageset/tour3.heic | Bin 0 -> 41049 bytes .../Tour.Isolation.imageset/Contents.json | 21 +++++ .../Tour/Tour.Isolation.imageset/tour4.heic | Bin 0 -> 55194 bytes .../Tour/Tour.MenuBar.imageset/Contents.json | 21 +++++ .../Tour/Tour.MenuBar.imageset/tour2.heic | Bin 0 -> 26215 bytes .../SwiftUI/Onboarding/OnboardingView.swift | 75 ++++++++++++------ phpmon/Localizable.strings | 12 +++ 9 files changed, 131 insertions(+), 25 deletions(-) create mode 100644 phpmon/Assets.xcassets/Tour/Contents.json create mode 100644 phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/Contents.json create mode 100644 phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/tour3.heic create mode 100644 phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/Contents.json create mode 100644 phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/tour4.heic create mode 100644 phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/Contents.json create mode 100644 phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/tour2.heic diff --git a/phpmon/Assets.xcassets/Tour/Contents.json b/phpmon/Assets.xcassets/Tour/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/phpmon/Assets.xcassets/Tour/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/Contents.json b/phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/Contents.json new file mode 100644 index 0000000..87011c7 --- /dev/null +++ b/phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "tour3.heic", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/tour3.heic b/phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/tour3.heic new file mode 100644 index 0000000000000000000000000000000000000000..2c64a6dff07b789a6612db697d11cea6b96bcbd4 GIT binary patch literal 41049 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%0Dk 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 Q&xCUS?*L@`PvOA-2dBX=2mk;8 literal 0 HcmV?d00001 diff --git a/phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/Contents.json b/phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/Contents.json new file mode 100644 index 0000000..4dfdc53 --- /dev/null +++ b/phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "tour4.heic", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/tour4.heic b/phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/tour4.heic new file mode 100644 index 0000000000000000000000000000000000000000..e9f37f8a1a76d00c42f639b4dc2b9aaf8a64e850 GIT binary patch literal 55194 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 T6~KSrL^9%!(GB7s0FeJ57EGT* literal 0 HcmV?d00001 diff --git a/phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/Contents.json b/phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/Contents.json new file mode 100644 index 0000000..e430b0e --- /dev/null +++ b/phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "tour2.heic", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/tour2.heic b/phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/tour2.heic new file mode 100644 index 0000000000000000000000000000000000000000..9bb7f6f043e3e10d9e76bc9c9f89ad2e3698aeed GIT binary patch literal 26215 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

lutKZbqLqFKAK52~+rmh|gA*x+)2) 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 K|0*A^ukB3k;#dy= literal 0 HcmV?d00001 diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift index 1003b90..569e543 100644 --- a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift @@ -10,41 +10,66 @@ import SwiftUI struct OnboardingView: View { var body: some View { - HStack(alignment: .top) { - Image(nsImage: NSApp.applicationIconImage) - .resizable() - .padding() - .frame(width: 120, height: 120) - VStack(alignment: .leading) { - Text("Welcome to PHP Monitor!") + VStack(alignment: .center) { + Image(nsImage: NSApp.applicationIconImage) + .resizable() + .frame(width: 90, height: 90) + Text("onboarding.welcome".localized) .font(.title) .bold() .padding(.bottom, 5) - Text("If you're seeing this message, then the app has successfully started without any issues. That's honestly the hardest part — from now on I hope it's smooth sailing for you.") + Text("onboarding.explore".localized) .padding(.bottom) - VStack(alignment: .leading) { - Text("Switch PHP versions").font(.headline) - Text("Manage your domains").font(.headline) - Text("Domain-specific PHP version isolation").font(.headline) - Text("Find your configuration files").font(.headline) + TabView { + VStack { + Image("Tour.MenuBar") + .resizable() + .aspectRatio(contentMode: .fit) + .padding(.top) + Text("onboarding.tour.menu_bar".localized) + .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) + }.tabItem { Label("onboarding.tour.menu_bar.title".localized, systemImage: "") } + VStack { + Image("Tour.Domains") + .resizable() + .aspectRatio(contentMode: .fit) + .padding(.top) + Text("onboarding.tour.domains".localized) + .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) + }.tabItem { Label("onboarding.tour.domains.title".localized, systemImage: "") } + VStack { + Image("Tour.Isolation") + .resizable() + .aspectRatio(contentMode: .fit) + .padding(.top) + Text("onboarding.tour.isolation".localized) + .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) + }.tabItem { Label("onboarding.tour.isolation.title".localized, systemImage: "") } } - Text("I hope you find the app as useful as I do. Enjoy, and if you can, please consider supporting the app. Thank you!") - .padding(.top) - .padding(.bottom) - VStack(alignment: .leading) { - Button("Get Started") { - // - } + Text("onboarding.tour.once".localized) + .font(.subheadline) + .foregroundColor(.gray) + .padding(.top, 5) + Button("Close Tour") { + } - }.frame(maxWidth: .infinity) - }.padding(20) + } + .frame(maxWidth: .infinity) + .padding() } } struct OnboardingView_Previews: PreviewProvider { static var previews: some View { - OnboardingView().frame( - width: 600 - ) + Group { + OnboardingView().frame( + width: 600, + height: 600 + ) + OnboardingView().preferredColorScheme(.dark).frame( + width: 600, + height: 600 + ) + } } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 36fd8a9..1bcc5c5 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -497,3 +497,15 @@ You can do this by running `composer global update` in your terminal. After that "alert.warnings.tld_issue.title" = "You are not using `.test` as the TLD for Valet."; "alert.warnings.tld_issue.subtitle" = "Using a non-default TLD may not work correctly and is not officially supported."; "alert.warnings.tld_issue.description" = "PHP Monitor will remain functional, but there might be issues: the app might not correctly show which domains have been secured. For optimal results, go to your Valet configuration file (config.json in the Valet directory) and change the TLD back to `test`."; + +// ONBOARDING + +"onboarding.welcome" = "Welcome to PHP Monitor!"; +"onboarding.explore" = "Explore some of the features below, or close the tour and get started."; +"onboarding.tour.menu_bar.title" = "Get Started"; +"onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From here, you can switch the globally linked PHP version, start or stop services, locate configuration files, and more."; +"onboarding.tour.domains.title" = "Domains"; +"onboarding.tour.domains" = "By opening the Domains window via the Menu Bar item, you can view which domains are linked and parked."; +"onboarding.tour.isolation.title" = "Isolation"; +"onboarding.tour.isolation" = "If you have Valet 3 installed, you can even use domain isolation by right-clicking on a given domain in the Domains window. This allows you to pick a specific version of PHP to use for that domain!"; +"onboarding.tour.once" = "You will only see the Welcome Tour once. You can re-open the Welcome Tour later via the menu bar icon."; From 8fb43f7a16de3865fdb9ec930f351c20d6a3d7c8 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 13 Jul 2022 22:22:30 +0200 Subject: [PATCH 07/43] =?UTF-8?q?=F0=9F=8D=B1=20Fix=20indentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SwiftUI/Onboarding/OnboardingView.swift | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift index 569e543..2756965 100644 --- a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift @@ -10,52 +10,52 @@ import SwiftUI struct OnboardingView: View { var body: some View { - VStack(alignment: .center) { - Image(nsImage: NSApp.applicationIconImage) - .resizable() - .frame(width: 90, height: 90) - Text("onboarding.welcome".localized) - .font(.title) - .bold() - .padding(.bottom, 5) - Text("onboarding.explore".localized) - .padding(.bottom) - TabView { - VStack { - Image("Tour.MenuBar") - .resizable() - .aspectRatio(contentMode: .fit) - .padding(.top) - Text("onboarding.tour.menu_bar".localized) - .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) - }.tabItem { Label("onboarding.tour.menu_bar.title".localized, systemImage: "") } - VStack { - Image("Tour.Domains") - .resizable() - .aspectRatio(contentMode: .fit) - .padding(.top) - Text("onboarding.tour.domains".localized) - .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) - }.tabItem { Label("onboarding.tour.domains.title".localized, systemImage: "") } - VStack { - Image("Tour.Isolation") - .resizable() - .aspectRatio(contentMode: .fit) - .padding(.top) - Text("onboarding.tour.isolation".localized) - .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) - }.tabItem { Label("onboarding.tour.isolation.title".localized, systemImage: "") } - } - Text("onboarding.tour.once".localized) - .font(.subheadline) - .foregroundColor(.gray) - .padding(.top, 5) - Button("Close Tour") { - - } + VStack(alignment: .center) { + Image(nsImage: NSApp.applicationIconImage) + .resizable() + .frame(width: 90, height: 90) + Text("onboarding.welcome".localized) + .font(.title) + .bold() + .padding(.bottom, 5) + Text("onboarding.explore".localized) + .padding(.bottom) + TabView { + VStack { + Image("Tour.MenuBar") + .resizable() + .aspectRatio(contentMode: .fit) + .padding(.top) + Text("onboarding.tour.menu_bar".localized) + .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) + }.tabItem { Label("onboarding.tour.menu_bar.title".localized, systemImage: "") } + VStack { + Image("Tour.Domains") + .resizable() + .aspectRatio(contentMode: .fit) + .padding(.top) + Text("onboarding.tour.domains".localized) + .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) + }.tabItem { Label("onboarding.tour.domains.title".localized, systemImage: "") } + VStack { + Image("Tour.Isolation") + .resizable() + .aspectRatio(contentMode: .fit) + .padding(.top) + Text("onboarding.tour.isolation".localized) + .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) + }.tabItem { Label("onboarding.tour.isolation.title".localized, systemImage: "") } } - .frame(maxWidth: .infinity) - .padding() + Text("onboarding.tour.once".localized) + .font(.subheadline) + .foregroundColor(.gray) + .padding(.top, 5) + Button("Close Tour") { + + } + } + .frame(maxWidth: .infinity) + .padding() } } From a1df2deec5516b02228e170fb1f92f2cbf29405f Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 25 Jul 2022 20:48:26 +0200 Subject: [PATCH 08/43] =?UTF-8?q?=F0=9F=91=8C=20Cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 12 ++++++ phpmon/Domain/Preferences/Keys.swift | 14 +++++++ .../Domain/Preferences/PrefsWC+Hotkey.swift | 38 +++++++++++++++++++ phpmon/Domain/Preferences/PrefsWC.swift | 34 ++--------------- 4 files changed, 67 insertions(+), 31 deletions(-) create mode 100644 phpmon/Domain/Preferences/Keys.swift create mode 100644 phpmon/Domain/Preferences/PrefsWC+Hotkey.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 332d8f2..c15f01f 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -204,6 +204,8 @@ C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; + C4CDA893288F1A71007CE25F /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CDA892288F1A71007CE25F /* Keys.swift */; }; + C4CDA894288F1A71007CE25F /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CDA892288F1A71007CE25F /* Keys.swift */; }; C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; }; C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; }; C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; }; @@ -263,6 +265,8 @@ C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; }; C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; }; C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; }; + C4FACE80288F1C0D00FC478F /* PrefsWC+Hotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE7F288F1C0D00FC478F /* PrefsWC+Hotkey.swift */; }; + C4FACE81288F1C0D00FC478F /* PrefsWC+Hotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE7F288F1C0D00FC478F /* PrefsWC+Hotkey.swift */; }; C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; }; C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F361602836BFD9003598CC /* MainMenu+Actions.swift */; }; C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; }; @@ -406,6 +410,7 @@ C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = ""; }; C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = ""; }; C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = ""; }; + C4CDA892288F1A71007CE25F /* Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = ""; }; C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = ""; }; C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerWindow.swift; sourceTree = ""; }; C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationFile.swift; sourceTree = ""; }; @@ -438,6 +443,7 @@ C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtensionTest.swift; sourceTree = ""; }; C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = ""; }; C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + C4FACE7F288F1C0D00FC478F /* PrefsWC+Hotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrefsWC+Hotkey.swift"; sourceTree = ""; }; C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionDetectionTest.swift; sourceTree = ""; }; C4FE011028084FC200D1DE6D /* SelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionVC.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -464,6 +470,7 @@ isa = PBXGroup; children = ( C4998F092617633900B2526E /* PrefsWC.swift */, + C4FACE7F288F1C0D00FC478F /* PrefsWC+Hotkey.swift */, 5420395826135DC100FB00FA /* PrefsVC.swift */, 5420395E2613607600FB00FA /* Preferences.swift */, C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */, @@ -471,6 +478,7 @@ C4DEB7D327A5D60B00834718 /* Stats.swift */, C41CD0272628D8E20065BBED /* Keybinds */, 54FCFD28276C88C0004CE748 /* Views */, + C4CDA892288F1A71007CE25F /* Keys.swift */, ); path = Preferences; sourceTree = ""; @@ -1238,9 +1246,11 @@ C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */, C4B6091A2853AAD300C95265 /* SectionHeaderView.swift in Sources */, C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */, + C4FACE80288F1C0D00FC478F /* PrefsWC+Hotkey.swift in Sources */, C42800AA28452AA10099C999 /* StatusMenu+Items.swift in Sources */, C415D3B72770F294005EF286 /* Actions.swift in Sources */, C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */, + C4CDA893288F1A71007CE25F /* Keys.swift in Sources */, C4F361612836BFD9003598CC /* MainMenu+Actions.swift in Sources */, C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */, 54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */, @@ -1324,6 +1334,7 @@ C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */, 54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */, C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */, + C4FACE81288F1C0D00FC478F /* PrefsWC+Hotkey.swift in Sources */, 54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */, C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */, C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */, @@ -1398,6 +1409,7 @@ C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */, C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */, C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */, + C4CDA894288F1A71007CE25F /* Keys.swift in Sources */, C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */, C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */, C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */, diff --git a/phpmon/Domain/Preferences/Keys.swift b/phpmon/Domain/Preferences/Keys.swift new file mode 100644 index 0000000..0216aa3 --- /dev/null +++ b/phpmon/Domain/Preferences/Keys.swift @@ -0,0 +1,14 @@ +// +// Keys.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 25/07/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +struct Keys { + static let Escape = 53 + static let Space = 49 +} diff --git a/phpmon/Domain/Preferences/PrefsWC+Hotkey.swift b/phpmon/Domain/Preferences/PrefsWC+Hotkey.swift new file mode 100644 index 0000000..0653d05 --- /dev/null +++ b/phpmon/Domain/Preferences/PrefsWC+Hotkey.swift @@ -0,0 +1,38 @@ +// +// PrefsWC+Hotkey.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 25/07/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Cocoa + +extension PrefsWC { + + // MARK: - Key Interaction + + override func keyDown(with event: NSEvent) { + super.keyDown(with: event) + + guard let tabVC = self.contentViewController as? NSTabViewController else { + return + } + + guard let vc = tabVC.tabViewItems[tabVC.selectedTabViewItemIndex].viewController as? GenericPreferenceVC else { + return + } + + if vc.listeningForHotkeyView == nil { + return + } + + if event.keyCode == Keys.Escape || event.keyCode == Keys.Space { + Log.info("A blacklisted key was pressed, canceling listen!") + vc.listeningForHotkeyView!.unregister(nil) + } else { + vc.listeningForHotkeyView!.updateShortcut(event) + } + } + +} diff --git a/phpmon/Domain/Preferences/PrefsWC.swift b/phpmon/Domain/Preferences/PrefsWC.swift index dfd080b..f9f1b64 100644 --- a/phpmon/Domain/Preferences/PrefsWC.swift +++ b/phpmon/Domain/Preferences/PrefsWC.swift @@ -8,11 +8,6 @@ import Cocoa -struct Keys { - static let Escape = 53 - static let Space = 49 -} - class PrefsWC: PMWindowController { // MARK: - Window Identifier @@ -54,7 +49,7 @@ class PrefsWC: PMWindowController { for vc in preferencesWC.tabVCs { tabVC.addChild(vc.viewController) let item = tabVC.tabViewItem(for: vc.viewController) - item?.image = NSImage(systemSymbolName: vc.icon, accessibilityDescription: "") + item?.image = NSImage(systemSymbolName: vc.icon, accessibilityDescription: "\(vc.label) Icon") item?.label = vc.label } @@ -75,6 +70,8 @@ class PrefsWC: PMWindowController { NSApp.activate(ignoringOtherApps: true) } + // MARK: - Tabs + struct PrefTabView { let viewController: GenericPreferenceVC let label: String @@ -101,29 +98,4 @@ class PrefsWC: PMWindowController { ] }() - // MARK: - Key Interaction - - override func keyDown(with event: NSEvent) { - super.keyDown(with: event) - - 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!.unregister(nil) - } else { - vc.listeningForHotkeyView!.updateShortcut(event) - } - } - } - } - } From fa3ec2aaa330d5a10603cacc4b53086cfb338d7c Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 25 Jul 2022 21:11:24 +0200 Subject: [PATCH 09/43] =?UTF-8?q?=F0=9F=8F=97=20WIP:=20Present=20Onboardin?= =?UTF-8?q?gWC=20if=20launch=20count=20>=3D=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 4 ++ phpmon/Domain/App/App.swift | 3 ++ phpmon/Domain/App/Base.lproj/Main.storyboard | 22 +++++++++ phpmon/Domain/Menu/MainMenu+Startup.swift | 8 ++++ .../SwiftUI/Onboarding/OnboardingView.swift | 2 +- .../SwiftUI/Onboarding/OnboardingWC.swift | 47 +++++++++++++++++++ phpmon/Localizable.strings | 1 + 7 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 phpmon/Domain/SwiftUI/Onboarding/OnboardingWC.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 18644f8..8e58fe3 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -264,6 +264,7 @@ C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; }; C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; }; C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; }; + C4FACE83288F1F9700FC478F /* OnboardingWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE82288F1F9700FC478F /* OnboardingWC.swift */; }; C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; }; C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F361602836BFD9003598CC /* MainMenu+Actions.swift */; }; C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; }; @@ -440,6 +441,7 @@ C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtensionTest.swift; sourceTree = ""; }; C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = ""; }; C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + C4FACE82288F1F9700FC478F /* OnboardingWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingWC.swift; sourceTree = ""; }; C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionDetectionTest.swift; sourceTree = ""; }; C4FE011028084FC200D1DE6D /* SelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionVC.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1011,6 +1013,7 @@ isa = PBXGroup; children = ( C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */, + C4FACE82288F1F9700FC478F /* OnboardingWC.swift */, ); path = Onboarding; sourceTree = ""; @@ -1308,6 +1311,7 @@ C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */, C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */, C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */, + C4FACE83288F1F9700FC478F /* OnboardingWC.swift in Sources */, C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */, C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */, 54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */, diff --git a/phpmon/Domain/App/App.swift b/phpmon/Domain/App/App.swift index fcbee6f..7615701 100644 --- a/phpmon/Domain/App/App.swift +++ b/phpmon/Domain/App/App.swift @@ -56,6 +56,9 @@ class App { /** The window controller of the currently active site list window. */ var domainListWindowController: DomainListWC? + /** The window controller of the onboarding window. */ + var onboardingWindowController: OnboardingWC? + /** List of detected (installed) applications that PHP Monitor can work with. */ var detectedApplications: [Application] = [] diff --git a/phpmon/Domain/App/Base.lproj/Main.storyboard b/phpmon/Domain/App/Base.lproj/Main.storyboard index 679485d..3990b72 100644 --- a/phpmon/Domain/App/Base.lproj/Main.storyboard +++ b/phpmon/Domain/App/Base.lproj/Main.storyboard @@ -397,6 +397,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 9258b3a..d38e4b1 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -88,6 +88,14 @@ extension MainMenu { Stats.incrementSuccessfulLaunchCount() Stats.evaluateSponsorMessageShouldBeDisplayed() + // Present first launch screen if needed + if Stats.successfulLaunchCount >= 1 { // TODO: Make this == 1 for release + Log.info("Should present the first launch screen!") + DispatchQueue.main.async { + OnboardingWC.show() + } + } + // Check for updates DispatchQueue.global(qos: .utility).async { AppUpdateChecker.checkIfNewerVersionIsAvailable() diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift index 2756965..9711ac2 100644 --- a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift @@ -51,7 +51,7 @@ struct OnboardingView: View { .foregroundColor(.gray) .padding(.top, 5) Button("Close Tour") { - + App.shared.onboardingWindowController?.close() } } .frame(maxWidth: .infinity) diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingWC.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingWC.swift new file mode 100644 index 0000000..22529cb --- /dev/null +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingWC.swift @@ -0,0 +1,47 @@ +// +// OnboardingWC.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 25/06/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Cocoa +import SwiftUI + +class OnboardingWC: PMWindowController { + + // MARK: - Window Identifier + + override var windowName: String { + return "Onboarding" + } + + public static func create(delegate: NSWindowDelegate?) { + let storyboard = NSStoryboard(name: "Main", bundle: nil) + + let windowController = storyboard.instantiateController( + withIdentifier: "onboardingWindow" + ) as! OnboardingWC + + windowController.window!.title = "onboarding.title".localized + windowController.window!.delegate = delegate + windowController.window!.styleMask = [.titled, .closable, .miniaturizable] + windowController.window!.delegate = windowController + windowController.window!.contentView = NSHostingView(rootView: OnboardingView()) + windowController.window!.setContentSize(NSSize(width: 600, height: 600)) + + App.shared.onboardingWindowController = windowController + } + + public static func show(delegate: NSWindowDelegate? = nil) { + if App.shared.onboardingWindowController == nil { + Self.create(delegate: delegate) + } + + App.shared.onboardingWindowController?.showWindow(self) + App.shared.onboardingWindowController?.window?.setCenterPosition(offsetY: 70) + + NSApp.activate(ignoringOtherApps: true) + } +} diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 1bcc5c5..834939e 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -500,6 +500,7 @@ You can do this by running `composer global update` in your terminal. After that // ONBOARDING +"onboarding.title" = ""; "onboarding.welcome" = "Welcome to PHP Monitor!"; "onboarding.explore" = "Explore some of the features below, or close the tour and get started."; "onboarding.tour.menu_bar.title" = "Get Started"; From 418d1e24791111d2e72dda2d54fffad9e5b2ee8b Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 25 Jul 2022 21:40:02 +0200 Subject: [PATCH 10/43] =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20custom?= =?UTF-8?q?=20environment=20vars=20(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds support for custom environment variables, which can be set in PHP Monitor's custom configuration file. Let's say you wish to customize the `COMPOSER_HOME` env variable, like in #183. You can fix this like so: ```json { "scan_apps": [], "services": [], "presets": [] "export": { "COMPOSER_HOME": "/absolute/path/to/composer/folder" } } ``` Please note that while it is possible to set the `PATH` this way, you WILL MOST CERTAINLY break PHP Monitor in the process. Setting other environment variables should generally not pose an issue, unless said environment variable affects the output of the shell that PHP Monitor uses internally. --- phpmon/Common/Core/Shell.swift | 21 +++++++++++++++++---- phpmon/Domain/Preferences/CustomPrefs.swift | 12 ++++++++++++ phpmon/Domain/Preferences/Preferences.swift | 7 ++++++- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/phpmon/Common/Core/Shell.swift b/phpmon/Common/Core/Shell.swift index 3258e8d..132309e 100644 --- a/phpmon/Common/Core/Shell.swift +++ b/phpmon/Common/Core/Shell.swift @@ -32,6 +32,9 @@ public class Shell { */ public var shell: String = "/bin/sh" + /** Additional exports that are sent if `requiresPath` is set to true. */ + public var exports: String = "" + /** Singleton to access a user shell (with --login) */ @@ -114,13 +117,23 @@ public class Shell { Creates a new process with the correct PATH and shell. */ public func createTask(for command: String, requiresPath: Bool) -> Process { - let tailoredCommand = requiresPath - ? "export PATH=\(Paths.binPath):$PATH && \(command)" - : command + var completeCommand = "" + + if requiresPath { + // Basic export (PATH) + completeCommand += "export PATH=\(Paths.binPath):$PATH && " + + // Put additional exports in between + if !self.exports.isEmpty { + completeCommand += "\(self.exports) && " + } + } + + completeCommand += command let task = Process() task.launchPath = self.shell - task.arguments = ["--noprofile", "-norc", "--login", "-c", tailoredCommand] + task.arguments = ["--noprofile", "-norc", "--login", "-c", completeCommand] return task } diff --git a/phpmon/Domain/Preferences/CustomPrefs.swift b/phpmon/Domain/Preferences/CustomPrefs.swift index 6706f2e..6f8cec4 100644 --- a/phpmon/Domain/Preferences/CustomPrefs.swift +++ b/phpmon/Domain/Preferences/CustomPrefs.swift @@ -12,6 +12,7 @@ struct CustomPrefs: Decodable { let scanApps: [String] let presets: [Preset]? let services: [String]? + let environmentVariables: [String: String]? public func hasPresets() -> Bool { return self.presets != nil && !self.presets!.isEmpty @@ -21,9 +22,20 @@ struct CustomPrefs: Decodable { return self.services != nil && !self.services!.isEmpty } + public func hasEnvironmentVariables() -> Bool { + return self.environmentVariables != nil && !self.environmentVariables!.keys.isEmpty + } + + public func getEnvironmentVariables() -> String { + return self.environmentVariables!.map { (key, value) in + return "export \(key)=\(value)" + }.joined(separator: "&&") + } + private enum CodingKeys: String, CodingKey { case scanApps = "scan_apps" case presets = "presets" case services = "services" + case environmentVariables = "export" } } diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index bc47a99..5ecaaa0 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: [], services: []) + customPreferences = CustomPrefs(scanApps: [], presets: [], services: [], environmentVariables: [:]) loadCustomPreferences() } @@ -252,6 +252,11 @@ class Preferences { if customPreferences.hasServices() { Log.info("There are custom services: \(customPreferences.services!)") } + + if customPreferences.hasEnvironmentVariables() { + Log.info("Configuring the additional exports...") + Shell.user.exports = customPreferences.getEnvironmentVariables() + } } catch { Log.warn("The ~/.config/phpmon/config.json file seems to be missing or malformed.") } From 0a55b45c60c2d370a88d422b94f48693c45d7952 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 25 Jul 2022 21:40:43 +0200 Subject: [PATCH 11/43] =?UTF-8?q?=F0=9F=94=A7=20Prepare=205.5=20branch=20f?= =?UTF-8?q?or=20dev=20builds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index c15f01f..4dceeb3 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -1599,13 +1599,13 @@ C41C1B4422B0098000E7CF16 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor; CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 912; + CURRENT_PROJECT_VERSION = 920; DEBUG = YES; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; @@ -1615,7 +1615,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 5.4.1; + MARKETING_VERSION = 5.5.0; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1626,13 +1626,13 @@ C41C1B4522B0098000E7CF16 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor; CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 912; + CURRENT_PROJECT_VERSION = 920; DEBUG = NO; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; @@ -1642,7 +1642,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 5.4.1; + MARKETING_VERSION = 5.5.0; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From f098ffbf3d28f5192f06b3f3b7fa97f7d4907ef6 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 26 Jul 2022 19:30:35 +0200 Subject: [PATCH 12/43] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Consistency=20in=20n?= =?UTF-8?q?aming=20of=20window=20controllers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 28 ++++++++++++++----- .../Composer/ComposerWindow.swift | 4 +-- .../Onboarding/OnboardingWC.swift | 0 phpmon/Domain/Progress/ProgressVC.swift | 24 ++++++++++++++++ ...{ProgressWindow.swift => ProgressWC.swift} | 20 ++----------- .../Domain/Progress/ProgressWindow.storyboard | 13 ++++----- 6 files changed, 56 insertions(+), 33 deletions(-) rename phpmon/Domain/{SwiftUI => }/Onboarding/OnboardingWC.swift (100%) create mode 100644 phpmon/Domain/Progress/ProgressVC.swift rename phpmon/Domain/Progress/{ProgressWindow.swift => ProgressWC.swift} (77%) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 8e58fe3..9ac8c06 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -114,8 +114,10 @@ C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; }; C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; }; C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; }; - C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; }; - C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; }; + C44A874828905BB000498BC4 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; }; + C44A874928905BB000498BC4 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; }; + C44C198D276E3A1C0072762D /* ProgressWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWC.swift */; }; + C44C198E276E3A1C0072762D /* ProgressWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWC.swift */; }; C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; }; C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; }; C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; }; @@ -352,7 +354,8 @@ C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTLSCell.swift; sourceTree = ""; }; C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIHelper.swift; sourceTree = ""; }; C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionPopoverView.swift; sourceTree = ""; }; - C44C198C276E3A1C0072762D /* ProgressWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWindow.swift; sourceTree = ""; }; + C44A874728905BB000498BC4 /* ProgressVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressVC.swift; sourceTree = ""; }; + C44C198C276E3A1C0072762D /* ProgressWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWC.swift; sourceTree = ""; }; C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = ""; }; C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = ""; }; C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = ""; }; @@ -649,6 +652,7 @@ C4D9ADBD27761084007277F4 /* PHP */, C47331A0247093AC009A0597 /* Menu */, C464ADAA275A7A25003FCD53 /* DomainList */, + C44A874628905B8500498BC4 /* Onboarding */, 5420395726135DB800FB00FA /* Preferences */, C44C198F276E3A380072762D /* Progress */, C4C8E81D276F5686003AC782 /* Watcher */, @@ -679,10 +683,19 @@ path = Cells; sourceTree = ""; }; + C44A874628905B8500498BC4 /* Onboarding */ = { + isa = PBXGroup; + children = ( + C4FACE82288F1F9700FC478F /* OnboardingWC.swift */, + ); + path = Onboarding; + sourceTree = ""; + }; C44C198F276E3A380072762D /* Progress */ = { isa = PBXGroup; children = ( - C44C198C276E3A1C0072762D /* ProgressWindow.swift */, + C44C198C276E3A1C0072762D /* ProgressWC.swift */, + C44A874728905BB000498BC4 /* ProgressVC.swift */, C44C1990276E44CB0072762D /* ProgressWindow.storyboard */, ); path = Progress; @@ -1013,7 +1026,6 @@ isa = PBXGroup; children = ( C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */, - C4FACE82288F1F9700FC478F /* OnboardingWC.swift */, ); path = Onboarding; sourceTree = ""; @@ -1246,6 +1258,7 @@ C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */, C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */, C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */, + C44A874828905BB000498BC4 /* ProgressVC.swift in Sources */, C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */, C4B585442770FE3900DA4FBE /* Command.swift in Sources */, C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */, @@ -1257,7 +1270,7 @@ C415D3B72770F294005EF286 /* Actions.swift in Sources */, C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */, C4F361612836BFD9003598CC /* MainMenu+Actions.swift in Sources */, - C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */, + C44C198D276E3A1C0072762D /* ProgressWC.swift in Sources */, 54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */, C4C0E8E727F88B41002D32A9 /* ProxyScanner.swift in Sources */, C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */, @@ -1350,6 +1363,7 @@ C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */, C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */, C493084B279F331F009C240B /* AddSiteVC.swift in Sources */, + C44A874928905BB000498BC4 /* ProgressVC.swift in Sources */, C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */, C41C02AA27E61CA3009F26CB /* SiteScanner.swift in Sources */, C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */, @@ -1421,7 +1435,7 @@ C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */, C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */, C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */, - C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */, + C44C198E276E3A1C0072762D /* ProgressWC.swift in Sources */, C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */, C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */, C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */, diff --git a/phpmon/Domain/Integrations/Composer/ComposerWindow.swift b/phpmon/Domain/Integrations/Composer/ComposerWindow.swift index 9f9f084..e25adfb 100644 --- a/phpmon/Domain/Integrations/Composer/ComposerWindow.swift +++ b/phpmon/Domain/Integrations/Composer/ComposerWindow.swift @@ -13,7 +13,7 @@ class ComposerWindow { private var menu: MainMenu? private var shouldNotify: Bool! = nil private var completion: ((Bool) -> Void)! = nil - private var window: ProgressWindowController? + private var window: ProgressWC? /** Updates the global dependencies and runs the completion callback when done. @@ -35,7 +35,7 @@ class ComposerWindow { menu?.setBusyImage() menu?.rebuild() - window = ProgressWindowController.display( + window = ProgressWC.display( title: "alert.composer_progress.title".localized, description: "alert.composer_progress.info".localized ) diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingWC.swift b/phpmon/Domain/Onboarding/OnboardingWC.swift similarity index 100% rename from phpmon/Domain/SwiftUI/Onboarding/OnboardingWC.swift rename to phpmon/Domain/Onboarding/OnboardingWC.swift diff --git a/phpmon/Domain/Progress/ProgressVC.swift b/phpmon/Domain/Progress/ProgressVC.swift new file mode 100644 index 0000000..2b77bb2 --- /dev/null +++ b/phpmon/Domain/Progress/ProgressVC.swift @@ -0,0 +1,24 @@ +// +// ProgressVC.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 26/07/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation +import AppKit + +class ProgressViewController: NSViewController { + + @IBOutlet weak var labelTitle: NSTextField! + @IBOutlet weak var labelDescription: NSTextField! + + @IBOutlet var textView: NSTextView! + @IBOutlet weak var imageViewType: NSImageView! + + deinit { + Log.perf("Deinitializing ProgressViewController") + } + +} diff --git a/phpmon/Domain/Progress/ProgressWindow.swift b/phpmon/Domain/Progress/ProgressWC.swift similarity index 77% rename from phpmon/Domain/Progress/ProgressWindow.swift rename to phpmon/Domain/Progress/ProgressWC.swift index 3cf5388..0138a54 100644 --- a/phpmon/Domain/Progress/ProgressWindow.swift +++ b/phpmon/Domain/Progress/ProgressWC.swift @@ -9,14 +9,14 @@ import Foundation import AppKit -class ProgressWindowController: NSWindowController, NSWindowDelegate { +class ProgressWC: NSWindowController, NSWindowDelegate { - static func display(title: String, description: String) -> ProgressWindowController { + static func display(title: String, description: String) -> ProgressWC { let storyboard = NSStoryboard(name: "ProgressWindow", bundle: nil) let windowController = storyboard.instantiateController( withIdentifier: "progressWindow" - ) as! ProgressWindowController + ) as! ProgressWC windowController.showWindow(windowController) windowController.window?.makeKeyAndOrderFront(nil) @@ -60,17 +60,3 @@ class ProgressWindowController: NSWindowController, NSWindowDelegate { } } - -class ProgressViewController: NSViewController { - - @IBOutlet weak var labelTitle: NSTextField! - @IBOutlet weak var labelDescription: NSTextField! - - @IBOutlet var textView: NSTextView! - @IBOutlet weak var imageViewType: NSImageView! - - deinit { - Log.perf("Deinitializing ProgressViewController") - } - -} diff --git a/phpmon/Domain/Progress/ProgressWindow.storyboard b/phpmon/Domain/Progress/ProgressWindow.storyboard index 05cd210..ef716b7 100644 --- a/phpmon/Domain/Progress/ProgressWindow.storyboard +++ b/phpmon/Domain/Progress/ProgressWindow.storyboard @@ -1,15 +1,14 @@ - + - - + - + @@ -43,7 +42,7 @@ - + @@ -65,11 +64,11 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/phpmon/Domain/Onboarding/OnboardingWC.swift b/phpmon/Domain/Onboarding/OnboardingWC.swift index 22529cb..57477b7 100644 --- a/phpmon/Domain/Onboarding/OnboardingWC.swift +++ b/phpmon/Domain/Onboarding/OnboardingWC.swift @@ -18,12 +18,8 @@ class OnboardingWC: PMWindowController { } public static func create(delegate: NSWindowDelegate?) { - let storyboard = NSStoryboard(name: "Main", bundle: nil) - - let windowController = storyboard.instantiateController( - withIdentifier: "onboardingWindow" - ) as! OnboardingWC - + let windowController = OnboardingWC() + windowController.window = NSWindow() windowController.window!.title = "onboarding.title".localized windowController.window!.delegate = delegate windowController.window!.styleMask = [.titled, .closable, .miniaturizable] From 8cb8e5e4092acae070dcfa9188ca493e2e91b9ae Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 26 Jul 2022 20:30:19 +0200 Subject: [PATCH 15/43] =?UTF-8?q?=F0=9F=8D=B1=20Visual=20changes=20to=20on?= =?UTF-8?q?boarding=20screen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/Extensions/StringExtension.swift | 5 ++ phpmon/Domain/Onboarding/OnboardingWC.swift | 14 +++-- .../SwiftUI/Onboarding/OnboardingView.swift | 58 ++++++++++++++----- phpmon/Localizable.strings | 5 +- 4 files changed, 59 insertions(+), 23 deletions(-) diff --git a/phpmon/Common/Extensions/StringExtension.swift b/phpmon/Common/Extensions/StringExtension.swift index 897832d..a8746c1 100644 --- a/phpmon/Common/Extensions/StringExtension.swift +++ b/phpmon/Common/Extensions/StringExtension.swift @@ -5,6 +5,7 @@ // Copyright © 2022 Nico Verbruggen. All rights reserved. // import Foundation +import SwiftUI extension String { var localized: String { @@ -17,6 +18,10 @@ extension String { return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") } + var localizedForSwiftUI: LocalizedStringKey { + return LocalizedStringKey(self.localized) + } + func localized(_ args: CVarArg...) -> String { String(format: self.localized, arguments: args) } diff --git a/phpmon/Domain/Onboarding/OnboardingWC.swift b/phpmon/Domain/Onboarding/OnboardingWC.swift index 57477b7..6696c75 100644 --- a/phpmon/Domain/Onboarding/OnboardingWC.swift +++ b/phpmon/Domain/Onboarding/OnboardingWC.swift @@ -20,12 +20,14 @@ class OnboardingWC: PMWindowController { public static func create(delegate: NSWindowDelegate?) { let windowController = OnboardingWC() windowController.window = NSWindow() - windowController.window!.title = "onboarding.title".localized - windowController.window!.delegate = delegate - windowController.window!.styleMask = [.titled, .closable, .miniaturizable] - windowController.window!.delegate = windowController - windowController.window!.contentView = NSHostingView(rootView: OnboardingView()) - windowController.window!.setContentSize(NSSize(width: 600, height: 600)) + + 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: OnboardingView()) + window.setContentSize(NSSize(width: 600, height: 600)) App.shared.onboardingWindowController = windowController } diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift index 9711ac2..0ab42e0 100644 --- a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift @@ -10,25 +10,34 @@ import SwiftUI struct OnboardingView: View { var body: some View { - VStack(alignment: .center) { - Image(nsImage: NSApp.applicationIconImage) - .resizable() - .frame(width: 90, height: 90) - Text("onboarding.welcome".localized) - .font(.title) - .bold() - .padding(.bottom, 5) - Text("onboarding.explore".localized) - .padding(.bottom) + VStack { + VStack(alignment: .leading) { + HStack { + Image(nsImage: NSApp.applicationIconImage) + .resizable() + .frame(width: 80, height: 80) + .padding(.bottom, 5) + .padding(.trailing, 25) + VStack(alignment: .leading, spacing: 0) { + Text("onboarding.welcome".localized) + .font(.title) + .bold() + .padding(.bottom, 5) + Text("onboarding.explore".localized) + .padding(.bottom) + }.padding(.top, 10) + } + TabView { VStack { Image("Tour.MenuBar") .resizable() .aspectRatio(contentMode: .fit) .padding(.top) - Text("onboarding.tour.menu_bar".localized) + Text("onboarding.tour.menu_bar".localizedForSwiftUI) .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) - }.tabItem { Label("onboarding.tour.menu_bar.title".localized, systemImage: "") } + }.tabItem { Label("onboarding.tour.menu_bar.title".localized, + systemImage: "info.circle.fill") } VStack { Image("Tour.Domains") .resizable() @@ -36,7 +45,8 @@ struct OnboardingView: View { .padding(.top) Text("onboarding.tour.domains".localized) .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) - }.tabItem { Label("onboarding.tour.domains.title".localized, systemImage: "") } + }.tabItem { Label("onboarding.tour.domains.title".localized, + systemImage: "info.circle.fill") } VStack { Image("Tour.Isolation") .resizable() @@ -44,18 +54,36 @@ struct OnboardingView: View { .padding(.top) Text("onboarding.tour.isolation".localized) .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) - }.tabItem { Label("onboarding.tour.isolation.title".localized, systemImage: "") } + }.tabItem { Label("onboarding.tour.isolation.title".localized, + systemImage: "info.circle.fill") } } + } + .frame(maxWidth: .infinity) + + VStack(alignment: .center) { + HStack { + Image(systemName: "person.fill.questionmark") + .resizable() + .frame(width: 24, height: 24) + .foregroundColor(.appSecondary) + .padding(.trailing, 16) + Text("onboarding.tour.faq_hint".localizedForSwiftUI) + }.padding() Text("onboarding.tour.once".localized) .font(.subheadline) .foregroundColor(.gray) .padding(.top, 5) + .padding(.bottom, 5) Button("Close Tour") { App.shared.onboardingWindowController?.close() } } .frame(maxWidth: .infinity) - .padding() + } + .padding(.top, 8) + .padding(.leading) + .padding(.trailing) + .padding(.bottom) } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 834939e..794eb52 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -500,11 +500,12 @@ You can do this by running `composer global update` in your terminal. After that // ONBOARDING -"onboarding.title" = ""; +"onboarding.title" = "Welcome Tour"; "onboarding.welcome" = "Welcome to PHP Monitor!"; -"onboarding.explore" = "Explore some of the features below, or close the tour and get started."; +"onboarding.explore" = "Explore some of the cool features PHP Monitor has to offer."; "onboarding.tour.menu_bar.title" = "Get Started"; "onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From here, you can switch the globally linked PHP version, start or stop services, locate configuration files, and more."; +"onboarding.tour.faq_hint" = "**If you are new to PHP Monitor**, I recommend that you check out the [README](https://github.com/nicoverbruggen/phpmon/blob/main/README.md) on GitHub: it contains a comprehensive FAQ with various tips and common questions and answers. Don't hesitate to get in touch either."; "onboarding.tour.domains.title" = "Domains"; "onboarding.tour.domains" = "By opening the Domains window via the Menu Bar item, you can view which domains are linked and parked."; "onboarding.tour.isolation.title" = "Isolation"; From 08fdcdbc6c5f395dba66dbff0f11d8f9416ed31c Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 27 Jul 2022 20:38:39 +0200 Subject: [PATCH 16/43] =?UTF-8?q?=F0=9F=91=8C=20Simple=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/DomainList/DomainListVC.swift | 17 ++++++++--------- phpmon/Domain/DomainList/DomainListWC.swift | 2 -- phpmon/Domain/Preferences/PrefsWC.swift | 11 ++++++----- .../SwiftUI/Onboarding/OnboardingView.swift | 4 ++-- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/phpmon/Domain/DomainList/DomainListVC.swift b/phpmon/Domain/DomainList/DomainListVC.swift index 9d8925d..e33c01d 100644 --- a/phpmon/Domain/DomainList/DomainListVC.swift +++ b/phpmon/Domain/DomainList/DomainListVC.swift @@ -66,15 +66,14 @@ class DomainListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource withIdentifier: "domainListWindow" ) as! DomainListWC - windowController.window!.title = "domain_list.title".localized - windowController.window!.subtitle = "domain_list.subtitle".localized - windowController.window!.delegate = delegate - windowController.window!.styleMask = [ - .titled, .closable, .resizable, .miniaturizable - ] - windowController.window!.minSize = NSSize(width: 550, height: 200) - windowController.window!.delegate = windowController - windowController.window!.setFrameAutosaveName("domainListWindow") + guard let window = windowController.window else { return } + + window.title = "domain_list.title".localized + window.subtitle = "domain_list.subtitle".localized + window.delegate = delegate ?? windowController + window.styleMask = [.titled, .closable, .resizable, .miniaturizable] + window.minSize = NSSize(width: 550, height: 200) + window.setFrameAutosaveName("domainListWindow") App.shared.domainListWindowController = windowController } diff --git a/phpmon/Domain/DomainList/DomainListWC.swift b/phpmon/Domain/DomainList/DomainListWC.swift index 6917680..e8d4efa 100644 --- a/phpmon/Domain/DomainList/DomainListWC.swift +++ b/phpmon/Domain/DomainList/DomainListWC.swift @@ -124,8 +124,6 @@ class DomainListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate withIdentifier: "addProxyWindow" ) as! NSWindowController - // let viewController = windowController.window!.contentViewController as! AddSiteVC - self.window?.beginSheet(windowController.window!) } } diff --git a/phpmon/Domain/Preferences/PrefsWC.swift b/phpmon/Domain/Preferences/PrefsWC.swift index f9f1b64..b2567fe 100644 --- a/phpmon/Domain/Preferences/PrefsWC.swift +++ b/phpmon/Domain/Preferences/PrefsWC.swift @@ -23,11 +23,12 @@ class PrefsWC: PMWindowController { withIdentifier: "preferencesWindow" ) as! PrefsWC - windowController.window!.title = "prefs.title".localized - windowController.window!.subtitle = "prefs.subtitle".localized - windowController.window!.delegate = delegate - windowController.window!.styleMask = [.titled, .closable, .miniaturizable] - windowController.window!.delegate = windowController + guard let window = windowController.window else { return } + + window.title = "prefs.title".localized + window.subtitle = "prefs.subtitle".localized + window.delegate = delegate ?? windowController + window.styleMask = [.titled, .closable, .miniaturizable] App.shared.preferencesWindowController = windowController } diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift index 0ab42e0..c00b636 100644 --- a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift @@ -65,8 +65,8 @@ struct OnboardingView: View { Image(systemName: "person.fill.questionmark") .resizable() .frame(width: 24, height: 24) - .foregroundColor(.appSecondary) - .padding(.trailing, 16) + .foregroundColor(.accentColor) + .padding(.trailing, 10) Text("onboarding.tour.faq_hint".localizedForSwiftUI) }.padding() Text("onboarding.tour.once".localized) From 2306529936e8bc2732334b97ce3fbcd05e7010dd Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Thu, 28 Jul 2022 21:26:41 +0200 Subject: [PATCH 17/43] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Differentiate=20betw?= =?UTF-8?q?een=20folder=20and=20file=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also fixes #182, because it introduces a check for Valet's config directory, which does not exist if Valet has not been installed yet. --- phpmon/Common/Helpers/Filesystem.swift | 34 ++++++++++++++++--- phpmon/Domain/App/Startup.swift | 14 +++++++- phpmon/Domain/DomainList/AddSiteVC.swift | 2 +- .../Integrations/Composer/PhpFrameworks.swift | 2 +- phpmon/Domain/Watcher/PhpConfigWatcher.swift | 2 +- phpmon/Localizable.strings | 6 ++++ 6 files changed, 52 insertions(+), 8 deletions(-) diff --git a/phpmon/Common/Helpers/Filesystem.swift b/phpmon/Common/Helpers/Filesystem.swift index 129763c..a54f665 100644 --- a/phpmon/Common/Helpers/Filesystem.swift +++ b/phpmon/Common/Helpers/Filesystem.swift @@ -1,5 +1,5 @@ // -// FileSystem.swift +// Filesystem.swift // PHP Monitor // // Created by Nico Verbruggen on 07/12/2021. @@ -7,17 +7,43 @@ // import Cocoa +import Foundation class Filesystem { /** - Checks if a file exists at the provided path. - Uses `FileManager`. + Checks if a file or directory exists at the provided path. */ - public static func fileExists(_ path: String) -> Bool { + public static func exists(_ path: String) -> Bool { return FileManager.default.fileExists( atPath: path.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)") ) } + /** + Checks if a file exists at the provided path. + */ + public static func fileExists(_ path: String) -> Bool { + var isDirectory: ObjCBool = true + let exists = FileManager.default.fileExists( + atPath: path.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)"), + isDirectory: &isDirectory + ) + + return exists && !isDirectory.boolValue + } + + /** + Checks if a directory exists at the provided path. + */ + public static func directoryExists(_ path: String) -> Bool { + var isDirectory: ObjCBool = true + let exists = FileManager.default.fileExists( + atPath: path.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)"), + isDirectory: &isDirectory + ) + + return exists && isDirectory.boolValue + } + } diff --git a/phpmon/Domain/App/Startup.swift b/phpmon/Domain/App/Startup.swift index 38ebf9b..08245cc 100644 --- a/phpmon/Domain/App/Startup.swift +++ b/phpmon/Domain/App/Startup.swift @@ -167,6 +167,18 @@ class Startup { descriptionText: "startup.errors.services_json_error.desc".localized ), // ================================================================================= + // Determine that Valet is installed + // ================================================================================= + EnvironmentCheck( + command: { + return !Filesystem.directoryExists("~/.config/valet") + }, + name: "`.config/valet` not empty (Valet installed)", + titleText: "startup.errors.valet_not_installed.title".localized, + subtitleText: "startup.errors.valet_not_installed.subtitle".localized, + descriptionText: "startup.errors.valet_not_installed.desc".localized + ), + // ================================================================================= // Determine that the Valet configuration JSON file is valid. // ================================================================================= EnvironmentCheck( @@ -224,7 +236,7 @@ class Startup { titleText: "startup.errors.valet_version_unknown.title".localized, subtitleText: "startup.errors.valet_version_unknown.subtitle".localized, descriptionText: "startup.errors.valet_version_unknown.desc".localized - ) + ), ] // MARK: - EnvironmentCheck struct diff --git a/phpmon/Domain/DomainList/AddSiteVC.swift b/phpmon/Domain/DomainList/AddSiteVC.swift index 43c2603..7a0193e 100644 --- a/phpmon/Domain/DomainList/AddSiteVC.swift +++ b/phpmon/Domain/DomainList/AddSiteVC.swift @@ -55,7 +55,7 @@ class AddSiteVC: NSViewController, NSTextFieldDelegate { let path = pathControl.url!.path let name = inputDomainName.stringValue - if !FileManager.default.fileExists(atPath: path) { + if !Filesystem.exists(path) { Alert.confirm( onWindow: view.window!, messageText: "domain_list.alert.folder_missing.title".localized, diff --git a/phpmon/Domain/Integrations/Composer/PhpFrameworks.swift b/phpmon/Domain/Integrations/Composer/PhpFrameworks.swift index fa72d92..95fc0d0 100644 --- a/phpmon/Domain/Integrations/Composer/PhpFrameworks.swift +++ b/phpmon/Domain/Integrations/Composer/PhpFrameworks.swift @@ -71,7 +71,7 @@ struct PhpFrameworks { public static func detectFallbackDependency(_ basePath: String) -> String? { for entry in Self.FileMapping { let found = entry.value - .map { path in return Filesystem.fileExists(basePath + path) } + .map { path in return Filesystem.exists(basePath + path) } .contains(true) if found { diff --git a/phpmon/Domain/Watcher/PhpConfigWatcher.swift b/phpmon/Domain/Watcher/PhpConfigWatcher.swift index b3ad98d..b1b1e45 100644 --- a/phpmon/Domain/Watcher/PhpConfigWatcher.swift +++ b/phpmon/Domain/Watcher/PhpConfigWatcher.swift @@ -51,7 +51,7 @@ class PhpConfigWatcher { eventMask: DispatchSource.FileSystemEvent, behaviour: FSWatcherBehaviour = .reloadsMenu ) { - if !Filesystem.fileExists(url.path) { + if !Filesystem.exists(url.path) { Log.warn("No watcher was created for \(url.path) because the requested file does not exist.") return } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 794eb52..85f8594 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -435,6 +435,12 @@ You can do this by running `composer global update` in your terminal. After that "startup.errors.valet_version_unknown.subtitle" = "Parsing the output of `valet --version` failed. Make sure your Valet installation works and is up-to-date."; "startup.errors.valet_version_unknown.desc" = "Try running `valet --version` in a terminal to find out what's going on."; +"startup.errors.valet_not_installed.title" = "Your Valet configuration directory is missing"; +"startup.errors.valet_not_installed.subtitle" = "The required directory `~/.config/valet` is missing. This usually means that you forgot to run `valet install`."; +"startup.errors.valet_not_installed.desc" = "Assuming you already installed Valet via Composer, please run `valet install` to finish setting up Laravel Valet. + +If you are seeing this message but are confused why this folder has gone missing, then you may want to investigate why it is gone—it shouldn't just disappear and it means your Valet installation is broken."; + /// Brew & sudoers "startup.errors.sudoers_brew.title" = "Brew has not been added to sudoers.d"; "startup.errors.sudoers_brew.subtitle" = "You must run `sudo valet trust` to ensure Valet can start and stop services without having to use sudo every time. The app will not work correctly until you resolve this issue."; From 7a580eef0cb06c64c007365f083f030dc5b89ae7 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 29 Jul 2022 18:13:10 +0200 Subject: [PATCH 18/43] =?UTF-8?q?=F0=9F=93=9D=20Update=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2a81b2f..84a9d3a 100644 --- a/README.md +++ b/README.md @@ -342,6 +342,7 @@ Here's an example of a working preset:

 {
     "scan_apps": [],
+    "services": [],
     "presets": [
         {
             "name": "Legacy Project",
@@ -355,13 +356,56 @@ Here's an example of a working preset:
                 "post_max_size": "128M"
             }
         }
-    ]
+    ],
+    "export": {}
 }
 
You can omit the `php` key in the preset if you do not wish for the preset to switch to a given PHP version. +
+How do I ensure additional Homebrew services are shown in the app? + +You must set these services up in a JSON file, located in `~/.config/phpmon/config.json`. + +You can specify custom services in the configuration file for Homebrew services that run as your own user (not root). + +You can find out which services are available by running `brew services list`. + +Here's an example where we add the `mailgun` and `mysql` services to PHP Monitor: + +
+{
+    "scan_apps": [],
+    "services": ["mailgun", "mysql"],
+    "presets": [],
+    "export": {}
+}
+
+
+ +
+How do I set custom environment variables? + +You must configure these custom environment variables up in a JSON file, located in `~/.config/phpmon/config.json`. + +PHP Monitor uses a default Shell environment, with no custom environment variables. You need to set custom environment variables manually. These are then used for e.g. Composer. + +Here's an example of a working `COMPOSER_HOME` environment variable which is respected: + +
+{
+    "scan_apps": [],
+    "services": [],
+    "presets": [],
+    "export": {
+        "COMPOSER_HOME": "/absolute/path/to/composer/folder"
+    }
+}
+
+
+
How do I get various applications to show up in the domain list's right-click menu? @@ -377,8 +421,7 @@ You can add your own apps by creating and editing a `~/.config/phpmon/config.jso
 {
-    "scan_apps": ["Xcode", "Kraken"],
-    "presets": []
+    "scan_apps": ["Xcode", "Kraken"]
 }
 
@@ -472,14 +515,14 @@ Donations really help with the Apple Developer Program cost, and keep me motivat ## 😎 Acknowledgements -While I did make this application during my own free time, PHP Monitor started out from various learning experiments during work hours at my employer, DIVE. I'd also like to shout out the following folks: +Special thanks go out to: -* My colleagues at [DIVE](https://dive.be) +* Everyone supporting me via [GitHub Sponsors](https://github.com/sponsors/nicoverbruggen) +* Everyone who has donated via [my sponsor page](https://nicoverbruggen.be/sponsor) * The [Homebrew](https://brew.sh/) team & [Valet maintainers](https://github.com/laravel/valet/graphs/contributors) * Various folks who [reached](https://twitter.com/stauffermatt) [out](https://twitter.com/marcelpociot) when PHP Monitor was still very much a small app with a handful of stars or so -* My [GitHub Sponsors](https://github.com/sponsors/nicoverbruggen) and those who have donated -* Everyone who has left feedback and reported bugs (appreciate it!) -* Everyone in the Laravel community who shared the app (thanks!) +* Everyone who has left feedback and reported bugs +* Everyone in the Laravel community who shared the app, especially on Twitter Thank you very much for your contributions, kind words and support. From 1bff75311b2ba7e6800b2c5ae6fe3644d05f3772 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 31 Jul 2022 21:27:52 +0200 Subject: [PATCH 19/43] =?UTF-8?q?=E2=9C=A8=20Add=20WarningView?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 14 ++++++- phpmon/Domain/Menu/MainMenu+Startup.swift | 2 +- .../Domain/SwiftUI/Warning/WarningView.swift | 39 +++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 phpmon/Domain/SwiftUI/Warning/WarningView.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 0ddd998..b5bf5e6 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -91,6 +91,7 @@ C42759682627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; }; C42800AA28452AA10099C999 /* StatusMenu+Items.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42800A928452AA10099C999 /* StatusMenu+Items.swift */; }; C42800AB28452AA50099C999 /* StatusMenu+Items.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42800A928452AA10099C999 /* StatusMenu+Items.swift */; }; + C4297F7A28970D59004C4630 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4297F7928970D59004C4630 /* WarningView.swift */; }; C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; }; C42CFB1627DFDE7900862737 /* nginx-site.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1527DFDE7900862737 /* nginx-site.test */; }; C42CFB1827DFDFDC00862737 /* nginx-site-isolated.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */; }; @@ -342,6 +343,7 @@ C42337A2281F19F000459A48 /* Xdebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xdebug.swift; sourceTree = ""; }; C42759662627662800093CAE /* NSMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuExtension.swift; sourceTree = ""; }; C42800A928452AA10099C999 /* StatusMenu+Items.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusMenu+Items.swift"; sourceTree = ""; }; + C4297F7928970D59004C4630 /* WarningView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningView.swift; sourceTree = ""; }; C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+FixMyValet.swift"; sourceTree = ""; }; C42CFB1527DFDE7900862737 /* nginx-site.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site.test"; sourceTree = ""; }; C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site-isolated.test"; sourceTree = ""; }; @@ -449,8 +451,8 @@ C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtensionTest.swift; sourceTree = ""; }; C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = ""; }; C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - C4FACE82288F1F9700FC478F /* OnboardingWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingWC.swift; sourceTree = ""; }; C4FACE7F288F1C0D00FC478F /* PrefsWC+Hotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrefsWC+Hotkey.swift"; sourceTree = ""; }; + C4FACE82288F1F9700FC478F /* OnboardingWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingWC.swift; sourceTree = ""; }; C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionDetectionTest.swift; sourceTree = ""; }; C4FE011028084FC200D1DE6D /* SelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionVC.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -678,6 +680,14 @@ path = Extensions; sourceTree = ""; }; + C4297F7828970D4E004C4630 /* Warning */ = { + isa = PBXGroup; + children = ( + C4297F7928970D59004C4630 /* WarningView.swift */, + ); + path = Warning; + sourceTree = ""; + }; C44067F327E256560045BD4E /* Cells */ = { isa = PBXGroup; children = ( @@ -1041,6 +1051,7 @@ C4EE55B027708BB2001DF387 /* SwiftUI */ = { isa = PBXGroup; children = ( + C4297F7828970D4E004C4630 /* Warning */, C4E9D2BE2878B32D008FFDAD /* Onboarding */, C4B609182853AAA700C95265 /* Domains */, C4B609172853AA9E00C95265 /* Menu */, @@ -1292,6 +1303,7 @@ C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */, C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */, 54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */, + C4297F7A28970D59004C4630 /* WarningView.swift in Sources */, C4C0E8E227F88B13002D32A9 /* ValetSiteScanner.swift in Sources */, C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */, 5420395F2613607600FB00FA /* Preferences.swift in Sources */, diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index c0d834d..1c2aa55 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -90,7 +90,7 @@ extension MainMenu { // Present first launch screen if needed #warning("The launch screen will be presented every time you launch the app.") - if Stats.successfulLaunchCount >= 1 { + if Stats.successfulLaunchCount >= 1 && !isRunningSwiftUIPreview { Log.info("Should present the first launch screen!") DispatchQueue.main.async { OnboardingWC.show() diff --git a/phpmon/Domain/SwiftUI/Warning/WarningView.swift b/phpmon/Domain/SwiftUI/Warning/WarningView.swift new file mode 100644 index 0000000..645c167 --- /dev/null +++ b/phpmon/Domain/SwiftUI/Warning/WarningView.swift @@ -0,0 +1,39 @@ +// +// WarningView.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 31/07/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import SwiftUI + +struct WarningView: View { + @State var title: String + @State var description: String + + var body: some View { + HStack { + Image(systemName: "exclamationmark.triangle.fill") + .resizable() + .frame(width: 25, height: 25) + .padding() + VStack(alignment: .leading) { + Text(title) + .fontWeight(.bold) + .padding(.bottom, 1) + Text(description) + .font(.body) + } + }.padding() + } +} + +struct WarningView_Previews: PreviewProvider { + static var previews: some View { + WarningView( + title: "Helpers not written", + description: "The helper files in `/usr/local/bin` could not be written because PHP Monitor does not have permission to write there." + ) + } +} From 8b6267f4116f3463a951ebb4ebef85d0856c52d9 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 1 Aug 2022 21:18:20 +0200 Subject: [PATCH 20/43] =?UTF-8?q?=F0=9F=91=8C=20Fix=20WarningView=20stylin?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Domain/SwiftUI/Warning/WarningView.swift | 18 ++++++++++++------ phpmon/Localizable.strings | 5 +++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/phpmon/Domain/SwiftUI/Warning/WarningView.swift b/phpmon/Domain/SwiftUI/Warning/WarningView.swift index 645c167..70ac712 100644 --- a/phpmon/Domain/SwiftUI/Warning/WarningView.swift +++ b/phpmon/Domain/SwiftUI/Warning/WarningView.swift @@ -18,12 +18,13 @@ struct WarningView: View { .resizable() .frame(width: 25, height: 25) .padding() - VStack(alignment: .leading) { - Text(title) + .foregroundColor(Color.orange) + VStack(alignment: .leading, spacing: 5) { + Text(title.localizedForSwiftUI) .fontWeight(.bold) - .padding(.bottom, 1) - Text(description) + Text(description.localizedForSwiftUI) .font(.body) + } }.padding() } @@ -32,8 +33,13 @@ struct WarningView: View { struct WarningView_Previews: PreviewProvider { static var previews: some View { WarningView( - title: "Helpers not written", - description: "The helper files in `/usr/local/bin` could not be written because PHP Monitor does not have permission to write there." + title: "warnings.helper_permissions_title", + description: "warnings.helper_permissions.description" ) + WarningView( + title: "warnings.helper_permissions_title", + description: "warnings.helper_permissions.description" + ) + .preferredColorScheme(.dark) } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 85f8594..56f8550 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -504,6 +504,11 @@ If you are seeing this message but are confused why this folder has gone missing "alert.warnings.tld_issue.subtitle" = "Using a non-default TLD may not work correctly and is not officially supported."; "alert.warnings.tld_issue.description" = "PHP Monitor will remain functional, but there might be issues: the app might not correctly show which domains have been secured. For optimal results, go to your Valet configuration file (config.json in the Valet directory) and change the TLD back to `test`."; +// WARNINGS + +"warnings.helper_permissions_title" = "Helpers could not be written!"; +"warnings.helper_permissions.description" = "The helper files in `/usr/local/bin` could not be written because PHP Monitor does not have permission to write there."; + // ONBOARDING "onboarding.title" = "Welcome Tour"; From a2c93833df42dc758130aec30038b2b5b2bac3b3 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 1 Aug 2022 22:39:21 +0200 Subject: [PATCH 21/43] =?UTF-8?q?=F0=9F=8D=B1=20Tweaked=20onboarding=20vie?= =?UTF-8?q?w?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SwiftUI/Onboarding/OnboardingView.swift | 163 ++++++++++-------- phpmon/Localizable.strings | 5 +- 2 files changed, 91 insertions(+), 77 deletions(-) diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift index c00b636..25b31fa 100644 --- a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift @@ -8,96 +8,109 @@ import SwiftUI +struct OnboardingTextItem: View { + @State var icon: String + @State var title: String + @State var description: String + var body: some View { + HStack(spacing: 15) { + Image(systemName: icon) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 24, height: 24) + .foregroundColor(Color.appPrimary) + .padding(.trailing, 10) + VStack(alignment: .leading, spacing: 4) { + Text(title.localizedForSwiftUI) + .font(.system(size: 15)) + Text(description.localizedForSwiftUI) + .foregroundColor(Color.secondary) + .font(.system(size: 13)) + .fixedSize(horizontal: false, vertical: true) + } + } + } +} + struct OnboardingView: View { var body: some View { - VStack { - VStack(alignment: .leading) { - HStack { - Image(nsImage: NSApp.applicationIconImage) - .resizable() - .frame(width: 80, height: 80) - .padding(.bottom, 5) - .padding(.trailing, 25) - VStack(alignment: .leading, spacing: 0) { - Text("onboarding.welcome".localized) - .font(.title) - .bold() + VStack(spacing: 10) { + VStack(alignment: .center) { + HStack { + Image(nsImage: NSApp.applicationIconImage) + .resizable() + .frame(width: 80, height: 80) .padding(.bottom, 5) - Text("onboarding.explore".localized) - .padding(.bottom) - }.padding(.top, 10) - } - - TabView { + .padding(.trailing, 25) + VStack(alignment: .leading, spacing: 0) { + Text("onboarding.welcome".localized) + .font(.title) + .bold() + .padding(.bottom, 5) + Text("onboarding.explore".localized) + .padding(.bottom) + } + .padding(.top, 10) + } VStack { - Image("Tour.MenuBar") - .resizable() - .aspectRatio(contentMode: .fit) - .padding(.top) - Text("onboarding.tour.menu_bar".localizedForSwiftUI) - .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) - }.tabItem { Label("onboarding.tour.menu_bar.title".localized, - systemImage: "info.circle.fill") } - VStack { - Image("Tour.Domains") - .resizable() - .aspectRatio(contentMode: .fit) - .padding(.top) - Text("onboarding.tour.domains".localized) - .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) - }.tabItem { Label("onboarding.tour.domains.title".localized, - systemImage: "info.circle.fill") } - VStack { - Image("Tour.Isolation") - .resizable() - .aspectRatio(contentMode: .fit) - .padding(.top) - Text("onboarding.tour.isolation".localized) - .padding(.init(top: 5, leading: 20, bottom: 20, trailing: 20)) - }.tabItem { Label("onboarding.tour.isolation.title".localized, - systemImage: "info.circle.fill") } + VStack(alignment: .leading, spacing: 20) { + OnboardingTextItem( + icon: "sparkles.rectangle.stack", + title: "onboarding.tour.menu_bar.title", + description: "onboarding.tour.menu_bar" + ) + OnboardingTextItem( + icon: "list.star", + title: "onboarding.tour.domains.title", + description: "onboarding.tour.domains" + ) + OnboardingTextItem( + icon: "pin.fill", + title: "onboarding.tour.isolation.title", + description: "onboarding.tour.isolation" + ) + } + .padding(20) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(Color.gray.opacity(0.3), lineWidth: 1) + ) + }.padding() + VStack(spacing: 20) { + HStack { + Image(systemName: "questionmark.circle.fill") + .resizable() + .frame(width: 24, height: 24) + .foregroundColor(Color.appSecondary) + .padding(.trailing, 10) + Text("onboarding.tour.faq_hint".localizedForSwiftUI) + } + VStack { + Text("onboarding.tour.once".localized) + .font(.subheadline) + .foregroundColor(.gray) + .padding(.top, 5) + .padding(.bottom, 5) + Button("onboarding.tour.close".localized) { + App.shared.onboardingWindowController?.close() + } + } + }.padding() } - } - .frame(maxWidth: .infinity) - - VStack(alignment: .center) { - HStack { - Image(systemName: "person.fill.questionmark") - .resizable() - .frame(width: 24, height: 24) - .foregroundColor(.accentColor) - .padding(.trailing, 10) - Text("onboarding.tour.faq_hint".localizedForSwiftUI) - }.padding() - Text("onboarding.tour.once".localized) - .font(.subheadline) - .foregroundColor(.gray) - .padding(.top, 5) - .padding(.bottom, 5) - Button("Close Tour") { - App.shared.onboardingWindowController?.close() - } - } - .frame(maxWidth: .infinity) + .frame(maxWidth: .infinity) + .frame(maxHeight: 580) } .padding(.top, 8) .padding(.leading) .padding(.trailing) - .padding(.bottom) } } struct OnboardingView_Previews: PreviewProvider { static var previews: some View { Group { - OnboardingView().frame( - width: 600, - height: 600 - ) - OnboardingView().preferredColorScheme(.dark).frame( - width: 600, - height: 600 - ) + OnboardingView() + OnboardingView().preferredColorScheme(.dark) } } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 56f8550..3ca811a 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -513,12 +513,13 @@ If you are seeing this message but are confused why this folder has gone missing "onboarding.title" = "Welcome Tour"; "onboarding.welcome" = "Welcome to PHP Monitor!"; -"onboarding.explore" = "Explore some of the cool features PHP Monitor has to offer."; +"onboarding.explore" = "Learn more about some of the features that PHP Monitor has to offer."; "onboarding.tour.menu_bar.title" = "Get Started"; "onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From here, you can switch the globally linked PHP version, start or stop services, locate configuration files, and more."; -"onboarding.tour.faq_hint" = "**If you are new to PHP Monitor**, I recommend that you check out the [README](https://github.com/nicoverbruggen/phpmon/blob/main/README.md) on GitHub: it contains a comprehensive FAQ with various tips and common questions and answers. Don't hesitate to get in touch either."; +"onboarding.tour.faq_hint" = "I recommend that you check out the [README](https://github.com/nicoverbruggen/phpmon/blob/main/README.md) on GitHub: it contains a comprehensive FAQ with various tips and common questions and answers."; "onboarding.tour.domains.title" = "Domains"; "onboarding.tour.domains" = "By opening the Domains window via the Menu Bar item, you can view which domains are linked and parked."; "onboarding.tour.isolation.title" = "Isolation"; "onboarding.tour.isolation" = "If you have Valet 3 installed, you can even use domain isolation by right-clicking on a given domain in the Domains window. This allows you to pick a specific version of PHP to use for that domain!"; "onboarding.tour.once" = "You will only see the Welcome Tour once. You can re-open the Welcome Tour later via the menu bar icon."; +"onboarding.tour.close" = "Close Tour"; From 023043a81de97f53404e7113b031d9ba1653f3cb Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 3 Aug 2022 19:10:13 +0200 Subject: [PATCH 22/43] =?UTF-8?q?=F0=9F=8D=B1=20Further=20tweaks=20to=20on?= =?UTF-8?q?boarding=20view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SwiftUI/Onboarding/OnboardingView.swift | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift index 25b31fa..b81befb 100644 --- a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift @@ -13,7 +13,7 @@ struct OnboardingTextItem: View { @State var title: String @State var description: String var body: some View { - HStack(spacing: 15) { + HStack(alignment: .top, spacing: 5) { Image(systemName: icon) .resizable() .aspectRatio(contentMode: .fit) @@ -22,13 +22,21 @@ struct OnboardingTextItem: View { .padding(.trailing, 10) VStack(alignment: .leading, spacing: 4) { Text(title.localizedForSwiftUI) - .font(.system(size: 15)) + .font(.system(size: 14)) + .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) Text(description.localizedForSwiftUI) .foregroundColor(Color.secondary) .font(.system(size: 13)) .fixedSize(horizontal: false, vertical: true) + .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) } } + .frame(maxWidth: .infinity) + .padding(18) + .overlay( + RoundedRectangle(cornerRadius: 5) + .stroke(Color.gray.opacity(0.3), lineWidth: 1) + ) } } @@ -53,28 +61,23 @@ struct OnboardingView: View { .padding(.top, 10) } VStack { - VStack(alignment: .leading, spacing: 20) { + VStack(alignment: .leading, spacing: 10) { OnboardingTextItem( - icon: "sparkles.rectangle.stack", + icon: "bolt.circle.fill", title: "onboarding.tour.menu_bar.title", description: "onboarding.tour.menu_bar" ) OnboardingTextItem( - icon: "list.star", + icon: "list.bullet.circle.fill", title: "onboarding.tour.domains.title", description: "onboarding.tour.domains" ) OnboardingTextItem( - icon: "pin.fill", + icon: "pin.circle.fill", title: "onboarding.tour.isolation.title", description: "onboarding.tour.isolation" ) } - .padding(20) - .overlay( - RoundedRectangle(cornerRadius: 10) - .stroke(Color.gray.opacity(0.3), lineWidth: 1) - ) }.padding() VStack(spacing: 20) { HStack { From bcc80b521051440e7955189fa75659c687dfece3 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 5 Aug 2022 20:05:29 +0200 Subject: [PATCH 23/43] =?UTF-8?q?=F0=9F=91=8C=20More=20SwiftUI=20tuning=20?= =?UTF-8?q?(automatic=20height)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SwiftUI/Onboarding/OnboardingView.swift | 34 +++++++++---------- phpmon/Localizable.strings | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift index b81befb..cf81c48 100644 --- a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift @@ -12,6 +12,7 @@ struct OnboardingTextItem: View { @State var icon: String @State var title: String @State var description: String + var body: some View { HStack(alignment: .top, spacing: 5) { Image(systemName: icon) @@ -23,20 +24,19 @@ struct OnboardingTextItem: View { VStack(alignment: .leading, spacing: 4) { Text(title.localizedForSwiftUI) .font(.system(size: 14)) - .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) - Text(description.localizedForSwiftUI) - .foregroundColor(Color.secondary) - .font(.system(size: 13)) - .fixedSize(horizontal: false, vertical: true) - .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) + .lineLimit(3) + HStack { + Text(description.localizedForSwiftUI) + .foregroundColor(Color.secondary) + .font(.system(size: 13)) + .lineLimit(3) + .fixedSize(horizontal: false, vertical: true) + .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) + } } } - .frame(maxWidth: .infinity) - .padding(18) - .overlay( - RoundedRectangle(cornerRadius: 5) - .stroke(Color.gray.opacity(0.3), lineWidth: 1) - ) + .padding() + .overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.gray.opacity(0.3), lineWidth: 1)) } } @@ -86,7 +86,10 @@ struct OnboardingView: View { .frame(width: 24, height: 24) .foregroundColor(Color.appSecondary) .padding(.trailing, 10) - Text("onboarding.tour.faq_hint".localizedForSwiftUI) + HStack { + Text("onboarding.tour.faq_hint".localizedForSwiftUI) + .lineLimit(5) + }.fixedSize(horizontal: false, vertical: true) } VStack { Text("onboarding.tour.once".localized) @@ -94,18 +97,15 @@ struct OnboardingView: View { .foregroundColor(.gray) .padding(.top, 5) .padding(.bottom, 5) + .lineLimit(5) Button("onboarding.tour.close".localized) { App.shared.onboardingWindowController?.close() } } }.padding() } - .frame(maxWidth: .infinity) - .frame(maxHeight: 580) } .padding(.top, 8) - .padding(.leading) - .padding(.trailing) } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 3ca811a..ec6a642 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -515,7 +515,7 @@ If you are seeing this message but are confused why this folder has gone missing "onboarding.welcome" = "Welcome to PHP Monitor!"; "onboarding.explore" = "Learn more about some of the features that PHP Monitor has to offer."; "onboarding.tour.menu_bar.title" = "Get Started"; -"onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From here, you can switch the globally linked PHP version, start or stop services, locate configuration files, and more."; +"onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From here, you can switch the globally linked PHP version, start or stop services, locate config files, and more."; "onboarding.tour.faq_hint" = "I recommend that you check out the [README](https://github.com/nicoverbruggen/phpmon/blob/main/README.md) on GitHub: it contains a comprehensive FAQ with various tips and common questions and answers."; "onboarding.tour.domains.title" = "Domains"; "onboarding.tour.domains" = "By opening the Domains window via the Menu Bar item, you can view which domains are linked and parked."; From ccfb15c83c59d2e7831bcc6234cf8d537c4d5bd6 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 9 Aug 2022 18:04:18 +0200 Subject: [PATCH 24/43] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20`WindowControl?= =?UTF-8?q?ler`=20instead=20of=20`WC`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Renamed `ProgressWC` to `TerminalProgressWindowController` so it better reflects the functionality of the class. - Renamed all `-WC` suffixed classes to `-WindowController`. This is cleaner because it avoids referencing water closets. 🚽 - Fixed some instances where the copyright notice used the wrong filename. All window controller classes are now accurate. --- PHP Monitor.xcodeproj/project.pbxproj | 56 +++++++++---------- phpmon/Domain/App/App.swift | 6 +- phpmon/Domain/App/Base.lproj/Main.storyboard | 7 +-- phpmon/Domain/App/Startup.swift | 2 +- phpmon/Domain/DomainList/DomainListVC.swift | 2 +- ...swift => DomainListWindowController.swift} | 4 +- phpmon/Domain/DomainList/SelectionVC.swift | 2 +- .../Composer/ComposerWindow.swift | 4 +- phpmon/Domain/Menu/MainMenu+Startup.swift | 2 +- phpmon/Domain/Menu/MainMenu.swift | 2 +- ...swift => OnboardingWindowController.swift} | 6 +- ... PreferencesWindowController+Hotkey.swift} | 4 +- ...wift => PreferencesWindowController.swift} | 6 +- .../Domain/Progress/ProgressWindow.storyboard | 2 +- ...=> TerminalProgressWindowController.swift} | 8 +-- 15 files changed, 56 insertions(+), 57 deletions(-) rename phpmon/Domain/DomainList/{DomainListWC.swift => DomainListWindowController.swift} (96%) rename phpmon/Domain/Onboarding/{OnboardingWC.swift => OnboardingWindowController.swift} (89%) rename phpmon/Domain/Preferences/{PrefsWC+Hotkey.swift => PreferencesWindowController+Hotkey.swift} (91%) rename phpmon/Domain/Preferences/{PrefsWC.swift => PreferencesWindowController.swift} (95%) rename phpmon/Domain/Progress/{ProgressWC.swift => TerminalProgressWindowController.swift} (85%) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index b5bf5e6..28cf0c2 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -117,8 +117,8 @@ C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; }; C44A874828905BB000498BC4 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; }; C44A874928905BB000498BC4 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; }; - C44C198D276E3A1C0072762D /* ProgressWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWC.swift */; }; - C44C198E276E3A1C0072762D /* ProgressWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWC.swift */; }; + C44C198D276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; }; + C44C198E276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; }; C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; }; C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; }; C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; }; @@ -131,8 +131,8 @@ C45E76152854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; }; C463E380284930EE00422731 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; }; C463E381284930EE00422731 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; }; - C464ADAC275A7A3F003FCD53 /* DomainListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */; }; - C464ADAD275A7A3F003FCD53 /* DomainListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */; }; + C464ADAC275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */; }; + C464ADAD275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */; }; C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; }; C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; }; C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */; }; @@ -162,8 +162,8 @@ C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; }; C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; }; C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; }; - C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; - C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; + C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; }; + C4998F0B2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; }; C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; }; C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; }; C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; }; @@ -269,9 +269,9 @@ C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; }; C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; }; C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; }; - C4FACE80288F1C0D00FC478F /* PrefsWC+Hotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE7F288F1C0D00FC478F /* PrefsWC+Hotkey.swift */; }; - C4FACE81288F1C0D00FC478F /* PrefsWC+Hotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE7F288F1C0D00FC478F /* PrefsWC+Hotkey.swift */; }; - C4FACE83288F1F9700FC478F /* OnboardingWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE82288F1F9700FC478F /* OnboardingWC.swift */; }; + C4FACE80288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */; }; + C4FACE81288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */; }; + C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */; }; C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; }; C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F361602836BFD9003598CC /* MainMenu+Actions.swift */; }; C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; }; @@ -361,7 +361,7 @@ C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIHelper.swift; sourceTree = ""; }; C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionPopoverView.swift; sourceTree = ""; }; C44A874728905BB000498BC4 /* ProgressVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressVC.swift; sourceTree = ""; }; - C44C198C276E3A1C0072762D /* ProgressWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWC.swift; sourceTree = ""; }; + C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalProgressWindowController.swift; sourceTree = ""; }; C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = ""; }; C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = ""; }; C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = ""; }; @@ -369,7 +369,7 @@ C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-proxy.test"; sourceTree = ""; }; C45E76132854A65300B4FE0C /* ServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesManager.swift; sourceTree = ""; }; C463E37F284930EE00422731 /* PresetHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetHelper.swift; sourceTree = ""; }; - C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListWC.swift; sourceTree = ""; }; + C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListWindowController.swift; sourceTree = ""; }; C464ADAE275A7A69003FCD53 /* DomainListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListVC.swift; sourceTree = ""; }; C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListCellProtocol.swift; sourceTree = ""; }; C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateChecker.swift; sourceTree = ""; }; @@ -390,7 +390,7 @@ 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 = ""; }; C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = ""; }; - C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = ""; }; + C4998F092617633900B2526E /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = ""; }; C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListKindCell.swift; sourceTree = ""; }; C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = ""; }; C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = ""; }; @@ -451,8 +451,8 @@ C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtensionTest.swift; sourceTree = ""; }; C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = ""; }; C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - C4FACE7F288F1C0D00FC478F /* PrefsWC+Hotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrefsWC+Hotkey.swift"; sourceTree = ""; }; - C4FACE82288F1F9700FC478F /* OnboardingWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingWC.swift; sourceTree = ""; }; + C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PreferencesWindowController+Hotkey.swift"; sourceTree = ""; }; + C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingWindowController.swift; sourceTree = ""; }; C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionDetectionTest.swift; sourceTree = ""; }; C4FE011028084FC200D1DE6D /* SelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionVC.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -478,8 +478,8 @@ 5420395726135DB800FB00FA /* Preferences */ = { isa = PBXGroup; children = ( - C4998F092617633900B2526E /* PrefsWC.swift */, - C4FACE7F288F1C0D00FC478F /* PrefsWC+Hotkey.swift */, + C4998F092617633900B2526E /* PreferencesWindowController.swift */, + C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */, 5420395826135DC100FB00FA /* PrefsVC.swift */, 5420395E2613607600FB00FA /* Preferences.swift */, C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */, @@ -704,7 +704,7 @@ C44A874628905B8500498BC4 /* Onboarding */ = { isa = PBXGroup; children = ( - C4FACE82288F1F9700FC478F /* OnboardingWC.swift */, + C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */, ); path = Onboarding; sourceTree = ""; @@ -712,7 +712,7 @@ C44C198F276E3A380072762D /* Progress */ = { isa = PBXGroup; children = ( - C44C198C276E3A1C0072762D /* ProgressWC.swift */, + C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */, C44A874728905BB000498BC4 /* ProgressVC.swift */, C44C1990276E44CB0072762D /* ProgressWindow.storyboard */, ); @@ -777,7 +777,7 @@ isa = PBXGroup; children = ( C44067F327E256560045BD4E /* Cells */, - C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */, + C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */, C464ADAE275A7A69003FCD53 /* DomainListVC.swift */, C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */, C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */, @@ -1249,7 +1249,7 @@ C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */, C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */, C4B585412770FE3900DA4FBE /* Shell.swift in Sources */, - C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */, + C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */, C46FA9882822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */, C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */, C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */, @@ -1285,13 +1285,13 @@ C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */, C4B6091A2853AAD300C95265 /* SectionHeaderView.swift in Sources */, C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */, - C4FACE80288F1C0D00FC478F /* PrefsWC+Hotkey.swift in Sources */, + C4FACE80288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */, C42800AA28452AA10099C999 /* StatusMenu+Items.swift in Sources */, C415D3B72770F294005EF286 /* Actions.swift in Sources */, C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */, C4CDA893288F1A71007CE25F /* Keys.swift in Sources */, C4F361612836BFD9003598CC /* MainMenu+Actions.swift in Sources */, - C44C198D276E3A1C0072762D /* ProgressWC.swift in Sources */, + C44C198D276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */, 54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */, C4C0E8E727F88B41002D32A9 /* ProxyScanner.swift in Sources */, C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */, @@ -1346,7 +1346,7 @@ C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */, C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */, C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */, - C4FACE83288F1F9700FC478F /* OnboardingWC.swift in Sources */, + C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */, C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */, C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */, 54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */, @@ -1360,7 +1360,7 @@ C42337A3281F19F000459A48 /* Xdebug.swift in Sources */, C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */, - C464ADAC275A7A3F003FCD53 /* DomainListWC.swift in Sources */, + C464ADAC275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */, C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */, C4EE188422D3386B00E126E5 /* Constants.swift in Sources */, C493084A279F331F009C240B /* AddSiteVC.swift in Sources */, @@ -1375,7 +1375,7 @@ C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */, 54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */, C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */, - C4FACE81288F1C0D00FC478F /* PrefsWC+Hotkey.swift in Sources */, + C4FACE81288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */, 54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */, C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */, C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */, @@ -1456,10 +1456,10 @@ C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */, C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */, 54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */, - C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */, + C4998F0B2617633900B2526E /* PreferencesWindowController.swift in Sources */, C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */, C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */, - C44C198E276E3A1C0072762D /* ProgressWC.swift in Sources */, + C44C198E276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */, C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */, C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */, C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */, @@ -1488,7 +1488,7 @@ C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */, C4B585422770FE3900DA4FBE /* Shell.swift in Sources */, C45E76152854A65300B4FE0C /* ServicesManager.swift in Sources */, - C464ADAD275A7A3F003FCD53 /* DomainListWC.swift in Sources */, + C464ADAD275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */, C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */, C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */, C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */, diff --git a/phpmon/Domain/App/App.swift b/phpmon/Domain/App/App.swift index 7615701..1689e09 100644 --- a/phpmon/Domain/App/App.swift +++ b/phpmon/Domain/App/App.swift @@ -51,13 +51,13 @@ class App { var preferences: [PreferenceName: Bool]! /** The window controller of the currently active preferences window. */ - var preferencesWindowController: PrefsWC? + var preferencesWindowController: PreferencesWindowController? /** The window controller of the currently active site list window. */ - var domainListWindowController: DomainListWC? + var domainListWindowController: DomainListWindowController? /** The window controller of the onboarding window. */ - var onboardingWindowController: OnboardingWC? + var onboardingWindowController: OnboardingWindowController? /** List of detected (installed) applications that PHP Monitor can work with. */ var detectedApplications: [Application] = [] diff --git a/phpmon/Domain/App/Base.lproj/Main.storyboard b/phpmon/Domain/App/Base.lproj/Main.storyboard index 679485d..1198b94 100644 --- a/phpmon/Domain/App/Base.lproj/Main.storyboard +++ b/phpmon/Domain/App/Base.lproj/Main.storyboard @@ -1,7 +1,6 @@ - @@ -325,7 +324,7 @@ - + @@ -399,7 +398,7 @@ - + @@ -819,7 +818,7 @@ Gw - + diff --git a/phpmon/Domain/App/Startup.swift b/phpmon/Domain/App/Startup.swift index 08245cc..34411db 100644 --- a/phpmon/Domain/App/Startup.swift +++ b/phpmon/Domain/App/Startup.swift @@ -236,7 +236,7 @@ class Startup { titleText: "startup.errors.valet_version_unknown.title".localized, subtitleText: "startup.errors.valet_version_unknown.subtitle".localized, descriptionText: "startup.errors.valet_version_unknown.desc".localized - ), + ) ] // MARK: - EnvironmentCheck struct diff --git a/phpmon/Domain/DomainList/DomainListVC.swift b/phpmon/Domain/DomainList/DomainListVC.swift index e33c01d..68257bb 100644 --- a/phpmon/Domain/DomainList/DomainListVC.swift +++ b/phpmon/Domain/DomainList/DomainListVC.swift @@ -64,7 +64,7 @@ class DomainListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource let windowController = storyboard.instantiateController( withIdentifier: "domainListWindow" - ) as! DomainListWC + ) as! DomainListWindowController guard let window = windowController.window else { return } diff --git a/phpmon/Domain/DomainList/DomainListWC.swift b/phpmon/Domain/DomainList/DomainListWindowController.swift similarity index 96% rename from phpmon/Domain/DomainList/DomainListWC.swift rename to phpmon/Domain/DomainList/DomainListWindowController.swift index e8d4efa..df92f75 100644 --- a/phpmon/Domain/DomainList/DomainListWC.swift +++ b/phpmon/Domain/DomainList/DomainListWindowController.swift @@ -1,5 +1,5 @@ // -// DomainListWC.swift +// DomainListWindowController.swift // PHP Monitor // // Created by Nico Verbruggen on 03/12/2021. @@ -8,7 +8,7 @@ import Cocoa -class DomainListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate { +class DomainListWindowController: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate { // MARK: - Window Identifier diff --git a/phpmon/Domain/DomainList/SelectionVC.swift b/phpmon/Domain/DomainList/SelectionVC.swift index 1f4a82c..80c2323 100644 --- a/phpmon/Domain/DomainList/SelectionVC.swift +++ b/phpmon/Domain/DomainList/SelectionVC.swift @@ -11,7 +11,7 @@ import Cocoa class SelectionVC: NSViewController { - weak var domainListWC: DomainListWC? + weak var domainListWC: DomainListWindowController? @IBOutlet weak var textFieldTitle: NSTextField! @IBOutlet weak var textFieldDescription: NSTextField! diff --git a/phpmon/Domain/Integrations/Composer/ComposerWindow.swift b/phpmon/Domain/Integrations/Composer/ComposerWindow.swift index e25adfb..2868636 100644 --- a/phpmon/Domain/Integrations/Composer/ComposerWindow.swift +++ b/phpmon/Domain/Integrations/Composer/ComposerWindow.swift @@ -13,7 +13,7 @@ class ComposerWindow { private var menu: MainMenu? private var shouldNotify: Bool! = nil private var completion: ((Bool) -> Void)! = nil - private var window: ProgressWC? + private var window: TerminalProgressWindowController? /** Updates the global dependencies and runs the completion callback when done. @@ -35,7 +35,7 @@ class ComposerWindow { menu?.setBusyImage() menu?.rebuild() - window = ProgressWC.display( + window = TerminalProgressWindowController.display( title: "alert.composer_progress.title".localized, description: "alert.composer_progress.info".localized ) diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 1c2aa55..3876df6 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -93,7 +93,7 @@ extension MainMenu { if Stats.successfulLaunchCount >= 1 && !isRunningSwiftUIPreview { Log.info("Should present the first launch screen!") DispatchQueue.main.async { - OnboardingWC.show() + OnboardingWindowController.show() } } diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index fe85771..8b5d4f5 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -171,7 +171,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate } @objc func openPrefs() { - PrefsWC.show() + PreferencesWindowController.show() } @objc func openDomainList() { diff --git a/phpmon/Domain/Onboarding/OnboardingWC.swift b/phpmon/Domain/Onboarding/OnboardingWindowController.swift similarity index 89% rename from phpmon/Domain/Onboarding/OnboardingWC.swift rename to phpmon/Domain/Onboarding/OnboardingWindowController.swift index 6696c75..2850462 100644 --- a/phpmon/Domain/Onboarding/OnboardingWC.swift +++ b/phpmon/Domain/Onboarding/OnboardingWindowController.swift @@ -1,5 +1,5 @@ // -// OnboardingWC.swift +// OnboardingWindowController.swift // PHP Monitor // // Created by Nico Verbruggen on 25/06/2022. @@ -9,7 +9,7 @@ import Cocoa import SwiftUI -class OnboardingWC: PMWindowController { +class OnboardingWindowController: PMWindowController { // MARK: - Window Identifier @@ -18,7 +18,7 @@ class OnboardingWC: PMWindowController { } public static func create(delegate: NSWindowDelegate?) { - let windowController = OnboardingWC() + let windowController = OnboardingWindowController() windowController.window = NSWindow() guard let window = windowController.window else { return } diff --git a/phpmon/Domain/Preferences/PrefsWC+Hotkey.swift b/phpmon/Domain/Preferences/PreferencesWindowController+Hotkey.swift similarity index 91% rename from phpmon/Domain/Preferences/PrefsWC+Hotkey.swift rename to phpmon/Domain/Preferences/PreferencesWindowController+Hotkey.swift index 0653d05..7ddd785 100644 --- a/phpmon/Domain/Preferences/PrefsWC+Hotkey.swift +++ b/phpmon/Domain/Preferences/PreferencesWindowController+Hotkey.swift @@ -1,5 +1,5 @@ // -// PrefsWC+Hotkey.swift +// PreferencesWindowController+Hotkey.swift // PHP Monitor // // Created by Nico Verbruggen on 25/07/2022. @@ -8,7 +8,7 @@ import Cocoa -extension PrefsWC { +extension PreferencesWindowController { // MARK: - Key Interaction diff --git a/phpmon/Domain/Preferences/PrefsWC.swift b/phpmon/Domain/Preferences/PreferencesWindowController.swift similarity index 95% rename from phpmon/Domain/Preferences/PrefsWC.swift rename to phpmon/Domain/Preferences/PreferencesWindowController.swift index b2567fe..652279c 100644 --- a/phpmon/Domain/Preferences/PrefsWC.swift +++ b/phpmon/Domain/Preferences/PreferencesWindowController.swift @@ -1,5 +1,5 @@ // -// PrefsWC.swift +// PreferencesWindowController.swift // PHP Monitor // // Created by Nico Verbruggen on 02/04/2021. @@ -8,7 +8,7 @@ import Cocoa -class PrefsWC: PMWindowController { +class PreferencesWindowController: PMWindowController { // MARK: - Window Identifier @@ -21,7 +21,7 @@ class PrefsWC: PMWindowController { let windowController = storyboard.instantiateController( withIdentifier: "preferencesWindow" - ) as! PrefsWC + ) as! PreferencesWindowController guard let window = windowController.window else { return } diff --git a/phpmon/Domain/Progress/ProgressWindow.storyboard b/phpmon/Domain/Progress/ProgressWindow.storyboard index ef716b7..0d9106b 100644 --- a/phpmon/Domain/Progress/ProgressWindow.storyboard +++ b/phpmon/Domain/Progress/ProgressWindow.storyboard @@ -8,7 +8,7 @@ - + diff --git a/phpmon/Domain/Progress/ProgressWC.swift b/phpmon/Domain/Progress/TerminalProgressWindowController.swift similarity index 85% rename from phpmon/Domain/Progress/ProgressWC.swift rename to phpmon/Domain/Progress/TerminalProgressWindowController.swift index 0138a54..c7c05ea 100644 --- a/phpmon/Domain/Progress/ProgressWC.swift +++ b/phpmon/Domain/Progress/TerminalProgressWindowController.swift @@ -1,5 +1,5 @@ // -// ProgressView.swift +// TerminalProgressWindowController.swift // PHP Monitor // // Created by Nico Verbruggen on 18/12/2021. @@ -9,14 +9,14 @@ import Foundation import AppKit -class ProgressWC: NSWindowController, NSWindowDelegate { +class TerminalProgressWindowController: NSWindowController, NSWindowDelegate { - static func display(title: String, description: String) -> ProgressWC { + static func display(title: String, description: String) -> TerminalProgressWindowController { let storyboard = NSStoryboard(name: "ProgressWindow", bundle: nil) let windowController = storyboard.instantiateController( withIdentifier: "progressWindow" - ) as! ProgressWC + ) as! TerminalProgressWindowController windowController.showWindow(windowController) windowController.window?.makeKeyAndOrderFront(nil) From f90925ee17c794a02d471b3ed53f4b226f4b19eb Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 9 Aug 2022 20:31:17 +0200 Subject: [PATCH 25/43] =?UTF-8?q?=F0=9F=8F=97=20WIP:=20Warnings=20window?= =?UTF-8?q?=20&=20views?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 16 +++++++ phpmon/Domain/App/App.swift | 3 ++ phpmon/Domain/Menu/MainMenu+Startup.swift | 3 +- .../OnboardingWindowController.swift | 2 +- .../SwiftUI/Warning/WarningListView.swift | 33 ++++++++++++++ .../Domain/SwiftUI/Warning/WarningView.swift | 35 +++++++++------ .../Warnings/WarningsWindowController.swift | 45 +++++++++++++++++++ phpmon/Localizable.strings | 5 +++ 8 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 phpmon/Domain/SwiftUI/Warning/WarningListView.swift create mode 100644 phpmon/Domain/Warnings/WarningsWindowController.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 28cf0c2..82d5544 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -85,6 +85,8 @@ C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */; }; C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; }; C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; }; + C422DDAA28A2C49900CEAC97 /* WarningListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C422DDA928A2C49900CEAC97 /* WarningListView.swift */; }; + C422DDAD28A2DAC600CEAC97 /* WarningsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */; }; C4232EE52612526500158FC6 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = C4232EE42612526500158FC6 /* Credits.html */; }; C42337A3281F19F000459A48 /* Xdebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42337A2281F19F000459A48 /* Xdebug.swift */; }; C42759672627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; }; @@ -339,6 +341,8 @@ C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = ""; }; C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+ContextMenu.swift"; sourceTree = ""; }; C4205A7D27F4D21800191A39 /* ValetProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetProxy.swift; sourceTree = ""; }; + C422DDA928A2C49900CEAC97 /* WarningListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningListView.swift; sourceTree = ""; }; + C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningsWindowController.swift; sourceTree = ""; }; C4232EE42612526500158FC6 /* Credits.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = Credits.html; sourceTree = ""; }; C42337A2281F19F000459A48 /* Xdebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xdebug.swift; sourceTree = ""; }; C42759662627662800093CAE /* NSMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuExtension.swift; sourceTree = ""; }; @@ -662,6 +666,7 @@ C4D9ADBD27761084007277F4 /* PHP */, C47331A0247093AC009A0597 /* Menu */, C464ADAA275A7A25003FCD53 /* DomainList */, + C422DDAB28A2DAA100CEAC97 /* Warnings */, C44A874628905B8500498BC4 /* Onboarding */, 5420395726135DB800FB00FA /* Preferences */, C44C198F276E3A380072762D /* Progress */, @@ -672,6 +677,14 @@ path = Domain; sourceTree = ""; }; + C422DDAB28A2DAA100CEAC97 /* Warnings */ = { + isa = PBXGroup; + children = ( + C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */, + ); + path = Warnings; + sourceTree = ""; + }; C42337A1281F19DC00459A48 /* Extensions */ = { isa = PBXGroup; children = ( @@ -684,6 +697,7 @@ isa = PBXGroup; children = ( C4297F7928970D59004C4630 /* WarningView.swift */, + C422DDA928A2C49900CEAC97 /* WarningListView.swift */, ); path = Warning; sourceTree = ""; @@ -1298,6 +1312,7 @@ 54B48B5F275F66AE006D90C5 /* Application.swift in Sources */, C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */, + C422DDAD28A2DAC600CEAC97 /* WarningsWindowController.swift in Sources */, C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */, C4811D2422D70A4700B5F6B3 /* App.swift in Sources */, C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */, @@ -1325,6 +1340,7 @@ C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */, C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C42759672627662800093CAE /* NSMenuExtension.swift in Sources */, + C422DDAA28A2C49900CEAC97 /* WarningListView.swift in Sources */, C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */, C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */, C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */, diff --git a/phpmon/Domain/App/App.swift b/phpmon/Domain/App/App.swift index 1689e09..5c377e3 100644 --- a/phpmon/Domain/App/App.swift +++ b/phpmon/Domain/App/App.swift @@ -59,6 +59,9 @@ class App { /** The window controller of the onboarding window. */ var onboardingWindowController: OnboardingWindowController? + /** The window controller of the warnings window. */ + var warningsWindowController: WarningsWindowController? + /** List of detected (installed) applications that PHP Monitor can work with. */ var detectedApplications: [Application] = [] diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 3876df6..3f03a00 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -93,7 +93,8 @@ extension MainMenu { if Stats.successfulLaunchCount >= 1 && !isRunningSwiftUIPreview { Log.info("Should present the first launch screen!") DispatchQueue.main.async { - OnboardingWindowController.show() + // OnboardingWindowController.show() + WarningsWindowController.show() } } diff --git a/phpmon/Domain/Onboarding/OnboardingWindowController.swift b/phpmon/Domain/Onboarding/OnboardingWindowController.swift index 2850462..0f73b9b 100644 --- a/phpmon/Domain/Onboarding/OnboardingWindowController.swift +++ b/phpmon/Domain/Onboarding/OnboardingWindowController.swift @@ -18,7 +18,7 @@ class OnboardingWindowController: PMWindowController { } public static func create(delegate: NSWindowDelegate?) { - let windowController = OnboardingWindowController() + let windowController = Self() windowController.window = NSWindow() guard let window = windowController.window else { return } diff --git a/phpmon/Domain/SwiftUI/Warning/WarningListView.swift b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift new file mode 100644 index 0000000..e4f7eb3 --- /dev/null +++ b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift @@ -0,0 +1,33 @@ +// +// WarningListView.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 09/08/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import SwiftUI + +struct WarningListView: View { + var body: some View { + List { + VStack(alignment: .leading) { + WarningView( + title: "warnings.arm_compatibility_title".localized, + description: "warnings.arm_compatibility.description".localized, + documentationUrl: "https://phpmon.app/documentation/apple-silicon-transition" + ) + Divider() + }.frame(height: 90) + + } + .navigationTitle("Warnings") + .listStyle(.automatic) + } +} + +struct WarningListView_Previews: PreviewProvider { + static var previews: some View { + WarningListView() + } +} diff --git a/phpmon/Domain/SwiftUI/Warning/WarningView.swift b/phpmon/Domain/SwiftUI/Warning/WarningView.swift index 70ac712..7339934 100644 --- a/phpmon/Domain/SwiftUI/Warning/WarningView.swift +++ b/phpmon/Domain/SwiftUI/Warning/WarningView.swift @@ -11,22 +11,29 @@ import SwiftUI struct WarningView: View { @State var title: String @State var description: String + @State var documentationUrl: String? var body: some View { - HStack { - Image(systemName: "exclamationmark.triangle.fill") - .resizable() - .frame(width: 25, height: 25) - .padding() - .foregroundColor(Color.orange) - VStack(alignment: .leading, spacing: 5) { - Text(title.localizedForSwiftUI) - .fontWeight(.bold) - Text(description.localizedForSwiftUI) - .font(.body) - - } - }.padding() + VStack(alignment: .leading) { + HStack(spacing: 5) { + Image(systemName: "exclamationmark.triangle.fill") + .resizable() + .frame(width: 25, height: 25) + .padding() + .foregroundColor(Color.orange) + VStack(alignment: .leading, spacing: 5) { + Text(title.localizedForSwiftUI) + .fontWeight(.bold) + Text(description.localizedForSwiftUI) + .font(.body) + } + if documentationUrl != nil { + Button("Learn More") { + NSWorkspace.shared.open(URL(string: documentationUrl!)!) + } + } + }.padding() + } } } diff --git a/phpmon/Domain/Warnings/WarningsWindowController.swift b/phpmon/Domain/Warnings/WarningsWindowController.swift new file mode 100644 index 0000000..65b1c25 --- /dev/null +++ b/phpmon/Domain/Warnings/WarningsWindowController.swift @@ -0,0 +1,45 @@ +// +// WarningsWindowController.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 09/08/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Cocoa +import SwiftUI + +class WarningsWindowController: PMWindowController { + + // MARK: - Window Identifier + + override var windowName: String { + return "Warnings" + } + + public static func create(delegate: NSWindowDelegate?) { + let windowController = Self() + windowController.window = NSWindow() + + guard let window = windowController.window else { return } + window.title = "warnings.title".localized + window.styleMask = [.titled, .closable, .miniaturizable] + window.titlebarAppearsTransparent = true + window.delegate = delegate ?? windowController + window.contentView = NSHostingView(rootView: WarningListView()) + window.setContentSize(NSSize(width: 600, height: 300)) + + App.shared.warningsWindowController = windowController + } + + public static func show(delegate: NSWindowDelegate? = nil) { + if App.shared.warningsWindowController == nil { + Self.create(delegate: delegate) + } + + App.shared.warningsWindowController?.showWindow(self) + App.shared.warningsWindowController?.window?.setCenterPosition(offsetY: 70) + + NSApp.activate(ignoringOtherApps: true) + } +} diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index ec6a642..d944dcc 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -506,9 +506,14 @@ If you are seeing this message but are confused why this folder has gone missing // WARNINGS +"warnings.title" = "Warnings"; + "warnings.helper_permissions_title" = "Helpers could not be written!"; "warnings.helper_permissions.description" = "The helper files in `/usr/local/bin` could not be written because PHP Monitor does not have permission to write there."; +"warnings.arm_compatibility_title" = "You are running PHP Monitor using Rosetta"; +"warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew."; + // ONBOARDING "onboarding.title" = "Welcome Tour"; From c1c7561361cc37c83749e784aafd4b22fa246e7c Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 9 Aug 2022 21:55:59 +0200 Subject: [PATCH 26/43] =?UTF-8?q?=F0=9F=8F=97=20WIP:=20Warning=20manager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 8 ++++++++ phpmon/Domain/Menu/MainMenu+Startup.swift | 1 - phpmon/Domain/Menu/MainMenu.swift | 4 ++++ phpmon/Domain/Menu/StatusMenu.swift | 4 ++++ .../Domain/SwiftUI/Warning/WarningListView.swift | 11 ++++++++--- phpmon/Domain/SwiftUI/Warning/WarningView.swift | 15 ++++++++------- phpmon/Domain/Warnings/Warning.swift | 13 +++++++++++++ phpmon/Domain/Warnings/WarningManager.swift | 15 +++++++++++++++ phpmon/Localizable.strings | 2 ++ 9 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 phpmon/Domain/Warnings/Warning.swift create mode 100644 phpmon/Domain/Warnings/WarningManager.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 82d5544..73601a5 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -149,6 +149,8 @@ C473319F2470923A009A0597 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; }; C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; }; C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; }; + C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699EE28A2F2A30060FEB8 /* WarningManager.swift */; }; + C47699F128A2F3150060FEB8 /* Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699F028A2F3150060FEB8 /* Warning.swift */; }; 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 */; }; @@ -385,6 +387,8 @@ C473319E2470923A009A0597 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; C47331A1247093B7009A0597 /* StatusMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMenu.swift; sourceTree = ""; }; C474B00524C0E98C00066A22 /* LocalNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = ""; }; + C47699EE28A2F2A30060FEB8 /* WarningManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningManager.swift; sourceTree = ""; }; + C47699F028A2F3150060FEB8 /* Warning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Warning.swift; sourceTree = ""; }; C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = ""; }; C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = ""; }; @@ -680,6 +684,8 @@ C422DDAB28A2DAA100CEAC97 /* Warnings */ = { isa = PBXGroup; children = ( + C47699F028A2F3150060FEB8 /* Warning.swift */, + C47699EE28A2F2A30060FEB8 /* WarningManager.swift */, C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */, ); path = Warnings; @@ -1258,6 +1264,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */, C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */, C4D8016622B1584700C6DA1B /* Startup.swift in Sources */, C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */, @@ -1324,6 +1331,7 @@ 5420395F2613607600FB00FA /* Preferences.swift in Sources */, C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */, 54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */, + C47699F128A2F3150060FEB8 /* Warning.swift in Sources */, 54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */, C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */, C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */, diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 3f03a00..edd5c86 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -94,7 +94,6 @@ extension MainMenu { Log.info("Should present the first launch screen!") DispatchQueue.main.async { // OnboardingWindowController.show() - WarningsWindowController.show() } } diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 8b5d4f5..3019c84 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -174,6 +174,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate PreferencesWindowController.show() } + @objc func openWarnings() { + WarningsWindowController.show() + } + @objc func openDomainList() { DomainListVC.show() } diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 5da271e..f27976c 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -76,6 +76,10 @@ class StatusMenu: NSMenu { func addCoreMenuItems() { self.addItem(NSMenuItem.separator()) + if (WarningManager.hasWarnings()) { + self.addItem(NSMenuItem(title: "mi_warnings".localized(2), + action: #selector(MainMenu.openWarnings), keyEquivalent: "")) + } self.addItem(NSMenuItem(title: "mi_preferences".localized, action: #selector(MainMenu.openPrefs), keyEquivalent: ",")) self.addItem(NSMenuItem(title: "mi_check_for_updates".localized, diff --git a/phpmon/Domain/SwiftUI/Warning/WarningListView.swift b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift index e4f7eb3..0417bf9 100644 --- a/phpmon/Domain/SwiftUI/Warning/WarningListView.swift +++ b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift @@ -13,12 +13,17 @@ struct WarningListView: View { List { VStack(alignment: .leading) { WarningView( - title: "warnings.arm_compatibility_title".localized, - description: "warnings.arm_compatibility.description".localized, + title: "warnings.arm_compatibility_title", + description: "warnings.arm_compatibility.description", documentationUrl: "https://phpmon.app/documentation/apple-silicon-transition" ) Divider() - }.frame(height: 90) + WarningView( + title: "warnings.helper_permissions_title", + description: "warnings.helper_permissions.description" + ) + Divider() + } } .navigationTitle("Warnings") diff --git a/phpmon/Domain/SwiftUI/Warning/WarningView.swift b/phpmon/Domain/SwiftUI/Warning/WarningView.swift index 7339934..efd98dd 100644 --- a/phpmon/Domain/SwiftUI/Warning/WarningView.swift +++ b/phpmon/Domain/SwiftUI/Warning/WarningView.swift @@ -15,24 +15,24 @@ struct WarningView: View { var body: some View { VStack(alignment: .leading) { - HStack(spacing: 5) { + HStack(spacing: 10) { Image(systemName: "exclamationmark.triangle.fill") .resizable() - .frame(width: 25, height: 25) - .padding() + .frame(width: 18, height: 18) .foregroundColor(Color.orange) + .padding() VStack(alignment: .leading, spacing: 5) { Text(title.localizedForSwiftUI) .fontWeight(.bold) Text(description.localizedForSwiftUI) - .font(.body) + .font(.system(size: 12)) } if documentationUrl != nil { Button("Learn More") { NSWorkspace.shared.open(URL(string: documentationUrl!)!) - } + }.padding() } - }.padding() + }.padding(5) } } } @@ -41,7 +41,8 @@ struct WarningView_Previews: PreviewProvider { static var previews: some View { WarningView( title: "warnings.helper_permissions_title", - description: "warnings.helper_permissions.description" + description: "warnings.helper_permissions.description", + documentationUrl: "https://nicoverbruggen.be" ) WarningView( title: "warnings.helper_permissions_title", diff --git a/phpmon/Domain/Warnings/Warning.swift b/phpmon/Domain/Warnings/Warning.swift new file mode 100644 index 0000000..ef629ef --- /dev/null +++ b/phpmon/Domain/Warnings/Warning.swift @@ -0,0 +1,13 @@ +// +// Warning.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 09/08/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +struct Warning { + +} diff --git a/phpmon/Domain/Warnings/WarningManager.swift b/phpmon/Domain/Warnings/WarningManager.swift new file mode 100644 index 0000000..179988c --- /dev/null +++ b/phpmon/Domain/Warnings/WarningManager.swift @@ -0,0 +1,15 @@ +// +// WarningManager.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 09/08/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class WarningManager { + + // TODO: Singleton initialization at launch + +} diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index d944dcc..d9e0a3a 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -55,6 +55,8 @@ "mi_detected_extensions" = "Detected Extensions"; "mi_no_extensions_detected" = "No additional extensions detected."; +"mi_warnings" = "⚠️ %i Warnings..."; + "mi_valet" = "Laravel Valet"; "mi_domain_list" = "View Domains List..."; From 78e750d764645c615cb7bb19086697e9f5235ee9 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 10 Aug 2022 21:04:23 +0200 Subject: [PATCH 27/43] =?UTF-8?q?=E2=9C=A8=20Check=20if=20running=20the=20?= =?UTF-8?q?app=20with=20Rosetta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 6 +++ phpmon/Domain/App/App.swift | 3 ++ phpmon/Domain/App/EnvironmentCheck.swift | 45 +++++++++++++++++++ phpmon/Domain/App/Startup.swift | 38 ---------------- phpmon/Domain/Menu/MainMenu+Startup.swift | 10 +++-- phpmon/Domain/Menu/MainMenu.swift | 1 + phpmon/Domain/Menu/StatusMenu.swift | 17 +++++-- .../SwiftUI/Warning/WarningListView.swift | 19 ++++---- phpmon/Domain/Warnings/Warning.swift | 25 ++++++++++- phpmon/Domain/Warnings/WarningManager.swift | 39 +++++++++++++++- phpmon/Localizable.strings | 5 ++- 11 files changed, 148 insertions(+), 60 deletions(-) create mode 100644 phpmon/Domain/App/EnvironmentCheck.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 73601a5..9a94c35 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -166,6 +166,8 @@ C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; }; C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; }; C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; }; + C495F5AF28A42E080087F70A /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; }; + C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; }; C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; }; C4998F0B2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; }; C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; }; @@ -398,6 +400,7 @@ 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 = ""; }; C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = ""; }; + C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentCheck.swift; sourceTree = ""; }; C4998F092617633900B2526E /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = ""; }; C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListKindCell.swift; sourceTree = ""; }; C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = ""; }; @@ -890,6 +893,7 @@ C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */, C4EED88827A48778006D7272 /* InterAppHandler.swift */, C4D8016522B1584700C6DA1B /* Startup.swift */, + C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */, C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */, C40FE736282ABA4F00A302C2 /* AppVersion.swift */, C45E76132854A65300B4FE0C /* ServicesManager.swift */, @@ -1322,6 +1326,7 @@ C422DDAD28A2DAC600CEAC97 /* WarningsWindowController.swift in Sources */, C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */, C4811D2422D70A4700B5F6B3 /* App.swift in Sources */, + C495F5AF28A42E080087F70A /* EnvironmentCheck.swift in Sources */, C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */, C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */, 54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */, @@ -1453,6 +1458,7 @@ C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */, C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */, + C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */, C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */, C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */, C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */, diff --git a/phpmon/Domain/App/App.swift b/phpmon/Domain/App/App.swift index 5c377e3..df288d8 100644 --- a/phpmon/Domain/App/App.swift +++ b/phpmon/Domain/App/App.swift @@ -68,6 +68,9 @@ class App { /** The services manager, responsible for figuring out what services are active/inactive. */ var services = ServicesManager.shared + /** The warning manager, responsible for keeping track of warnings. */ + var warnings = WarningManager.shared + /** Timer that will periodically reload info about the user's PHP installation. */ var timer: Timer? diff --git a/phpmon/Domain/App/EnvironmentCheck.swift b/phpmon/Domain/App/EnvironmentCheck.swift new file mode 100644 index 0000000..a044ddf --- /dev/null +++ b/phpmon/Domain/App/EnvironmentCheck.swift @@ -0,0 +1,45 @@ +// +// EnvironmentCheck.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 10/08/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +/** + The `EnvironmentCheck` is used to defer the execution of all of these commands until necessary. + Checks that require an app restart will always lead to an alert and app termination shortly after. + */ +struct EnvironmentCheck { + let command: () async -> Bool + let name: String + let titleText: String + let subtitleText: String + let descriptionText: String + let buttonText: String + let requiresAppRestart: Bool + + init( + command: @escaping () async -> Bool, + name: String, + titleText: String, + subtitleText: String, + descriptionText: String = "", + buttonText: String = "OK", + requiresAppRestart: Bool = false + ) { + self.command = command + self.name = name + self.titleText = titleText + self.subtitleText = subtitleText + self.descriptionText = descriptionText + self.buttonText = buttonText + self.requiresAppRestart = requiresAppRestart + } + + public func succeeds() async -> Bool { + return await !self.command() + } +} diff --git a/phpmon/Domain/App/Startup.swift b/phpmon/Domain/App/Startup.swift index 34411db..589e938 100644 --- a/phpmon/Domain/App/Startup.swift +++ b/phpmon/Domain/App/Startup.swift @@ -238,42 +238,4 @@ class Startup { descriptionText: "startup.errors.valet_version_unknown.desc".localized ) ] - - // MARK: - EnvironmentCheck struct - - /** - The `EnvironmentCheck` is used to defer the execution of all of these commands until necessary. - Checks that require an app restart will always lead to an alert and app termination shortly after. - */ - struct EnvironmentCheck { - let command: () async -> Bool - let name: String - let titleText: String - let subtitleText: String - let descriptionText: String - let buttonText: String - let requiresAppRestart: Bool - - init( - command: @escaping () async -> Bool, - name: String, - titleText: String, - subtitleText: String, - descriptionText: String = "", - buttonText: String = "OK", - requiresAppRestart: Bool = false - ) { - self.command = command - self.name = name - self.titleText = titleText - self.subtitleText = subtitleText - self.descriptionText = descriptionText - self.buttonText = buttonText - self.requiresAppRestart = requiresAppRestart - } - - public func succeeds() async -> Bool { - return await !self.command() - } - } } diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index edd5c86..868c00f 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -79,21 +79,25 @@ extension MainMenu { // A non-default TLD is not officially supported since Valet 3.2.x Valet.notifyAboutUnsupportedTLD() + // Find out which services are active ServicesManager.shared.loadData() // Start the background refresh timer startSharedTimer() + // Check warnings + WarningManager.shared.evaluateWarnings() + // Update the stats Stats.incrementSuccessfulLaunchCount() Stats.evaluateSponsorMessageShouldBeDisplayed() // Present first launch screen if needed - #warning("The launch screen will be presented every time you launch the app.") - if Stats.successfulLaunchCount >= 1 && !isRunningSwiftUIPreview { + #warning("You should definitely tweak this view again") + if Stats.successfulLaunchCount == 0 && !isRunningSwiftUIPreview { Log.info("Should present the first launch screen!") DispatchQueue.main.async { - // OnboardingWindowController.show() + OnboardingWindowController.show() } } diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 3019c84..e70cdb1 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -64,6 +64,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate menu.addRemainingMenuItems() menu.addItem(NSMenuItem.separator()) + menu.addWarningsMenuItem() menu.addCoreMenuItems() menu.items.forEach({ (item) in diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index f27976c..b80acb5 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -74,12 +74,21 @@ class StatusMenu: NSMenu { self.addFirstAidAndServicesMenuItems() } + func addWarningsMenuItem() { + if !WarningManager.shared.hasWarnings() { + return + } + + self.addItem(NSMenuItem.separator()) + + let count = WarningManager.shared.warnings.count + self.addItem(NSMenuItem(title: (count == 1 ? "mi_warning" : "mi_warnings").localized(count), + action: #selector(MainMenu.openWarnings), keyEquivalent: "")) + } + func addCoreMenuItems() { self.addItem(NSMenuItem.separator()) - if (WarningManager.hasWarnings()) { - self.addItem(NSMenuItem(title: "mi_warnings".localized(2), - action: #selector(MainMenu.openWarnings), keyEquivalent: "")) - } + self.addItem(NSMenuItem(title: "mi_preferences".localized, action: #selector(MainMenu.openPrefs), keyEquivalent: ",")) self.addItem(NSMenuItem(title: "mi_check_for_updates".localized, diff --git a/phpmon/Domain/SwiftUI/Warning/WarningListView.swift b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift index 0417bf9..92c488e 100644 --- a/phpmon/Domain/SwiftUI/Warning/WarningListView.swift +++ b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift @@ -12,17 +12,14 @@ struct WarningListView: View { var body: some View { List { VStack(alignment: .leading) { - WarningView( - title: "warnings.arm_compatibility_title", - description: "warnings.arm_compatibility.description", - documentationUrl: "https://phpmon.app/documentation/apple-silicon-transition" - ) - Divider() - WarningView( - title: "warnings.helper_permissions_title", - description: "warnings.helper_permissions.description" - ) - Divider() + ForEach(WarningManager.shared.warnings) { warning in + WarningView( + title: warning.titleText, + description: warning.descriptionText, + documentationUrl: warning.url + ) + Divider() + } } } diff --git a/phpmon/Domain/Warnings/Warning.swift b/phpmon/Domain/Warnings/Warning.swift index ef629ef..d5a9ac0 100644 --- a/phpmon/Domain/Warnings/Warning.swift +++ b/phpmon/Domain/Warnings/Warning.swift @@ -8,6 +8,29 @@ import Foundation -struct Warning { +struct Warning: Identifiable { + var id = UUID() + let command: () async -> Bool + let name: String + let titleText: String + let descriptionText: String + let url: String? + init( + command: @escaping () async -> Bool, + name: String, + titleText: String, + descriptionText: String, + url: String? + ) { + self.command = command + self.name = name + self.titleText = titleText + self.descriptionText = descriptionText + self.url = url + } + + public func applies() async -> Bool { + return await self.command() + } } diff --git a/phpmon/Domain/Warnings/WarningManager.swift b/phpmon/Domain/Warnings/WarningManager.swift index 179988c..047d807 100644 --- a/phpmon/Domain/Warnings/WarningManager.swift +++ b/phpmon/Domain/Warnings/WarningManager.swift @@ -10,6 +10,43 @@ import Foundation class WarningManager { - // TODO: Singleton initialization at launch + static var shared = WarningManager() + + public let evaluations: [Warning] = [ + Warning( + command: { + return Shell.pipe("sysctl -n sysctl.proc_translated") + .trimmingCharacters(in: .whitespacesAndNewlines) == "1" + }, + name: "Running PHP Monitor with Rosetta on M1", + titleText: "warnings.arm_compatibility.title", + descriptionText: "warnings.arm_compatibility.description", + url: nil + ) + ] + + @Published public var warnings: [Warning] = [] + + public func hasWarnings() -> Bool { + return !warnings.isEmpty + } + + func evaluateWarnings() { + Task { await WarningManager.shared.checkEnvironment() } + } + + /** + Checks the user's environment and checks if any special warnings apply. + */ + func checkEnvironment() async { + self.warnings = [] + for check in self.evaluations { + if await check.applies() { + Log.info("[WARNING] \(check.name)") + self.warnings.append(check) + continue + } + } + } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index d9e0a3a..f36d415 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -55,6 +55,7 @@ "mi_detected_extensions" = "Detected Extensions"; "mi_no_extensions_detected" = "No additional extensions detected."; +"mi_warning" = "⚠️ %i Warning"; "mi_warnings" = "⚠️ %i Warnings..."; "mi_valet" = "Laravel Valet"; @@ -510,10 +511,10 @@ If you are seeing this message but are confused why this folder has gone missing "warnings.title" = "Warnings"; -"warnings.helper_permissions_title" = "Helpers could not be written!"; +"warnings.helper_permissions.title" = "Helpers could not be written!"; "warnings.helper_permissions.description" = "The helper files in `/usr/local/bin` could not be written because PHP Monitor does not have permission to write there."; -"warnings.arm_compatibility_title" = "You are running PHP Monitor using Rosetta"; +"warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta"; "warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew."; // ONBOARDING From 2a9c8e68302ac8d3f9edff1691a7631a4e3485e8 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 10 Aug 2022 21:20:11 +0200 Subject: [PATCH 28/43] =?UTF-8?q?=E2=9C=A8=20Check=20if=20`/usr/local/bin`?= =?UTF-8?q?=20helper=20is=20writable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Warnings/WarningManager.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/phpmon/Domain/Warnings/WarningManager.swift b/phpmon/Domain/Warnings/WarningManager.swift index 047d807..316c59d 100644 --- a/phpmon/Domain/Warnings/WarningManager.swift +++ b/phpmon/Domain/Warnings/WarningManager.swift @@ -7,12 +7,22 @@ // import Foundation +import Cocoa class WarningManager { static var shared = WarningManager() public let evaluations: [Warning] = [ + Warning( + command: { + !FileManager.default.isWritableFile(atPath: "/usr/local/bin/pm81") + }, + name: "`/usr/local/bin` not writable", + titleText: "warnings.helper_permissions.title", + descriptionText: "warnings.helper_permissions.description", + url: nil + ), Warning( command: { return Shell.pipe("sysctl -n sysctl.proc_translated") @@ -21,7 +31,7 @@ class WarningManager { name: "Running PHP Monitor with Rosetta on M1", titleText: "warnings.arm_compatibility.title", descriptionText: "warnings.arm_compatibility.description", - url: nil + url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-and-Apple-Silicon" ) ] From 0c59d14da50cb8225e5d1d1ff03a211afeb441bc Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Thu, 11 Aug 2022 22:41:47 +0200 Subject: [PATCH 29/43] =?UTF-8?q?=F0=9F=90=9B=20Fix=20`isWriteableFile:atP?= =?UTF-8?q?ath`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Warnings/WarningManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpmon/Domain/Warnings/WarningManager.swift b/phpmon/Domain/Warnings/WarningManager.swift index 316c59d..92260a0 100644 --- a/phpmon/Domain/Warnings/WarningManager.swift +++ b/phpmon/Domain/Warnings/WarningManager.swift @@ -16,7 +16,7 @@ class WarningManager { public let evaluations: [Warning] = [ Warning( command: { - !FileManager.default.isWritableFile(atPath: "/usr/local/bin/pm81") + !FileManager.default.isWritableFile(atPath: "/usr/local/bin/") }, name: "`/usr/local/bin` not writable", titleText: "warnings.helper_permissions.title", From 4e5b178e3643049cf3655308d87bb23cb5ac43eb Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 12 Aug 2022 20:50:27 +0200 Subject: [PATCH 30/43] =?UTF-8?q?=F0=9F=8F=97=20WIP:=20Tweaks=20to=20PHP?= =?UTF-8?q?=20Doctor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcschemes/PHP Monitor.xcscheme | 5 ++ phpmon/Domain/Menu/StatusMenu.swift | 2 +- .../SwiftUI/Warning/WarningListView.swift | 46 ++++++++++++----- .../Domain/SwiftUI/Warning/WarningView.swift | 49 ++++++++++--------- phpmon/Domain/Warnings/Warning.swift | 12 ++--- phpmon/Domain/Warnings/WarningManager.swift | 34 ++++++++----- .../Warnings/WarningsWindowController.swift | 4 +- phpmon/Localizable.strings | 14 +++--- 8 files changed, 105 insertions(+), 61 deletions(-) diff --git a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme index 6d711af..b6917c2 100644 --- a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme +++ b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme @@ -68,6 +68,11 @@ + + Bool let name: String - let titleText: String - let descriptionText: String + let title: String + let paragraphs: [String] let url: String? init( command: @escaping () async -> Bool, name: String, - titleText: String, - descriptionText: String, + title: String, + paragraphs: [String], url: String? ) { self.command = command self.name = name - self.titleText = titleText - self.descriptionText = descriptionText + self.title = title + self.paragraphs = paragraphs self.url = url } diff --git a/phpmon/Domain/Warnings/WarningManager.swift b/phpmon/Domain/Warnings/WarningManager.swift index 92260a0..1365fcc 100644 --- a/phpmon/Domain/Warnings/WarningManager.swift +++ b/phpmon/Domain/Warnings/WarningManager.swift @@ -13,25 +13,31 @@ class WarningManager { static var shared = WarningManager() + init() { + if isRunningSwiftUIPreview { + self.warnings = self.evaluations + } + } + public let evaluations: [Warning] = [ - Warning( - command: { - !FileManager.default.isWritableFile(atPath: "/usr/local/bin/") - }, - name: "`/usr/local/bin` not writable", - titleText: "warnings.helper_permissions.title", - descriptionText: "warnings.helper_permissions.description", - url: nil - ), Warning( command: { return Shell.pipe("sysctl -n sysctl.proc_translated") .trimmingCharacters(in: .whitespacesAndNewlines) == "1" }, name: "Running PHP Monitor with Rosetta on M1", - titleText: "warnings.arm_compatibility.title", - descriptionText: "warnings.arm_compatibility.description", + title: "warnings.arm_compatibility.title", + paragraphs: ["warnings.arm_compatibility.description"], url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-and-Apple-Silicon" + ), + Warning( + command: { + !FileManager.default.isWritableFile(atPath: "/usr/local/bin/") + }, + name: "`/usr/local/bin` not writable", + title: "warnings.helper_permissions.title", + paragraphs: ["warnings.helper_permissions.description", "warnings.helper_permissions.unavailable"], + url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries" ) ] @@ -50,6 +56,7 @@ class WarningManager { */ func checkEnvironment() async { self.warnings = [] + for check in self.evaluations { if await check.applies() { Log.info("[WARNING] \(check.name)") @@ -57,6 +64,11 @@ class WarningManager { continue } } + + // For debugging purposes, we may wish to see all possible evaluations listed + if ProcessInfo.processInfo.environment["EXTREME_DOCTOR_MODE"] != nil { + self.warnings = self.evaluations + } } } diff --git a/phpmon/Domain/Warnings/WarningsWindowController.swift b/phpmon/Domain/Warnings/WarningsWindowController.swift index 65b1c25..4fda741 100644 --- a/phpmon/Domain/Warnings/WarningsWindowController.swift +++ b/phpmon/Domain/Warnings/WarningsWindowController.swift @@ -22,12 +22,12 @@ class WarningsWindowController: PMWindowController { windowController.window = NSWindow() guard let window = windowController.window else { return } - window.title = "warnings.title".localized + window.title = "" window.styleMask = [.titled, .closable, .miniaturizable] window.titlebarAppearsTransparent = true window.delegate = delegate ?? windowController window.contentView = NSHostingView(rootView: WarningListView()) - window.setContentSize(NSSize(width: 600, height: 300)) + window.setContentSize(NSSize(width: 600, height: 480)) App.shared.warningsWindowController = windowController } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index f36d415..841c6cf 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -55,8 +55,7 @@ "mi_detected_extensions" = "Detected Extensions"; "mi_no_extensions_detected" = "No additional extensions detected."; -"mi_warning" = "⚠️ %i Warning"; -"mi_warnings" = "⚠️ %i Warnings..."; +"mi_warnings" = "(%i) PHP Doctor..."; "mi_valet" = "Laravel Valet"; "mi_domain_list" = "View Domains List..."; @@ -509,12 +508,15 @@ If you are seeing this message but are confused why this folder has gone missing // WARNINGS -"warnings.title" = "Warnings"; +"warnings.title" = "PHP Doctor"; +"warnings.description" = "**PHP Doctor** will suggest improvements to your active system configuration."; +"warnings.disclaimer" = "You may choose to hide all recommendations from the PHP Monitor menu in Preferences, but it is recommended that you deal with all actionable items."; -"warnings.helper_permissions.title" = "Helpers could not be written!"; -"warnings.helper_permissions.description" = "The helper files in `/usr/local/bin` could not be written because PHP Monitor does not have permission to write there."; +"warnings.helper_permissions.title" = "PHP Monitor’s helpers are currently unavailable."; +"warnings.helper_permissions.description" = "PHP Monitor comes with various helper binaries. Using these binaries allows you to easily invoke a specific version of PHP without switching the linked PHP version."; +"warnings.helper_permissions.unavailable" = "However, these helpers are currently *unavailable* because PHP Monitor could not create the required symlinks (alternatively, you could add PHP Monitor's helper directory to your `PATH` variable to make this warning go away as well)."; -"warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta"; +"warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta."; "warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew."; // ONBOARDING From cfbb83976d226aed3dcf2065fbf60e267a95e0b9 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 12 Aug 2022 21:07:38 +0200 Subject: [PATCH 31/43] =?UTF-8?q?=F0=9F=91=8C=20Tweak=20preview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/SwiftUI/Warning/WarningView.swift | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/phpmon/Domain/SwiftUI/Warning/WarningView.swift b/phpmon/Domain/SwiftUI/Warning/WarningView.swift index 1497934..ec47af2 100644 --- a/phpmon/Domain/SwiftUI/Warning/WarningView.swift +++ b/phpmon/Domain/SwiftUI/Warning/WarningView.swift @@ -49,6 +49,21 @@ struct WarningView: View { struct WarningView_Previews: PreviewProvider { static var previews: some View { - WarningListView().frame(width: 600, height: 480) + WarningView( + title: "warnings.helper_permissions.title", + paragraphs: ["warnings.helper_permissions.description"], + documentationUrl: "https://nicoverbruggen.be" + ) + .frame(width: 600, height: 105) + + WarningView( + title: "warnings.helper_permissions.title", + paragraphs: ["warnings.helper_permissions.description"], + documentationUrl: "https://nicoverbruggen.be" + ) + .preferredColorScheme(.dark) + .frame(width: 600, height: 105) + + // WarningListView().frame(width: 600, height: 580) } } From 2ee0934080d7539fff6eb2c1e54d46f522c72398 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 13 Aug 2022 23:16:10 +0200 Subject: [PATCH 32/43] =?UTF-8?q?=F0=9F=94=A7=20Make=20PHP=20Doctor=20opti?= =?UTF-8?q?onal=20(preference)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcshareddata/xcschemes/PHP Monitor.xcscheme | 2 +- phpmon/Domain/Menu/StatusMenu.swift | 3 ++- phpmon/Domain/Preferences/Preferences.swift | 4 ++++ phpmon/Domain/Preferences/PrefsVC.swift | 15 +++++++++++++++ phpmon/Localizable.strings | 4 ++++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme index b6917c2..730932a 100644 --- a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme +++ b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme @@ -71,7 +71,7 @@ + isEnabled = "NO"> NSView { + return CheckboxPreferenceView.make( + sectionText: "prefs.php_doctor".localized, + descriptionText: "prefs.php_doctor_suggestions_desc".localized, + checkboxText: "prefs.php_doctor_suggestions_title".localized, + preference: .showPhpDoctorSuggestions, + action: { + MainMenu.shared.refreshIcon() + MainMenu.shared.rebuild() + } + ) + + } + func getNotifyAboutVersionChangePV() -> NSView { return CheckboxPreferenceView.make( sectionText: "prefs.notifications".localized, @@ -194,6 +208,7 @@ class GeneralPreferencesVC: GenericPreferenceVC { .instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC vc.views = [ + vc.getShowPhpDoctorSuggestionsPV(), vc.getAutoRestartPV(), vc.getAutomaticComposerUpdatePV(), vc.getShortcutPV(), diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 841c6cf..39c0a1d 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -212,6 +212,7 @@ "prefs.info_density" = "Info Density:"; "prefs.services" = "Services:"; "prefs.switcher" = "Switcher:"; +"prefs.php_doctor" = "PHP Doctor:"; "prefs.integrations" = "Integrations:"; "prefs.updates" = "Updates:"; "prefs.notifications" = "Notifications:"; @@ -239,6 +240,9 @@ "prefs.automatic_update_check_title" = "Automatically check for updates"; "prefs.automatic_update_check_desc" = "When checked, PHP Monitor will automatically check if there is a newer version available, and notify you if that is the case."; + +"prefs.php_doctor_suggestions_title" = "Show suggestions (if applicable)"; +"prefs.php_doctor_suggestions_desc" = "If you uncheck this item, no PHP Doctor suggestions will appear in PHP Monitor's menu. Keep in mind that PHP Doctor will not appear if there are no recommendations."; "prefs.shortcut_set" = "Set global shortcut"; "prefs.shortcut_listening" = ""; From 48f950fc3a0461ef8a2b12a85a7d93bdd684a3cd Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 14 Aug 2022 22:22:06 +0200 Subject: [PATCH 33/43] =?UTF-8?q?=F0=9F=90=9B=20Fix=20translation=20string?= =?UTF-8?q?=20(#185)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ensures that the "failed to check for update" modal now displays the correct localized string. --- phpmon/Domain/App/AppUpdateChecker.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/phpmon/Domain/App/AppUpdateChecker.swift b/phpmon/Domain/App/AppUpdateChecker.swift index 4620ccd..7ac48da 100644 --- a/phpmon/Domain/App/AppUpdateChecker.swift +++ b/phpmon/Domain/App/AppUpdateChecker.swift @@ -161,14 +161,14 @@ class AppUpdateChecker { private static func notifyAboutConnectionIssue() { DispatchQueue.main.async { BetterAlert().withInformation( - title: "updater.errors.cannot_check_for_update.title".localized, - subtitle: "updater.errors.cannot_check_for_update.subtitle".localized, - description: "updater.errors.cannot_check_for_update.description".localized( + title: "updater.alerts.cannot_check_for_update.title".localized, + subtitle: "updater.alerts.cannot_check_for_update.subtitle".localized, + description: "updater.alerts.cannot_check_for_update.description".localized( App.version ) ) .withTertiary( - text: "updater.errors.buttons.releases_on_github".localized, + text: "updater.alerts.buttons.releases_on_github".localized, action: { _ in NSWorkspace.shared.open(Constants.Urls.GitHubReleases) } From 237185995da5538c378f52041e15af4eb2aa401f Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 14 Aug 2022 22:23:39 +0200 Subject: [PATCH 34/43] =?UTF-8?q?=F0=9F=94=A5=20Removed=20assets=20from=20?= =?UTF-8?q?tour?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Assets.xcassets/Tour/Contents.json | 6 ----- .../Tour/Tour.Domains.imageset/Contents.json | 21 ------------------ .../Tour/Tour.Domains.imageset/tour3.heic | Bin 41049 -> 0 bytes .../Tour.Isolation.imageset/Contents.json | 21 ------------------ .../Tour/Tour.Isolation.imageset/tour4.heic | Bin 55194 -> 0 bytes .../Tour/Tour.MenuBar.imageset/Contents.json | 21 ------------------ .../Tour/Tour.MenuBar.imageset/tour2.heic | Bin 26215 -> 0 bytes 7 files changed, 69 deletions(-) delete mode 100644 phpmon/Assets.xcassets/Tour/Contents.json delete mode 100644 phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/Contents.json delete mode 100644 phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/tour3.heic delete mode 100644 phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/Contents.json delete mode 100644 phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/tour4.heic delete mode 100644 phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/Contents.json delete mode 100644 phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/tour2.heic diff --git a/phpmon/Assets.xcassets/Tour/Contents.json b/phpmon/Assets.xcassets/Tour/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/phpmon/Assets.xcassets/Tour/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/Contents.json b/phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/Contents.json deleted file mode 100644 index 87011c7..0000000 --- a/phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "tour3.heic", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/tour3.heic b/phpmon/Assets.xcassets/Tour/Tour.Domains.imageset/tour3.heic deleted file mode 100644 index 2c64a6dff07b789a6612db697d11cea6b96bcbd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41049 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%0Dk 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 Q&xCUS?*L@`PvOA-2dBX=2mk;8 diff --git a/phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/Contents.json b/phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/Contents.json deleted file mode 100644 index 4dfdc53..0000000 --- a/phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "tour4.heic", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/tour4.heic b/phpmon/Assets.xcassets/Tour/Tour.Isolation.imageset/tour4.heic deleted file mode 100644 index e9f37f8a1a76d00c42f639b4dc2b9aaf8a64e850..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55194 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 T6~KSrL^9%!(GB7s0FeJ57EGT* diff --git a/phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/Contents.json b/phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/Contents.json deleted file mode 100644 index e430b0e..0000000 --- a/phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "tour2.heic", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/tour2.heic b/phpmon/Assets.xcassets/Tour/Tour.MenuBar.imageset/tour2.heic deleted file mode 100644 index 9bb7f6f043e3e10d9e76bc9c9f89ad2e3698aeed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26215 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

lutKZbqLqFKAK52~+rmh|gA*x+)2) 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 K|0*A^ukB3k;#dy= From bbbdce6b4408a95bc8f07262f9f88c76d2f3a4d9 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 14 Aug 2022 23:57:46 +0200 Subject: [PATCH 35/43] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Reworked=20helper=20?= =?UTF-8?q?scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 'Welcome Tour' to First Aid menu - Updated 'Welcome Tour' - Helpers are now always written to ~/.config/phpmon/bin - Updated helpers (now symlinked) - Updated checks for when to symlink helpers --- PHP Monitor.xcodeproj/project.pbxproj | 4 +- phpmon/Common/Core/Paths.swift | 7 +++ phpmon/Common/Helpers/Filesystem.swift | 9 ++++ phpmon/Common/PHP/PHP Version/PhpHelper.swift | 44 ++++++++++++++++++- phpmon/Domain/Menu/MainMenu+Startup.swift | 7 ++- phpmon/Domain/Menu/MainMenu.swift | 10 +++++ phpmon/Domain/Menu/StatusMenu+Items.swift | 29 ++++++------ .../SwiftUI/Onboarding/OnboardingView.swift | 27 ++++++++---- phpmon/Domain/Warnings/WarningManager.swift | 11 +++-- phpmon/Localizable.strings | 19 +++++--- 10 files changed, 124 insertions(+), 43 deletions(-) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 9a94c35..bd55d39 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -1677,7 +1677,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 920; + CURRENT_PROJECT_VERSION = 950; DEBUG = YES; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; @@ -1704,7 +1704,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 920; + CURRENT_PROJECT_VERSION = 950; DEBUG = NO; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; diff --git a/phpmon/Common/Core/Paths.swift b/phpmon/Common/Core/Paths.swift index 465951b..6a0a4ba 100644 --- a/phpmon/Common/Core/Paths.swift +++ b/phpmon/Common/Core/Paths.swift @@ -19,9 +19,12 @@ public class Paths { private var userName: String + private var PATH: String + init() { baseDir = App.architecture != "x86_64" ? .opt : .usr userName = String(Shell.pipe("whoami").split(separator: "\n")[0]) + PATH = String(Shell.pipe("echo $PATH")).trimmingCharacters(in: .whitespacesAndNewlines) } public func detectBinaryPaths() { @@ -57,6 +60,10 @@ public class Paths { return shared.userName } + public static var PATH: String { + return shared.PATH + } + public static var cellarPath: String { return "\(shared.baseDir.rawValue)/Cellar" } diff --git a/phpmon/Common/Helpers/Filesystem.swift b/phpmon/Common/Helpers/Filesystem.swift index a54f665..652ae44 100644 --- a/phpmon/Common/Helpers/Filesystem.swift +++ b/phpmon/Common/Helpers/Filesystem.swift @@ -33,6 +33,15 @@ class Filesystem { return exists && !isDirectory.boolValue } + public static func fileIsSymlink(_ path: String) -> Bool { + do { + let attribs = try FileManager.default.attributesOfItem(atPath: path) + return attribs[.type] as! FileAttributeType == FileAttributeType.typeSymbolicLink + } catch { + return false + } + } + /** Checks if a directory exists at the provided path. */ diff --git a/phpmon/Common/PHP/PHP Version/PhpHelper.swift b/phpmon/Common/PHP/PHP Version/PhpHelper.swift index 33935fa..cc5c9bb 100644 --- a/phpmon/Common/PHP/PHP Version/PhpHelper.swift +++ b/phpmon/Common/PHP/PHP Version/PhpHelper.swift @@ -16,8 +16,18 @@ class PhpHelper { // Take the PHP version (e.g. "7.2") and generate a dotless version let dotless = version.replacingOccurrences(of: ".", with: "") + // Determine the dotless name for this PHP version + let destination = "/Users/\(Paths.whoami)/.config/phpmon/bin/pm\(dotless)" + + // Check if the ~/.config/phpmon/bin directory is in the PATH + let inPath = Paths.PATH.contains("/Users/\(Paths.whoami)/.config/phpmon/bin") + + // Check if we can create symlinks (`/usr/local/bin` must be writable) + let canWriteSymlinks = FileManager.default.isWritableFile(atPath: "/usr/local/bin/") + do { - let destination = "/usr/local/bin/pm\(dotless)" + Shell.run("mkdir -p ~/.config/phpmon/bin") + if FileManager.default.fileExists(atPath: destination) { let contents = try String(contentsOfFile: destination) if !contents.contains(keyPhrase) { @@ -52,10 +62,40 @@ class PhpHelper { // Make sure the file is executable Shell.run("chmod +x \(destination)") + + // Create a symlink if the folder is not in the PATH + if !inPath { + // First, check if we can create symlinks at all + if !canWriteSymlinks { + Log.err("PHP Monitor does not have permission to symlink `/usr/local/bin/\(dotless)`.") + return + } + + // Write the symlink + self.createSymlink(dotless) + } } catch { print(error) - Log.err("Could not write PHP Monitor helper for PHP \(version) to /usr/local/bin/pm\(dotless)") + Log.err("Could not write PHP Monitor helper for PHP \(version) to \(destination))") } } + private static func createSymlink(_ dotless: String) { + let source = "/Users/\(Paths.whoami)/.config/phpmon/bin/pm\(dotless)" + let destination = "/usr/local/bin/pm\(dotless)" + + if !Filesystem.fileExists(destination) { + Log.info("Creating new symlink: \(destination)") + Shell.run("ln -s \(source) \(destination)") + return + } + + if !Filesystem.fileIsSymlink(destination) { + Log.info("Overwriting existing file with new symlink: \(destination)") + Shell.run("ln -fs \(source) \(destination)") + return + } + + Log.info("Symlink in \(destination) already exists, OK.") + } } diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 868c00f..6cc8e4b 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -57,6 +57,9 @@ extension MainMenu { let installation = PhpEnv.phpInstall installation.notifyAboutBrokenPhpFpm() + // Check for other problems + WarningManager.shared.evaluateWarnings() + // Set up the config watchers on launch (updated automatically when switching) Log.info("Setting up watchers...") App.shared.handlePhpConfigWatcher() @@ -85,15 +88,11 @@ extension MainMenu { // Start the background refresh timer startSharedTimer() - // Check warnings - WarningManager.shared.evaluateWarnings() - // Update the stats Stats.incrementSuccessfulLaunchCount() Stats.evaluateSponsorMessageShouldBeDisplayed() // Present first launch screen if needed - #warning("You should definitely tweak this view again") if Stats.successfulLaunchCount == 0 && !isRunningSwiftUIPreview { Log.info("Should present the first launch screen!") DispatchQueue.main.async { diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index e70cdb1..8e4cfff 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -126,6 +126,16 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate ServicesManager.shared.loadData() } + /** + Shows the Welcome Tour screen, again. + Did this need a comment? No, probably not. + */ + @objc func showWelcomeTour() { + DispatchQueue.main.async { + OnboardingWindowController.show() + } + } + /** Reloads the menu in the background, using `asyncExecution`. */ @objc func reloadPhpMonitorMenuInBackground() { asyncExecution({ diff --git a/phpmon/Domain/Menu/StatusMenu+Items.swift b/phpmon/Domain/Menu/StatusMenu+Items.swift index 58c95be..98aaf86 100644 --- a/phpmon/Domain/Menu/StatusMenu+Items.swift +++ b/phpmon/Domain/Menu/StatusMenu+Items.swift @@ -199,6 +199,10 @@ extension StatusMenu { let servicesMenu = NSMenu() servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_first_aid".localized)) + + servicesMenu.addItem(NSMenuItem(title: "mi_view_onboarding".localized, + action: #selector(MainMenu.showWelcomeTour), keyEquivalent: "")) + let fixMyValetMenuItem = NSMenuItem( title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion), action: #selector(MainMenu.fixMyValet), keyEquivalent: "" @@ -216,22 +220,15 @@ extension StatusMenu { servicesMenu.addItem(NSMenuItem.separator()) servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized)) - servicesMenu.addItem( - NSMenuItem(title: "mi_restart_dnsmasq".localized, - action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d") - ) - servicesMenu.addItem( - NSMenuItem(title: "mi_restart_php_fpm".localized, - action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p") - ) - servicesMenu.addItem( - NSMenuItem(title: "mi_restart_nginx".localized, - action: #selector(MainMenu.restartNginx), keyEquivalent: "n") - ) - servicesMenu.addItem( - NSMenuItem(title: "mi_restart_valet_services".localized, - action: #selector(MainMenu.restartValetServices), keyEquivalent: "s") - ) + servicesMenu.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized, + action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d")) + servicesMenu.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized, + action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p")) + + servicesMenu.addItem(NSMenuItem(title: "mi_restart_nginx".localized, + action: #selector(MainMenu.restartNginx), keyEquivalent: "n")) + servicesMenu.addItem(NSMenuItem(title: "mi_restart_valet_services".localized, + action: #selector(MainMenu.restartValetServices), keyEquivalent: "s")) servicesMenu.addItem( NSMenuItem(title: "mi_stop_valet_services".localized, action: #selector(MainMenu.stopValetServices), keyEquivalent: "s"), diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift index cf81c48..34a2c0f 100644 --- a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift @@ -25,18 +25,17 @@ struct OnboardingTextItem: View { Text(title.localizedForSwiftUI) .font(.system(size: 14)) .lineLimit(3) - HStack { - Text(description.localizedForSwiftUI) - .foregroundColor(Color.secondary) - .font(.system(size: 13)) - .lineLimit(3) - .fixedSize(horizontal: false, vertical: true) - .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) - } + Text(description.localizedForSwiftUI) + .foregroundColor(Color.secondary) + .font(.system(size: 13)) + .lineLimit(6) + .fixedSize(horizontal: false, vertical: true) + .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) } } .padding() - .overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.gray.opacity(0.3), lineWidth: 1)) + .overlay(RoundedRectangle(cornerRadius: 5) + .stroke(Color.gray.opacity(0.3), lineWidth: 1)) } } @@ -57,9 +56,13 @@ struct OnboardingView: View { .padding(.bottom, 5) Text("onboarding.explore".localized) .padding(.bottom) + .padding(.trailing) } .padding(.top, 10) } + .padding(.leading) + .padding(.trailing) + VStack { VStack(alignment: .leading, spacing: 10) { OnboardingTextItem( @@ -67,6 +70,11 @@ struct OnboardingView: View { title: "onboarding.tour.menu_bar.title", description: "onboarding.tour.menu_bar" ) + OnboardingTextItem( + icon: "checkmark.circle.fill", + title: "onboarding.tour.services.title", + description: "onboarding.tour.services" + ) OnboardingTextItem( icon: "list.bullet.circle.fill", title: "onboarding.tour.domains.title", @@ -79,6 +87,7 @@ struct OnboardingView: View { ) } }.padding() + VStack(spacing: 20) { HStack { Image(systemName: "questionmark.circle.fill") diff --git a/phpmon/Domain/Warnings/WarningManager.swift b/phpmon/Domain/Warnings/WarningManager.swift index 1365fcc..9f65c4a 100644 --- a/phpmon/Domain/Warnings/WarningManager.swift +++ b/phpmon/Domain/Warnings/WarningManager.swift @@ -32,11 +32,16 @@ class WarningManager { ), Warning( command: { + !Paths.PATH.contains("/Users/\(Paths.whoami)/.config/phpmon/bin") && !FileManager.default.isWritableFile(atPath: "/usr/local/bin/") }, - name: "`/usr/local/bin` not writable", + name: "Helpers cannot be symlinked and not in PATH", title: "warnings.helper_permissions.title", - paragraphs: ["warnings.helper_permissions.description", "warnings.helper_permissions.unavailable"], + paragraphs: [ + "warnings.helper_permissions.description", + "warnings.helper_permissions.unavailable", + "warnings.helper_permissions.symlink" + ], url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries" ) ] @@ -59,7 +64,7 @@ class WarningManager { for check in self.evaluations { if await check.applies() { - Log.info("[WARNING] \(check.name)") + Log.info("[DOCTOR] \(check.name) (!)") self.warnings.append(check) continue } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 39c0a1d..c090432 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -74,6 +74,8 @@ "mi_no_presets" = "No presets available."; "mi_set_up_presets" = "Learn more about presets..."; +"mi_view_onboarding" = "Show Welcome Tour..."; + "mi_xdebug_available_modes" = "Available Modes"; "mi_xdebug_actions" = "Actions"; "mi_xdebug_disable_all" = "Disable All Modes"; @@ -518,7 +520,8 @@ If you are seeing this message but are confused why this folder has gone missing "warnings.helper_permissions.title" = "PHP Monitor’s helpers are currently unavailable."; "warnings.helper_permissions.description" = "PHP Monitor comes with various helper binaries. Using these binaries allows you to easily invoke a specific version of PHP without switching the linked PHP version."; -"warnings.helper_permissions.unavailable" = "However, these helpers are currently *unavailable* because PHP Monitor could not create the required symlinks (alternatively, you could add PHP Monitor's helper directory to your `PATH` variable to make this warning go away as well)."; +"warnings.helper_permissions.unavailable" = "However, these helpers are potentially *unavailable* because PHP Monitor cannot currently create or update the required symlinks."; +"warnings.helper_permissions.symlink" = "If you do not wish to make `/usr/local/bin` writable, you can add PHP Monitor's helper directory to your `PATH` variable to make this warning go away. (Click on ”Learn More” to find out how to fix this issue.)"; "warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta."; "warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew."; @@ -527,13 +530,15 @@ If you are seeing this message but are confused why this folder has gone missing "onboarding.title" = "Welcome Tour"; "onboarding.welcome" = "Welcome to PHP Monitor!"; -"onboarding.explore" = "Learn more about some of the features that PHP Monitor has to offer."; +"onboarding.explore" = "Learn more about some of the features that PHP Monitor has to offer. You can find a more comprehensive list of features on GitHub."; "onboarding.tour.menu_bar.title" = "Get Started"; "onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From here, you can switch the globally linked PHP version, start or stop services, locate config files, and more."; "onboarding.tour.faq_hint" = "I recommend that you check out the [README](https://github.com/nicoverbruggen/phpmon/blob/main/README.md) on GitHub: it contains a comprehensive FAQ with various tips and common questions and answers."; -"onboarding.tour.domains.title" = "Domains"; -"onboarding.tour.domains" = "By opening the Domains window via the Menu Bar item, you can view which domains are linked and parked."; -"onboarding.tour.isolation.title" = "Isolation"; -"onboarding.tour.isolation" = "If you have Valet 3 installed, you can even use domain isolation by right-clicking on a given domain in the Domains window. This allows you to pick a specific version of PHP to use for that domain!"; -"onboarding.tour.once" = "You will only see the Welcome Tour once. You can re-open the Welcome Tour later via the menu bar icon."; +"onboarding.tour.services.title" = "Manage Services"; +"onboarding.tour.services" = "Once you click on the menu bar item, you can see at a glance based on the checkmarks or crosses if all of the Homebrew services are up and running. You can also click on a service to quickly toggle it. You can also add your own!"; +"onboarding.tour.domains.title" = "Manage Domains"; +"onboarding.tour.domains" = "By opening the Domains window via the menu bar item, you can view which domains are linked and parked, as well as active nginx proxies."; +"onboarding.tour.isolation.title" = "Isolate Domains"; +"onboarding.tour.isolation" = "If you have Valet 3 installed, you can even use domain isolation by right-clicking on a given domain in the Domains window. This allows you to pick a specific version of PHP to use for that domain, and that domain only!"; +"onboarding.tour.once" = "You will only see the Welcome Tour once. You can re-open the Welcome Tour later via the menu bar icon (under First Aid & Services)."; "onboarding.tour.close" = "Close Tour"; From a9f9c38e0dc5c8155f3de1b0e3aaa45485d7d5b4 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 15 Aug 2022 01:47:55 +0200 Subject: [PATCH 36/43] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rework=20how=20the?= =?UTF-8?q?=20user's=20PATH=20is=20loaded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 6 + phpmon/Common/Core/Paths.swift | 7 - phpmon/Common/Core/Shell+PATH.swift | 27 ++++ phpmon/Common/PHP/PHP Version/PhpHelper.swift | 2 +- phpmon/Credits.html | 6 +- phpmon/Domain/Menu/MainMenu.swift | 1 - phpmon/Domain/Menu/StatusMenu+Items.swift | 1 + phpmon/Domain/Menu/StatusMenu.swift | 23 ++- .../SwiftUI/Onboarding/OnboardingView.swift | 143 +++++++++--------- .../SwiftUI/Warning/WarningListView.swift | 18 ++- phpmon/Domain/Warnings/WarningManager.swift | 6 +- phpmon/Localizable.strings | 16 +- 12 files changed, 154 insertions(+), 102 deletions(-) create mode 100644 phpmon/Common/Core/Shell+PATH.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index bd55d39..cbdae3f 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -98,6 +98,8 @@ C42CFB1627DFDE7900862737 /* nginx-site.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1527DFDE7900862737 /* nginx-site.test */; }; C42CFB1827DFDFDC00862737 /* nginx-site-isolated.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */; }; C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */; }; + C42E3BF428A9BF5100AFECFC /* Shell+PATH.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */; }; + C42E3BF528A9BF5100AFECFC /* Shell+PATH.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */; }; C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; }; C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; }; C42F26762805FEE200938AC7 /* nginx-secure-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */; }; @@ -356,6 +358,7 @@ C42CFB1527DFDE7900862737 /* nginx-site.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site.test"; sourceTree = ""; }; C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site-isolated.test"; sourceTree = ""; }; C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationTest.swift; sourceTree = ""; }; + C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Shell+PATH.swift"; sourceTree = ""; }; C42F26722805B4B400938AC7 /* DomainListable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListable.swift; sourceTree = ""; }; C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-secure-proxy.test"; sourceTree = ""; }; C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Notifications.swift"; sourceTree = ""; }; @@ -608,6 +611,7 @@ C4B5853D2770FE3900DA4FBE /* Command.swift */, C4B5853B2770FE3900DA4FBE /* Paths.swift */, C4B5853C2770FE3900DA4FBE /* Shell.swift */, + C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */, C4C1019A27C65C6F001FACC2 /* Process.swift */, C40C7F2F27722E8D00DDDCDC /* Logger.swift */, C417DC73277614690015E6EE /* Helpers.swift */, @@ -1386,6 +1390,7 @@ C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */, C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */, C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */, + C42E3BF428A9BF5100AFECFC /* Shell+PATH.swift in Sources */, C42337A3281F19F000459A48 /* Xdebug.swift in Sources */, C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */, @@ -1430,6 +1435,7 @@ C449B4F027EE7FB800C47E8A /* DomainListTLSCell.swift in Sources */, C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */, C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */, + C42E3BF528A9BF5100AFECFC /* Shell+PATH.swift in Sources */, C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */, C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, C4F319C927B034A500AFF46F /* Stats.swift in Sources */, diff --git a/phpmon/Common/Core/Paths.swift b/phpmon/Common/Core/Paths.swift index 6a0a4ba..465951b 100644 --- a/phpmon/Common/Core/Paths.swift +++ b/phpmon/Common/Core/Paths.swift @@ -19,12 +19,9 @@ public class Paths { private var userName: String - private var PATH: String - init() { baseDir = App.architecture != "x86_64" ? .opt : .usr userName = String(Shell.pipe("whoami").split(separator: "\n")[0]) - PATH = String(Shell.pipe("echo $PATH")).trimmingCharacters(in: .whitespacesAndNewlines) } public func detectBinaryPaths() { @@ -60,10 +57,6 @@ public class Paths { return shared.userName } - public static var PATH: String { - return shared.PATH - } - public static var cellarPath: String { return "\(shared.baseDir.rawValue)/Cellar" } diff --git a/phpmon/Common/Core/Shell+PATH.swift b/phpmon/Common/Core/Shell+PATH.swift new file mode 100644 index 0000000..cd29071 --- /dev/null +++ b/phpmon/Common/Core/Shell+PATH.swift @@ -0,0 +1,27 @@ +// +// Shell+PATH.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 15/08/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +extension Shell { + + var PATH: String { + let task = Process() + task.launchPath = "/bin/zsh" + + // We need an interactive shell so the user's PATH is loaded in correctly + task.arguments = ["--login", "-ilc", "echo $PATH"] + + let pipe = Pipe() + task.standardOutput = pipe + task.launch() + let data = pipe.fileHandleForReading.readDataToEndOfFile() + + return String(data: data, encoding: String.Encoding.utf8) ?? "" + } +} diff --git a/phpmon/Common/PHP/PHP Version/PhpHelper.swift b/phpmon/Common/PHP/PHP Version/PhpHelper.swift index cc5c9bb..9224635 100644 --- a/phpmon/Common/PHP/PHP Version/PhpHelper.swift +++ b/phpmon/Common/PHP/PHP Version/PhpHelper.swift @@ -20,7 +20,7 @@ class PhpHelper { let destination = "/Users/\(Paths.whoami)/.config/phpmon/bin/pm\(dotless)" // Check if the ~/.config/phpmon/bin directory is in the PATH - let inPath = Paths.PATH.contains("/Users/\(Paths.whoami)/.config/phpmon/bin") + let inPath = Shell.user.PATH.contains("/Users/\(Paths.whoami)/.config/phpmon/bin") // Check if we can create symlinks (`/usr/local/bin` must be writable) let canWriteSymlinks = FileManager.default.isWritableFile(atPath: "/usr/local/bin/") diff --git a/phpmon/Credits.html b/phpmon/Credits.html index 40bcc83..d2e9a84 100644 --- a/phpmon/Credits.html +++ b/phpmon/Credits.html @@ -13,9 +13,9 @@
-

Want to spread the love? Leave a star on GitHub!

-

Having issues? Consult the FAQ & Troubleshooting section.

-

Want to support me? You can financially support the continued development of this app.

+

Do you enjoy using the app? Leave a star on GitHub!

+

Having issues? Consult the FAQ section, I did my best to ensure everything is documented.

+

Want to support further development of PHP Monitor? You can financially support the continued development of this app.

Get the latest on Twitter Give me a follow on Twitter to learn about the latest and greatest updates of this app.


diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 8e4cfff..7fe10b6 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -64,7 +64,6 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate menu.addRemainingMenuItems() menu.addItem(NSMenuItem.separator()) - menu.addWarningsMenuItem() menu.addCoreMenuItems() menu.items.forEach({ (item) in diff --git a/phpmon/Domain/Menu/StatusMenu+Items.swift b/phpmon/Domain/Menu/StatusMenu+Items.swift index 98aaf86..1259185 100644 --- a/phpmon/Domain/Menu/StatusMenu+Items.swift +++ b/phpmon/Domain/Menu/StatusMenu+Items.swift @@ -157,6 +157,7 @@ extension StatusMenu { return } + self.addItem(NSMenuItem.separator()) let xdebugSwitch = NSMenuItem( title: "mi_xdebug_mode".localized, action: nil, diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 1c37355..8ece249 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -66,25 +66,34 @@ class StatusMenu: NSMenu { self.addExtensionsMenuItems() + self.addXdebugMenuItem() + + self.addPhpDoctorMenuItem() + self.addItem(NSMenuItem.separator()) - self.addXdebugMenuItem() self.addPresetsMenuItem() - self.addFirstAidAndServicesMenuItems() } - func addWarningsMenuItem() { + func addPhpDoctorMenuItem() { if !Preferences.isEnabled(.showPhpDoctorSuggestions) || !WarningManager.shared.hasWarnings() { return } self.addItem(NSMenuItem.separator()) - - let count = WarningManager.shared.warnings.count - self.addItem(NSMenuItem(title: "mi_warnings".localized(count), - action: #selector(MainMenu.openWarnings), keyEquivalent: "")) + self.addItem(HeaderView.asMenuItem(text: "mi_php_doctor".localized)) + self.addItem(NSMenuItem( + title: "mi_recommendations_count".localized(WarningManager.shared.warnings.count), + action: nil, + keyEquivalent: "" + )) + self.addItem(NSMenuItem( + title: "mi_view_recommendations".localized, + action: #selector(MainMenu.openWarnings), + keyEquivalent: "" + )) } func addCoreMenuItems() { diff --git a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift index 34a2c0f..80df681 100644 --- a/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift +++ b/phpmon/Domain/SwiftUI/Onboarding/OnboardingView.swift @@ -28,9 +28,9 @@ struct OnboardingTextItem: View { Text(description.localizedForSwiftUI) .foregroundColor(Color.secondary) .font(.system(size: 13)) - .lineLimit(6) + .lineLimit(4) .fixedSize(horizontal: false, vertical: true) - .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) + .frame(minWidth: 0, maxWidth: 800, alignment: .leading) } } .padding() @@ -41,80 +41,79 @@ struct OnboardingTextItem: View { struct OnboardingView: View { var body: some View { - VStack(spacing: 10) { - VStack(alignment: .center) { - HStack { - Image(nsImage: NSApp.applicationIconImage) - .resizable() - .frame(width: 80, height: 80) + VStack(alignment: .center, spacing: 5) { + HStack { + Image(nsImage: NSApp.applicationIconImage) + .resizable() + .frame(width: 80, height: 80) + .padding(.bottom, 5) + .padding(.trailing, 25) + VStack(alignment: .leading, spacing: 0) { + Text("onboarding.welcome".localized) + .font(.title) + .bold() .padding(.bottom, 5) - .padding(.trailing, 25) - VStack(alignment: .leading, spacing: 0) { - Text("onboarding.welcome".localized) - .font(.title) - .bold() - .padding(.bottom, 5) - Text("onboarding.explore".localized) - .padding(.bottom) - .padding(.trailing) - } - .padding(.top, 10) + Text("onboarding.explore".localized) + .padding(.bottom) + .padding(.trailing) } - .padding(.leading) - .padding(.trailing) - - VStack { - VStack(alignment: .leading, spacing: 10) { - OnboardingTextItem( - icon: "bolt.circle.fill", - title: "onboarding.tour.menu_bar.title", - description: "onboarding.tour.menu_bar" - ) - OnboardingTextItem( - icon: "checkmark.circle.fill", - title: "onboarding.tour.services.title", - description: "onboarding.tour.services" - ) - OnboardingTextItem( - icon: "list.bullet.circle.fill", - title: "onboarding.tour.domains.title", - description: "onboarding.tour.domains" - ) - OnboardingTextItem( - icon: "pin.circle.fill", - title: "onboarding.tour.isolation.title", - description: "onboarding.tour.isolation" - ) - } - }.padding() - - VStack(spacing: 20) { - HStack { - Image(systemName: "questionmark.circle.fill") - .resizable() - .frame(width: 24, height: 24) - .foregroundColor(Color.appSecondary) - .padding(.trailing, 10) - HStack { - Text("onboarding.tour.faq_hint".localizedForSwiftUI) - .lineLimit(5) - }.fixedSize(horizontal: false, vertical: true) - } - VStack { - Text("onboarding.tour.once".localized) - .font(.subheadline) - .foregroundColor(.gray) - .padding(.top, 5) - .padding(.bottom, 5) - .lineLimit(5) - Button("onboarding.tour.close".localized) { - App.shared.onboardingWindowController?.close() - } - } - }.padding() + .padding(.top, 10) } + .padding(.leading) + .padding(.trailing) + + VStack { + VStack(alignment: .leading, spacing: 10) { + OnboardingTextItem( + icon: "bolt.circle.fill", + title: "onboarding.tour.menu_bar.title", + description: "onboarding.tour.menu_bar" + ) + OnboardingTextItem( + icon: "checkmark.circle.fill", + title: "onboarding.tour.services.title", + description: "onboarding.tour.services" + ) + OnboardingTextItem( + icon: "list.bullet.circle.fill", + title: "onboarding.tour.domains.title", + description: "onboarding.tour.domains" + ) + OnboardingTextItem( + icon: "pin.circle.fill", + title: "onboarding.tour.isolation.title", + description: "onboarding.tour.isolation" + ) + } + }.padding() + + VStack(spacing: 20) { + HStack { + Image(systemName: "questionmark.circle.fill") + .resizable() + .frame(width: 24, height: 24) + .foregroundColor(Color.appSecondary) + .padding(.trailing, 10) + HStack { + Text("onboarding.tour.faq_hint".localizedForSwiftUI) + .lineLimit(5) + }.fixedSize(horizontal: false, vertical: true) + } + VStack { + Text("onboarding.tour.once".localized) + .font(.subheadline) + .foregroundColor(.gray) + .padding(.top, 5) + .padding(.bottom, 5) + .lineLimit(5) + Button("onboarding.tour.close".localized) { + App.shared.onboardingWindowController?.close() + } + } + } + .padding(.leading) + .padding(.trailing) } - .padding(.top, 8) } } diff --git a/phpmon/Domain/SwiftUI/Warning/WarningListView.swift b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift index 7659c5a..0d29b57 100644 --- a/phpmon/Domain/SwiftUI/Warning/WarningListView.swift +++ b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift @@ -11,23 +11,35 @@ import SwiftUI struct WarningListView: View { var body: some View { VStack { - HStack(spacing: 15) { + HStack(alignment: .center, spacing: 15) { Image(systemName: "stethoscope.circle.fill") .resizable() .frame(width: 40, height: 40) .foregroundColor(Color.red) .padding(12) - VStack(alignment: .trailing, spacing: 5) { + VStack(alignment: .leading, spacing: 5) { Text("warnings.description".localizedForSwiftUI) + .font(.system(size: 12)) .frame(maxWidth: .infinity, alignment: .leading) Text("warnings.disclaimer".localizedForSwiftUI) .font(.system(size: 12)) - .foregroundColor(.gray) .frame(maxWidth: .infinity, alignment: .leading) } } .padding(10) + Divider() + + HStack(alignment: .center, spacing: 15) { + Button("warnings.refresh.button".localizedForSwiftUI) { + WarningManager.shared.evaluateWarnings() + } + Text("warnings.refresh.button.description".localizedForSwiftUI) + .foregroundColor(.gray) + .font(.system(size: 11)) + } + .padding(10) + List { VStack(alignment: .leading, spacing: 0) { ForEach(WarningManager.shared.warnings) { warning in diff --git a/phpmon/Domain/Warnings/WarningManager.swift b/phpmon/Domain/Warnings/WarningManager.swift index 9f65c4a..5dd21de 100644 --- a/phpmon/Domain/Warnings/WarningManager.swift +++ b/phpmon/Domain/Warnings/WarningManager.swift @@ -32,8 +32,8 @@ class WarningManager { ), Warning( command: { - !Paths.PATH.contains("/Users/\(Paths.whoami)/.config/phpmon/bin") && - !FileManager.default.isWritableFile(atPath: "/usr/local/bin/") + return !Shell.user.PATH.contains("/Users/\(Paths.whoami)/.config/phpmon/bin") && + !FileManager.default.isWritableFile(atPath: "/usr/local/bin/") }, name: "Helpers cannot be symlinked and not in PATH", title: "warnings.helper_permissions.title", @@ -74,6 +74,8 @@ class WarningManager { if ProcessInfo.processInfo.environment["EXTREME_DOCTOR_MODE"] != nil { self.warnings = self.evaluations } + + MainMenu.shared.rebuild() } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index c090432..68fac0c 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -55,7 +55,9 @@ "mi_detected_extensions" = "Detected Extensions"; "mi_no_extensions_detected" = "No additional extensions detected."; -"mi_warnings" = "(%i) PHP Doctor..."; +"mi_php_doctor" = "PHP Doctor"; +"mi_recommendations_count" = "%i Issue(s) Detected!"; +"mi_view_recommendations" = "View Recommendations..."; "mi_valet" = "Laravel Valet"; "mi_domain_list" = "View Domains List..."; @@ -517,6 +519,8 @@ If you are seeing this message but are confused why this folder has gone missing "warnings.title" = "PHP Doctor"; "warnings.description" = "**PHP Doctor** will suggest improvements to your active system configuration."; "warnings.disclaimer" = "You may choose to hide all recommendations from the PHP Monitor menu in Preferences, but it is recommended that you deal with all actionable items."; +"warnings.refresh.button" = "Scan Again"; +"warnings.refresh.button.description" = "Press this button once you've fixed an issue. This will cause PHP Monitor to re-evaluate your environment. If it's really fixed, the recommendation should disappear."; "warnings.helper_permissions.title" = "PHP Monitor’s helpers are currently unavailable."; "warnings.helper_permissions.description" = "PHP Monitor comes with various helper binaries. Using these binaries allows you to easily invoke a specific version of PHP without switching the linked PHP version."; @@ -531,14 +535,14 @@ If you are seeing this message but are confused why this folder has gone missing "onboarding.title" = "Welcome Tour"; "onboarding.welcome" = "Welcome to PHP Monitor!"; "onboarding.explore" = "Learn more about some of the features that PHP Monitor has to offer. You can find a more comprehensive list of features on GitHub."; -"onboarding.tour.menu_bar.title" = "Get Started"; -"onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From here, you can switch the globally linked PHP version, start or stop services, locate config files, and more."; +"onboarding.tour.menu_bar.title" = "Power In Your Menu Bar"; +"onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From this menu, you can access most of PHP Monitor's key functionality, including switching the globally linked PHP version, locating config files, and much more."; "onboarding.tour.faq_hint" = "I recommend that you check out the [README](https://github.com/nicoverbruggen/phpmon/blob/main/README.md) on GitHub: it contains a comprehensive FAQ with various tips and common questions and answers."; -"onboarding.tour.services.title" = "Manage Services"; -"onboarding.tour.services" = "Once you click on the menu bar item, you can see at a glance based on the checkmarks or crosses if all of the Homebrew services are up and running. You can also click on a service to quickly toggle it. You can also add your own!"; +"onboarding.tour.services.title" = "Manage Homebrew Services"; +"onboarding.tour.services" = "Once you click on the menu bar item, you can see at a glance based on the checkmarks or crosses if all of the Homebrew services are up and running. You can also click on a service to quickly toggle it."; "onboarding.tour.domains.title" = "Manage Domains"; "onboarding.tour.domains" = "By opening the Domains window via the menu bar item, you can view which domains are linked and parked, as well as active nginx proxies."; "onboarding.tour.isolation.title" = "Isolate Domains"; -"onboarding.tour.isolation" = "If you have Valet 3 installed, you can even use domain isolation by right-clicking on a given domain in the Domains window. This allows you to pick a specific version of PHP to use for that domain, and that domain only!"; +"onboarding.tour.isolation" = "If you have Valet 3 installed, you can even use domain isolation by right-clicking on a given domain in the Domains window. This allows you to pick a specific version of PHP to use for that domain, and that domain only."; "onboarding.tour.once" = "You will only see the Welcome Tour once. You can re-open the Welcome Tour later via the menu bar icon (under First Aid & Services)."; "onboarding.tour.close" = "Close Tour"; From b281df3bbdd8d01bf13e4193e75a3786ba9cdb8e Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 15 Aug 2022 01:57:22 +0200 Subject: [PATCH 37/43] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Correctly=20refresh?= =?UTF-8?q?=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/SwiftUI/Warning/WarningListView.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/phpmon/Domain/SwiftUI/Warning/WarningListView.swift b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift index 0d29b57..da4bb89 100644 --- a/phpmon/Domain/SwiftUI/Warning/WarningListView.swift +++ b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift @@ -9,6 +9,8 @@ import SwiftUI struct WarningListView: View { + @State var warnings: [Warning] = WarningManager.shared.warnings + var body: some View { VStack { HStack(alignment: .center, spacing: 15) { @@ -32,7 +34,10 @@ struct WarningListView: View { HStack(alignment: .center, spacing: 15) { Button("warnings.refresh.button".localizedForSwiftUI) { - WarningManager.shared.evaluateWarnings() + Task { + await WarningManager.shared.checkEnvironment() + self.warnings = WarningManager.shared.warnings + } } Text("warnings.refresh.button.description".localizedForSwiftUI) .foregroundColor(.gray) @@ -42,7 +47,7 @@ struct WarningListView: View { List { VStack(alignment: .leading, spacing: 0) { - ForEach(WarningManager.shared.warnings) { warning in + ForEach(warnings) { warning in Group { WarningView( title: warning.title, From 9134f08ec9e4497480b28d1578f4f31fcf969e9e Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 16 Aug 2022 19:29:15 +0200 Subject: [PATCH 38/43] =?UTF-8?q?=F0=9F=91=8C=20Fix=20no=20warnings=20view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 4 ++ .../SwiftUI/Warning/NoWarningsView.swift | 33 ++++++++++++++++ .../SwiftUI/Warning/WarningListView.swift | 39 +++++++++++++------ phpmon/Domain/Warnings/WarningManager.swift | 20 ++++++---- phpmon/Localizable.strings | 2 + 5 files changed, 78 insertions(+), 20 deletions(-) create mode 100644 phpmon/Domain/SwiftUI/Warning/NoWarningsView.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index cbdae3f..1d44ff5 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -78,6 +78,7 @@ C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; }; C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; }; C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; }; + C41C708D28AA7F7900E8D498 /* NoWarningsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C708C28AA7F7900E8D498 /* NoWarningsView.swift */; }; C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; }; C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; }; C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; }; @@ -343,6 +344,7 @@ C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; sourceTree = ""; }; C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarImageGenerator.swift; sourceTree = ""; }; C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePhpInstallation.swift; sourceTree = ""; }; + C41C708C28AA7F7900E8D498 /* NoWarningsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoWarningsView.swift; sourceTree = ""; }; C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+Actions.swift"; sourceTree = ""; }; C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = ""; }; C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+ContextMenu.swift"; sourceTree = ""; }; @@ -711,6 +713,7 @@ children = ( C4297F7928970D59004C4630 /* WarningView.swift */, C422DDA928A2C49900CEAC97 /* WarningListView.swift */, + C41C708C28AA7F7900E8D498 /* NoWarningsView.swift */, ); path = Warning; sourceTree = ""; @@ -1288,6 +1291,7 @@ C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, 5489625828312FAD004F647A /* CreatedFromFile.swift in Sources */, C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */, + C41C708D28AA7F7900E8D498 /* NoWarningsView.swift in Sources */, C4080FF627BD8C6400BF2C6B /* BetterAlert.swift in Sources */, C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */, C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */, diff --git a/phpmon/Domain/SwiftUI/Warning/NoWarningsView.swift b/phpmon/Domain/SwiftUI/Warning/NoWarningsView.swift new file mode 100644 index 0000000..3c29dff --- /dev/null +++ b/phpmon/Domain/SwiftUI/Warning/NoWarningsView.swift @@ -0,0 +1,33 @@ +// +// NoWarningsView.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 15/08/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import SwiftUI + +struct NoWarningsView: View { + var body: some View { + VStack(alignment: .center, spacing: 15) { + Image(systemName: "checkmark.circle.fill") + .resizable() + .renderingMode(.template) + .foregroundColor(Color.green) + .frame(width: 24, height: 24) + VStack(alignment: .center) { + Text("warnings.none".localizedForSwiftUI) + } + + } + .frame(minWidth: 0, maxWidth: .infinity) + .padding(25) + } +} + +struct NoWarningsView_Previews: PreviewProvider { + static var previews: some View { + NoWarningsView() + } +} diff --git a/phpmon/Domain/SwiftUI/Warning/WarningListView.swift b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift index da4bb89..c250c63 100644 --- a/phpmon/Domain/SwiftUI/Warning/WarningListView.swift +++ b/phpmon/Domain/SwiftUI/Warning/WarningListView.swift @@ -9,7 +9,11 @@ import SwiftUI struct WarningListView: View { - @State var warnings: [Warning] = WarningManager.shared.warnings + @State var warnings: [Warning] + + init(empty: Bool = false) { + self.warnings = empty ? [] : WarningManager.shared.warnings + } var body: some View { VStack { @@ -47,17 +51,21 @@ struct WarningListView: View { List { VStack(alignment: .leading, spacing: 0) { - ForEach(warnings) { warning in - Group { - WarningView( - title: warning.title, - paragraphs: warning.paragraphs, - documentationUrl: warning.url - ) - .fixedSize(horizontal: false, vertical: true) + if warnings.isEmpty { + NoWarningsView() + } else { + ForEach(warnings) { warning in + Group { + WarningView( + title: warning.title, + paragraphs: warning.paragraphs, + documentationUrl: warning.url + ) + .fixedSize(horizontal: false, vertical: true) - Divider() - }.padding(5) + Divider() + }.padding(5) + } } }.frame(minHeight: 0, maxHeight: .infinity).padding(5) } @@ -70,7 +78,14 @@ struct WarningListView: View { struct WarningListView_Previews: PreviewProvider { static var previews: some View { - WarningListView() + WarningListView(empty: true) .frame(width: 600, height: 480) + + /* + WarningListView() + // TODO: Figure out how the empty() only applies to this single instance + // .empty() + .frame(width: 600, height: 480) + */ } } diff --git a/phpmon/Domain/Warnings/WarningManager.swift b/phpmon/Domain/Warnings/WarningManager.swift index 5dd21de..e56d977 100644 --- a/phpmon/Domain/Warnings/WarningManager.swift +++ b/phpmon/Domain/Warnings/WarningManager.swift @@ -62,6 +62,18 @@ class WarningManager { func checkEnvironment() async { self.warnings = [] + if ProcessInfo.processInfo.environment["EXTREME_DOCTOR_MODE"] != nil { + // For debugging purposes, we may wish to see all possible evaluations listed + self.warnings = self.evaluations + } else { + // Otherwise, loop over the actual evaluations and list the warnings + await loopOverEvaluations() + } + + MainMenu.shared.rebuild() + } + + private func loopOverEvaluations() async { for check in self.evaluations { if await check.applies() { Log.info("[DOCTOR] \(check.name) (!)") @@ -69,13 +81,5 @@ class WarningManager { continue } } - - // For debugging purposes, we may wish to see all possible evaluations listed - if ProcessInfo.processInfo.environment["EXTREME_DOCTOR_MODE"] != nil { - self.warnings = self.evaluations - } - - MainMenu.shared.rebuild() } - } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 68fac0c..324c40a 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -530,6 +530,8 @@ If you are seeing this message but are confused why this folder has gone missing "warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta."; "warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew."; +"warnings.none" = "There are no recommendations available for you right now. You're all good!"; + // ONBOARDING "onboarding.title" = "Welcome Tour"; From fcdb4a89935e932d0f4902e346d0c997bb7a159a Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 16 Aug 2022 19:35:18 +0200 Subject: [PATCH 39/43] =?UTF-8?q?=E2=9C=A8=20Allow=20opening=20of=20PHP=20?= =?UTF-8?q?Doctor=20via=20First=20Aid=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Menu/StatusMenu+Items.swift | 4 ++++ phpmon/Domain/Menu/StatusMenu.swift | 1 + phpmon/Localizable.strings | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/phpmon/Domain/Menu/StatusMenu+Items.swift b/phpmon/Domain/Menu/StatusMenu+Items.swift index 1259185..c6bfde5 100644 --- a/phpmon/Domain/Menu/StatusMenu+Items.swift +++ b/phpmon/Domain/Menu/StatusMenu+Items.swift @@ -204,6 +204,10 @@ extension StatusMenu { servicesMenu.addItem(NSMenuItem(title: "mi_view_onboarding".localized, action: #selector(MainMenu.showWelcomeTour), keyEquivalent: "")) + servicesMenu.addItem(NSMenuItem(title: "mi_fa_php_doctor".localized, + action: #selector(MainMenu.openWarnings), keyEquivalent: "")) + servicesMenu.addItem(NSMenuItem.separator()) + let fixMyValetMenuItem = NSMenuItem( title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion), action: #selector(MainMenu.fixMyValet), keyEquivalent: "" diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 8ece249..5f14cad 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -73,6 +73,7 @@ class StatusMenu: NSMenu { self.addItem(NSMenuItem.separator()) self.addPresetsMenuItem() + self.addFirstAidAndServicesMenuItems() } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 324c40a..97957e0 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -56,6 +56,7 @@ "mi_no_extensions_detected" = "No additional extensions detected."; "mi_php_doctor" = "PHP Doctor"; +"mi_fa_php_doctor" = "Open PHP Doctor..."; "mi_recommendations_count" = "%i Issue(s) Detected!"; "mi_view_recommendations" = "View Recommendations..."; @@ -76,7 +77,7 @@ "mi_no_presets" = "No presets available."; "mi_set_up_presets" = "Learn more about presets..."; -"mi_view_onboarding" = "Show Welcome Tour..."; +"mi_view_onboarding" = "Open Welcome Tour..."; "mi_xdebug_available_modes" = "Available Modes"; "mi_xdebug_actions" = "Actions"; From a85e016b4abe77b42082d16aaee76df16fc3197d Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 22 Aug 2022 18:45:02 +0200 Subject: [PATCH 40/43] =?UTF-8?q?=F0=9F=93=9D=20Update=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 84a9d3a..7575671 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +> **Note** > If this software has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider leaving [a one-time donation](https://nicoverbruggen.be/sponsor) to support the project, as this is something I make in my free time. **Thank you!** ⭐️

PHP Monitor Logo

@@ -362,6 +363,9 @@ Here's an example of a working preset: You can omit the `php` key in the preset if you do not wish for the preset to switch to a given PHP version. + +> **Warning** +> You must restart PHP Monitor for these changes to be detected.
@@ -371,18 +375,24 @@ You must set these services up in a JSON file, located in `~/.config/phpmon/conf You can specify custom services in the configuration file for Homebrew services that run as your own user (not root). +> **Info** +> If your service must run as root, it cannot currently be added to PHP Monitor. + You can find out which services are available by running `brew services list`. -Here's an example where we add the `mailgun` and `mysql` services to PHP Monitor: +Here's an example where we add the `mailhog` and `mysql` services to PHP Monitor:
 {
     "scan_apps": [],
-    "services": ["mailgun", "mysql"],
+    "services": ["mailhog", "mysql"],
     "presets": [],
     "export": {}
 }
 
+ +> **Warning** +> You must restart PHP Monitor for these changes to be detected.
@@ -404,6 +414,10 @@ Here's an example of a working `COMPOSER_HOME` environment variable which is res } } + +> **Warning** +> You must restart PHP Monitor for these changes to be detected. +
@@ -426,6 +440,9 @@ You can add your own apps by creating and editing a `~/.config/phpmon/config.jso You can put as many apps as you'd like in the `scan_apps` array, and PHP Monitor will check for the existence of these apps. You do not need to set the full path, just the name of the app should work. Not all apps support opening a folder, though, so your success might vary. + +> **Warning** +> You must restart PHP Monitor for these changes to be detected.
@@ -556,7 +573,8 @@ If an extension or other process writes to a single file a bunch of times in a s 1. **Sites secured or not secured**: Whether the directory has been secured is determined by checking if a matching certificate exists under Valet's `Certificates` directory for that site name. 1. **Project type**: PHP Monitor checks your `composer.json` file for "notable dependencies". If you have `laravel/framework` in your `require`, there's a good chance the project type is `Laravel`, after all. -*Note*: If you have linked a folder in Documents, Desktop or Downloads you might need to grant PHP Monitor access to those directories for PHP Monitor to work correctly. +> **Note** +> If you have linked a folder in Documents, Desktop or Downloads you might need to grant PHP Monitor access to those directories for PHP Monitor to work correctly. ### Want to know more? From 7587126a42aedbf502c43844b69d408017701fb5 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 24 Aug 2022 20:26:32 +0200 Subject: [PATCH 41/43] =?UTF-8?q?=F0=9F=90=9B=20Potential=20fix=20for=20pa?= =?UTF-8?q?rsing=20Valet=20version=20(#188)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/App/Startup.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/phpmon/Domain/App/Startup.swift b/phpmon/Domain/App/Startup.swift index 589e938..8d80154 100644 --- a/phpmon/Domain/App/Startup.swift +++ b/phpmon/Domain/App/Startup.swift @@ -229,8 +229,19 @@ class Startup { EnvironmentCheck( command: { let output = valet("--version", sudo: false) + // Failure condition #1: does not contain Laravel Valet + if !output.contains("Laravel Valet") { + return true + } + // Failure condition #2: version cannot be parsed + let versionString = output + .trimmingCharacters(in: .whitespacesAndNewlines) + .components(separatedBy: "Laravel Valet")[1] + .trimmingCharacters(in: .whitespaces) + // Extract the version number Valet.shared.version = VersionExtractor.from(output) - return Valet.shared.version == nil && output.contains("Laravel Valet") + // Get the actual version + return Valet.shared.version == nil }, name: "`valet --version` was loaded", titleText: "startup.errors.valet_version_unknown.title".localized, From 845235a27621cef30c39b084321a90f20ac1d8e7 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 24 Aug 2022 20:28:12 +0200 Subject: [PATCH 42/43] =?UTF-8?q?=F0=9F=94=A7=20Bump=20build=20number=20fo?= =?UTF-8?q?r=20new=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 1d44ff5..edb2e01 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -1687,7 +1687,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 950; + CURRENT_PROJECT_VERSION = 951; DEBUG = YES; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; @@ -1714,7 +1714,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 950; + CURRENT_PROJECT_VERSION = 951; DEBUG = NO; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; From 87e08e4607c84d33c7dc2adefeef115f0960dacc Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 24 Aug 2022 20:33:35 +0200 Subject: [PATCH 43/43] =?UTF-8?q?=F0=9F=94=A7=20Use=20icon=20for=20stable?= =?UTF-8?q?=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index edb2e01..62fb4e4 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -1681,7 +1681,7 @@ C41C1B4422B0098000E7CF16 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor; CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; @@ -1708,7 +1708,7 @@ C41C1B4522B0098000E7CF16 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor; CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements; CODE_SIGN_IDENTITY = "Apple Development";