From 78e750d764645c615cb7bb19086697e9f5235ee9 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 10 Aug 2022 21:04:23 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Check=20if=20running=20the=20app=20?= =?UTF-8?q?with=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