From 50f003a2bdd8199756a1bcb639a81f0b2ebe8e65 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 16 Feb 2022 22:42:43 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20New=20notice=20alert=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 20 ++++ phpmon/Common/Core/Actions.swift | 2 + phpmon/Domain/App/AppDelegate.swift | 19 ++++ phpmon/Domain/App/Base.lproj/Main.storyboard | 97 ++++++++++++-------- phpmon/Domain/Notice/Notice.swift | 81 ++++++++++++++++ phpmon/Domain/Notice/NoticeVC.swift | 63 +++++++++++++ 6 files changed, 245 insertions(+), 37 deletions(-) create mode 100644 phpmon/Domain/Notice/Notice.swift create mode 100644 phpmon/Domain/Notice/NoticeVC.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index ad7275f..4073b35 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -28,6 +28,10 @@ C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */; }; C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */; }; C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */; }; + C4080FF627BD8C6400BF2C6B /* Notice.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF527BD8C6400BF2C6B /* Notice.swift */; }; + C4080FF727BD8C6400BF2C6B /* Notice.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF527BD8C6400BF2C6B /* Notice.swift */; }; + C4080FFA27BD956700BF2C6B /* NoticeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* NoticeVC.swift */; }; + C4080FFB27BD956700BF2C6B /* NoticeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* NoticeVC.swift */; }; C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; }; C40B24F227A310770018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; }; C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; }; @@ -215,6 +219,8 @@ C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CheckboxPreferenceView.xib; sourceTree = ""; }; C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectPreferenceView.swift; sourceTree = ""; }; C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarIcons.swift; sourceTree = ""; }; + C4080FF527BD8C6400BF2C6B /* Notice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notice.swift; sourceTree = ""; }; + C4080FF927BD956700BF2C6B /* NoticeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeVC.swift; sourceTree = ""; }; C40C7F1D2772136000DDDCDC /* PhpEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpEnv.swift; sourceTree = ""; }; C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = ""; }; C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; @@ -385,6 +391,15 @@ path = IAP; sourceTree = ""; }; + C4080FF827BD955900BF2C6B /* Notice */ = { + isa = PBXGroup; + children = ( + C4080FF527BD8C6400BF2C6B /* Notice.swift */, + C4080FF927BD956700BF2C6B /* NoticeVC.swift */, + ); + path = Notice; + sourceTree = ""; + }; C40C7F1C27720E1400DDDCDC /* Test Files */ = { isa = PBXGroup; children = ( @@ -460,6 +475,7 @@ C41E181722CB61EB0072CF09 /* Domain */ = { isa = PBXGroup; children = ( + C4080FF827BD955900BF2C6B /* Notice */, C4AF9F6B275445D300D44ED0 /* Integrations */, C4B13B1D25C4915000548C3A /* App */, C4D9ADBD27761084007277F4 /* PHP */, @@ -829,6 +845,7 @@ 5420395926135DC100FB00FA /* PrefsVC.swift in Sources */, C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */, + C4080FF627BD8C6400BF2C6B /* Notice.swift in Sources */, C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */, C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */, C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, @@ -884,6 +901,7 @@ C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */, C4CE3BBA27B31F670086CA49 /* MainMenu+Composer.swift in Sources */, C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */, + C4080FFA27BD956700BF2C6B /* NoticeVC.swift in Sources */, C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */, C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */, C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */, @@ -912,6 +930,7 @@ C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */, C493084B279F331F009C240B /* AddSiteVC.swift in Sources */, C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */, + C4080FFB27BD956700BF2C6B /* NoticeVC.swift in Sources */, C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */, C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */, C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */, @@ -947,6 +966,7 @@ C4CE3BBC27B324250086CA49 /* MainMenu+Composer.swift in Sources */, C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */, C417DC75277614690015E6EE /* Helpers.swift in Sources */, + C4080FF727BD8C6400BF2C6B /* Notice.swift in Sources */, C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */, C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */, diff --git a/phpmon/Common/Core/Actions.swift b/phpmon/Common/Core/Actions.swift index 23a677b..8346655 100644 --- a/phpmon/Common/Core/Actions.swift +++ b/phpmon/Common/Core/Actions.swift @@ -116,6 +116,8 @@ class Actions { Detects all currently available PHP versions, and unlinks each and every one of them. + This all happens in sequence, nothing runs in parallel. + After this, the brew services are also stopped, the latest PHP version is linked, and php + nginx are restarted. diff --git a/phpmon/Domain/App/AppDelegate.swift b/phpmon/Domain/App/AppDelegate.swift index 1b1f329..94aaccf 100644 --- a/phpmon/Domain/App/AppDelegate.swift +++ b/phpmon/Domain/App/AppDelegate.swift @@ -88,6 +88,25 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele startup procedure. */ func applicationDidFinishLaunching(_ aNotification: Notification) { + + /* + let notice = Notice.make() + .withInformation( + title: "Oh no, your PHP is kaput?!", + subtitle: "Unfortunately, PHP Monitor has detected an issue. You can find more info below." + // description: "brew install php@8.0 has returned error code -35" + ) + .withPrimary(text: "Retry") + .withSecondary(text: "Quit", action: { _ in + exit(1) + }) + .withTertiary(text: "Copy to Clipboard", action: { _ in + // Do something here + }) + + print(notice.present() == .alertFirstButtonReturn) + */ + // Make sure notifications will work setupNotifications() // Make sure the menu performs its initial checks diff --git a/phpmon/Domain/App/Base.lproj/Main.storyboard b/phpmon/Domain/App/Base.lproj/Main.storyboard index 720853f..1b3a38e 100644 --- a/phpmon/Domain/App/Base.lproj/Main.storyboard +++ b/phpmon/Domain/App/Base.lproj/Main.storyboard @@ -467,10 +467,9 @@ - - - - + + + @@ -489,24 +488,22 @@ - + - + - + - - - - - - - - + + + - + - + - - + + - - + + - + - - + + - + - - + + Sometimes you need a really long explanation and in that case you can get a really, really long description here, along with, for example, various steps you can take. This allows for a lot of text to be displayed, yay! @@ -570,38 +576,55 @@ DQ - + - - - + + + + + + + - - - + + + + + + + + + + diff --git a/phpmon/Domain/Notice/Notice.swift b/phpmon/Domain/Notice/Notice.swift new file mode 100644 index 0000000..7bf4138 --- /dev/null +++ b/phpmon/Domain/Notice/Notice.swift @@ -0,0 +1,81 @@ +// +// Notice.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 16/02/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation +import Cocoa + +class Notice { + + var windowController: NSWindowController! + + var noticeVC: NoticeVC { + return self.windowController.contentViewController as! NoticeVC + } + + public static func make() -> Notice { + let storyboard = NSStoryboard(name: "Main" , bundle : nil) + + let notice = Notice() + notice.windowController = storyboard.instantiateController( + withIdentifier: "noticeWindow" + ) as? NSWindowController + return notice + } + + public func withPrimary( + text: String, + action: @escaping (NoticeVC) -> Void = { + vc in vc.close(with: .alertFirstButtonReturn) + } + ) -> Self { + self.noticeVC.buttonPrimary.title = text + self.noticeVC.actionPrimary = action + return self + } + + public func withSecondary( + text: String, + action: ((NoticeVC) -> Void)? = nil + ) -> Self { + self.noticeVC.buttonSecondary.title = text + self.noticeVC.actionSecondary = action + return self + } + + public func withTertiary( + text: String, + action: ((NoticeVC) -> Void)? = nil + ) -> Self { + self.noticeVC.buttonTertiary.title = text + self.noticeVC.actionTertiary = action + return self + } + + public func withInformation( + title: String, + subtitle: String, + description: String = "" + ) -> Self { + self.noticeVC.labelTitle.stringValue = title + self.noticeVC.labelSubtitle.stringValue = subtitle + self.noticeVC.labelDescription.stringValue = description + + // If the description is missing, handle the excess space and change the top margin + if (description == "") { + self.noticeVC.labelDescription.isHidden = true + self.noticeVC.primaryButtonTopMargin.constant = 0 + } + return self + } + + public func present() -> NSApplication.ModalResponse { + NSApp.activate(ignoringOtherApps: true) + windowController.window?.makeKeyAndOrderFront(nil) + return NSApplication.shared.runModal(for: windowController.window!) + } +} diff --git a/phpmon/Domain/Notice/NoticeVC.swift b/phpmon/Domain/Notice/NoticeVC.swift new file mode 100644 index 0000000..3c31b4a --- /dev/null +++ b/phpmon/Domain/Notice/NoticeVC.swift @@ -0,0 +1,63 @@ +// +// NoticeVC.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 16/02/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation +import Cocoa + +class NoticeVC: NSViewController { + + @IBOutlet weak var labelTitle: NSTextField! + @IBOutlet weak var labelSubtitle: NSTextField! + @IBOutlet weak var labelDescription: NSTextField! + + @IBOutlet weak var buttonPrimary: NSButton! + @IBOutlet weak var buttonSecondary: NSButton! + @IBOutlet weak var buttonTertiary: NSButton! + + var actionPrimary: (NoticeVC) -> Void = { _ in } + var actionSecondary: ((NoticeVC) -> Void)? + var actionTertiary: ((NoticeVC) -> Void)? + + @IBOutlet weak var imageView: NSImageView! + + @IBOutlet weak var primaryButtonTopMargin: NSLayoutConstraint! + + override func viewWillAppear() { + imageView.image = NSApp.applicationIconImage + if actionSecondary == nil { + buttonSecondary.isHidden = true + } + if actionTertiary == nil { + buttonTertiary.isHidden = true + } + } + + @IBAction func primaryButtonAction(_ sender: Any) { + self.actionPrimary(self) + } + + @IBAction func secondaryButtonAction(_ sender: Any) { + if self.actionSecondary != nil { + self.actionSecondary!(self) + } else { + self.close(with: .alertSecondButtonReturn) + } + } + + @IBAction func tertiaryButtonAction(_ sender: Any) { + if self.actionSecondary != nil { + self.actionTertiary!(self) + } + } + + public func close(with code: NSApplication.ModalResponse) { + self.view.window?.close() + NSApplication.shared.stopModal(withCode: code) + } + +}