diff --git a/Images/Rebranding/rebranding-6.png b/Images/Rebranding/rebranding-6.png index 0b4fd52..335786a 100644 Binary files a/Images/Rebranding/rebranding-6.png and b/Images/Rebranding/rebranding-6.png differ diff --git a/Images/SystemAlert/systemalert.png b/Images/SystemAlert/systemalert.png new file mode 100644 index 0000000..22c87c2 Binary files /dev/null and b/Images/SystemAlert/systemalert.png differ diff --git a/Notification Agent Alerts/AppDelegate.swift b/Notification Agent Alerts/AppDelegate.swift index f677eef..96b30e0 100644 --- a/Notification Agent Alerts/AppDelegate.swift +++ b/Notification Agent Alerts/AppDelegate.swift @@ -25,7 +25,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { return } isConfigured = true - NSApplication.shared.activate(ignoringOtherApps: true) notificationDispatch.startObservingForNotifications() guard !UserNotificationController.shared.agentTriggeredByNotificationCenter else { completion() diff --git a/Notification Agent Alerts/Info.plist b/Notification Agent Alerts/Info.plist index 687d4e5..6a11bc8 100644 --- a/Notification Agent Alerts/Info.plist +++ b/Notification Agent Alerts/Info.plist @@ -17,7 +17,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 96 ITSAppUsesNonExemptEncryption LSApplicationCategoryType diff --git a/Notification Agent Banners/AppDelegate.swift b/Notification Agent Banners/AppDelegate.swift index 7cb3eac..902807d 100644 --- a/Notification Agent Banners/AppDelegate.swift +++ b/Notification Agent Banners/AppDelegate.swift @@ -24,7 +24,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { return } isConfigured = true - NSApplication.shared.activate(ignoringOtherApps: true) notificationDispatch.startObservingForNotifications() guard !UserNotificationController.shared.agentTriggeredByNotificationCenter else { completion() diff --git a/Notification Agent Banners/Info.plist b/Notification Agent Banners/Info.plist index 3ea67e1..a76d44f 100644 --- a/Notification Agent Banners/Info.plist +++ b/Notification Agent Banners/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 96 ITSAppUsesNonExemptEncryption LSApplicationCategoryType diff --git a/Notification Agent Core/Controllers/HelpBuilder.swift b/Notification Agent Core/Controllers/HelpBuilder.swift index 4c960de..143c9ef 100644 --- a/Notification Agent Core/Controllers/HelpBuilder.swift +++ b/Notification Agent Core/Controllers/HelpBuilder.swift @@ -6,7 +6,7 @@ // Copyright © 2021 IBM Inc. All rights reserved // SPDX-License-Identifier: Apache2.0 // -// swiftlint:disable type_body_length +// swiftlint:disable type_body_length file_length import Foundation public final class HelpBuilder { @@ -61,6 +61,21 @@ public final class HelpBuilder { "-payload".green(), "-always_on_top".yellow(), "-hide_title_bar_buttons".yellow()] + static let systemAlertArguments: [String] = ["-type".green(), + "-title".yellow(), + "-subtitle".yellow(), + "-icon_path".yellow(), + "-main_button_label".yellow(), + "-main_button_cta_type".yellow(), + "-main_button_cta_payload".yellow(), + "-secondary_button_label".yellow(), + "-secondary_button_cta_type".yellow(), + "-secondary_button_cta_payload".yellow(), + "-tertiary_button_label".yellow(), + "-tertiary_button_cta_type".yellow(), + "-tertiary_button_cta_payload".yellow(), + "-silent".yellow(), + "-showSuppressionButton".yellow()] static let popupDescriptions: [String] = ["[ popup ]".red() + "\n The UI type of the notification.\n Example: -type popup", "\n The bar title.\n Example: -bar_title \"Bar Title\"", "\n The title of the notification.\n Suggested length < 120 characters.\n Allowed length < 240 characters.\n Example: -title \"Title\"", @@ -125,12 +140,29 @@ public final class HelpBuilder { "\n The json payload for the \"onboarding\" UI type.\n Example: -payload \"{ \"pages\": [\n {\n \"title\": \"Some title\",\n \"subtitle\": \"Some subtitle\",\n \"body\": \"Some body\",\n \"accessoryViews\": \"[\n [\n {\n \"type\": \"input\"\n \"payload\": \"/placeholder Something /title First\"\n },\n {\n \"type\": \"input\"\n \"payload\": \"/placeholder Something /title Second\"\n }\n ],\n [\n {\n \"type\": \"image\"\n \"payload\": \"/local/or/remote/path/to/image.png\"\n },\n {\n \"type\": \"video\"\n \"payload\": \"/local/or/remote/path/to/video.mov\"\n }\n ]\n ]\n }\n ]\n }\"\n Please see more about this feature on the project wiki.", "\n Flag that tells the agent to keep the Onbording UI always on top of the window hierarchy.\n Example: -always_on_top", "\n Flag that tells the agent to remove the title bar buttons for the Onbording UI.\n Example: -hide_title_bar_buttons"] + static let systemAlertDescriptions: [String] = ["[ systemAlert ]".red() + "\n The UI type of the notification.\n Example: -type popup", + "\n The title of the notification.\n Example: -title \"Title\"", + "\n The subtitle of the notification.\n Example: -subtitle \"Subtitle\"", + "\n The custom icon path defined for this notification.\n Example: -icon_path \"~/Icon/Path.png\"", + "\n The label of the main button.\n Example: -main_button_label \"Main button title\"", + "[ none | link ]".red() + "\n The call to action type for the main button (default: none -> exit).\n Example: -main_button_cta_type link", + "\n An URL if " + "[ link ]".red() + " cta type defined.\n Example: -main_button_cta_payload \"URL\"", + "\n The label of the secondary button.\n Example: -secondary_button_label \"Secondary button title\"", + "[ none | link ]".red() + "\n The call to action type for the secondary button (default: none -> exit).\n Example: -secondary_button_cta_type link", + "\n An URL if " + "[ link ]".red() + " cta type defined.\n Example: -secondary_button_cta_payload \"URL\"", + "\n The label of the tertiary button.\n Example: -tertiary_button_label \"Tertiary button title\"", + "[ link | exitlink ]".red() + "\n The call to action type for the tertiary button.\n Example: -tertiary_button_cta_type link", + "\n A mandatory URL if " + "[ link ]".red() + " cta type defined, optional if " + "[ exitlink ]".red() + " cta defined.\n Example: -tertiary_button_cta_payload \"URL\"", + "\n Flag that tells the agent to not reproduce any sound when the pop-up appear.\n Example: -silent", + "\n Flag that tells the agent to show the suppression future notifications button on the UI. If checked by the user the agent will print \"suppressed\" in the output before exit.\n Example: -showSuppressionButton"] static let popupSyntacticRules: [String] = ["At least one argument between" + " [ -title | -subtitle | -accessory_view_type + -accessory_view_payload ] ".red() + "must be defined to present a pop-up.", "By default tertiary button is not destructive. Use " + "[ exitlink ]".red() + " cta type to trigger a link (optional) and make it destructive for the pop-up.", "In general if a call to action type is defined for a button, must be defined also the related payload. Except for the cta types " + "[ none | exitlink ]".red() + "."] static let bannerSyntacticRules: [String] = ["At least one argument between" + " [ -title | -subtitle ] ".red() + "must be defined to present a banner.", "In general if a call to action type is defined for a button, must be defined also the related payload."] + static let systemAlertSyntacticRules: [String] = ["At least one argument between" + " [ -title | -subtitle ] ".red() + "must be defined to present a systemAlert."] static let popupNotes: [String] = ["If no call to action type defined for a button the default one is " + "[ none ]".red() + ", that simply return the exit code related to the clicked button.\n- \"/selected\" and \"/placeholder\" keys for " + "[ dropdown ]".red() + " accessory view payload are mutually exclusive."] + static let systemAlertNotes: [String] = ["If no call to action type defined for a button the default one is " + "[ none ]".red()] static let bannerNotes: [String] = ["If no call to action type defined for a button the default one is " + "[ none ]".red() + ", that simply return the exit code related to the clicked button.\nAlert UI require additional configurations to work properly. Please refer to the related Github Wiki Page."] static let specialArguments: [String] = ["--help".blue(), "--version".blue(), @@ -196,10 +228,11 @@ public final class HelpBuilder { "239".bold()] static let onboardingReturnValuesDescription: [String] = ["User did finish onboarding.", "User dimissed the onboarding window."] - static let examplePopup: String = "~/IBM\\ Notifier.app/Contents/MacOS/IBM\\ Notifier -type popup -title \"Test title\" -subtitle \"Test subtitle\" -accessory_view_type whitebox -accessory_view_payload \"Test accessory view\" -main_button_label \"Main button\" -secondary_button_label \"Secondary button\" -tertiary_button_label \"Tertiary button\" -tertiary_button_cta_type link -tertiary_button_cta_payload \"https://www.ibm.com\" -help_button_cta_type infopopup -help_button_cta_payload \"Test help text\"]" + static let examplePopup: String = "~/IBM\\ Notifier.app/Contents/MacOS/IBM\\ Notifier -type popup -title \"Test title\" -subtitle \"Test subtitle\" -accessory_view_type whitebox -accessory_view_payload \"Test accessory view\" -main_button_label \"Main button\" -secondary_button_label \"Secondary button\" -tertiary_button_label \"Tertiary button\" -tertiary_button_cta_type link -tertiary_button_cta_payload \"https://www.ibm.com\" -help_button_cta_type infopopup -help_button_cta_payload \"Test help text\"" static let exampleBanner: String = "~/IBM\\ Notifier.app/Contents/MacOS/IBM\\ Notifier -type banner -title \"Test title\" -subtitle \"Test subtitle\" -main_button_label \"Main button\" -secondary_button_label \"Secondary button\" -tertiary_button_label \"Tertiary button\" -tertiary_button_cta_type link -tertiary_button_cta_payload \"https://www.ibm.com\"" static let exampleAlert: String = "~/IBM\\ Notifier.app/Contents/MacOS/IBM\\ Notifier -type alert -title \"Test title\" -subtitle \"Test subtitle\" -main_button_label \"Main button\" -secondary_button_label \"Secondary button\" -tertiary_button_label \"Tertiary button\" -tertiary_button_cta_type link -tertiary_button_cta_payload \"https://www.ibm.com\"" static let exampleOnboarding: String = "~/IBM\\ Notifier.app/Contents/MacOS/IBM\\ Notifier -type onboarding -payload \"{\\\"pages\\\":[{\\\"title\\\":\\\"First page's title\\\",\\\"subtitle\\\":\\\"First page's subtitle\\\",\\\"body\\\":\\\"First page's body\\\"}]}\"" + static let exampleSystemAlert: String = "~/IBM\\ Notifier.app/Contents/MacOS/IBM\\ Notifier -type popup -title \"Test title\" -subtitle \"Test subtitle\" -icon_path \"path/to/icon.png\" -main_button_label \"Main button\" -secondary_button_label \"Secondary button\" -tertiary_button_label \"Tertiary button\" -tertiary_button_cta_type link -tertiary_button_cta_payload \"https://www.ibm.com\" -silent -showSuppressionButton" static func printHelp(_ arguments: [String]) { guard !arguments.contains("-popup") else { @@ -218,12 +251,16 @@ public final class HelpBuilder { Self.printOnboardingHelp() return } + guard !arguments.contains("-systemAlert") else { + Self.printSystemAlertHelp() + return + } guard !arguments.contains("-configuration") else { Self.printConfigurationHelp() return } print("\nIBM Notifier Help Page".bold().blue() + "\n") - print("You can use:\n --help -popup - To show help page about the pop-up UI;\n --help -banner - To show help page about the banner (temporary banner notification) UI;\n --help -alert - To show help page about the alert (persistent banner notification) UI;\n --help -onboarding - To show help page about the onboarding UI;\n --help -configuration - To show help page about the configuration mode.\n") + print("You can use:\n --help -popup - To show help page about the pop-up UI;\n --help -banner - To show help page about the banner (temporary banner notification) UI;\n --help -alert - To show help page about the alert (persistent banner notification) UI;\n --help -onboarding - To show help page about the onboarding UI;\n --help -systemAlert - To show help page about the system alert UI;\n --help -configuration - To show help page about the configuration mode.\n") } static func printPopupHelp() { @@ -319,6 +356,36 @@ public final class HelpBuilder { } } + static func printSystemAlertHelp() { + print("\nIBM Notifier System Alert UI".bold().blue() + "\n") + var argumentsString = "" + for argument in systemAlertArguments { + argumentsString += "[\(argument)] " + } + print("Usage: ".cyan().bold() + "\n~/IBM\\ Notifier.app/Contents/MacOS/IBM\\ Notifier " + argumentsString + "\n") + print("Color Legend: ".cyan().bold()) + print("Mandatory value".green() + " " + "Optional value".yellow() + "\n") + print("Arguments details:".cyan().bold()) + for index in systemAlertArguments.indices { + print("\(systemAlertArguments[index]): \(systemAlertDescriptions[index])") + } + print("\nSyntactic rules:".bold().cyan()) + for index in systemAlertSyntacticRules.indices { + print("- \(systemAlertSyntacticRules[index])") + } + print("\nBe aware of:".bold().cyan()) + for index in systemAlertNotes.indices { + print("- \(systemAlertNotes[index])") + } + print("\nReturn values:".bold().cyan()) + for index in popupReturnValues.indices { + print("\(popupReturnValues[index]) - \(popupReturnValuesDescription[index])") + } + print("\nUsage examples:".bold().cyan()) + print("- System Alert UI - ".blue() + exampleSystemAlert) + print("\n") + } + static func printNoArgumentsPage() { print("\nIBM Notifier".bold().blue() + "\n") for index in specialArguments.indices { diff --git a/Notification Agent Core/Extensions/NotificationDispatch-Extension.swift b/Notification Agent Core/Extensions/NotificationDispatch-Extension.swift index b184cbc..123bde2 100644 --- a/Notification Agent Core/Extensions/NotificationDispatch-Extension.swift +++ b/Notification Agent Core/Extensions/NotificationDispatch-Extension.swift @@ -28,7 +28,7 @@ extension NotificationDispatch { taskManager.runAsyncTaskOnComponent(.banner, with: jsonData) { terminationStatus in exit(terminationStatus) } - case .popup: + case .popup, .systemalert: var isInteractive: Bool { var int = false object.notification.accessoryViews?.forEach({ accessoryView in diff --git a/Notification Agent Core/Info.plist b/Notification Agent Core/Info.plist index 1d19076..06c95d6 100644 --- a/Notification Agent Core/Info.plist +++ b/Notification Agent Core/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 96 ITSAppUsesNonExemptEncryption LSApplicationCategoryType diff --git a/Notification Agent Onboarding/Info.plist b/Notification Agent Onboarding/Info.plist index 3ea67e1..a76d44f 100644 --- a/Notification Agent Onboarding/Info.plist +++ b/Notification Agent Onboarding/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 96 ITSAppUsesNonExemptEncryption LSApplicationCategoryType diff --git a/Notification Agent Onboarding/Views/OnboardingPageViewController.swift b/Notification Agent Onboarding/Views/OnboardingPageViewController.swift index eed1dd1..346fd0d 100644 --- a/Notification Agent Onboarding/Views/OnboardingPageViewController.swift +++ b/Notification Agent Onboarding/Views/OnboardingPageViewController.swift @@ -89,8 +89,8 @@ final class OnboardingPageViewController: NSViewController { // MARK: - Instance methods - override func viewWillAppear() { - super.viewWillAppear() + override func viewDidLoad() { + super.viewDidLoad() self.setupStackViewLayout() self.setupButtonsLayout() self.configureAccessibilityElements() diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/Contents.json b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/Contents.json index 5dead08..7cf206c 100644 --- a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/Contents.json +++ b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/Contents.json @@ -1,8 +1,9 @@ { "images" : [ { - "filename" : "icon_512x512.png", - "idiom" : "universal" + "filename" : "icon_128x128.png", + "idiom" : "universal", + "scale" : "1x" }, { "appearances" : [ @@ -11,8 +12,9 @@ "value" : "light" } ], - "filename" : "icon_512x512-1.png", - "idiom" : "universal" + "filename" : "icon_128x128 1.png", + "idiom" : "universal", + "scale" : "1x" }, { "appearances" : [ @@ -21,8 +23,63 @@ "value" : "dark" } ], - "filename" : "icon_512x512-2.png", - "idiom" : "universal" + "filename" : "icon_128x128 2.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon_128x128@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "filename" : "icon_128x128@2x 1.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "icon_128x128@2x 2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon_128x128@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "filename" : "icon_128x128@3x 1.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "icon_128x128@3x 2.png", + "idiom" : "universal", + "scale" : "3x" } ], "info" : { diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128 1.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128 1.png new file mode 100644 index 0000000..ebb3f96 Binary files /dev/null and b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128 1.png differ diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128 2.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128 2.png new file mode 100644 index 0000000..ebb3f96 Binary files /dev/null and b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128 2.png differ diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128.png new file mode 100644 index 0000000..ebb3f96 Binary files /dev/null and b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128.png differ diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@2x 1.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@2x 1.png new file mode 100644 index 0000000..63ff287 Binary files /dev/null and b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@2x 1.png differ diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@2x 2.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@2x 2.png new file mode 100644 index 0000000..63ff287 Binary files /dev/null and b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@2x 2.png differ diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@2x.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@2x.png new file mode 100644 index 0000000..63ff287 Binary files /dev/null and b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@2x.png differ diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@3x 1.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@3x 1.png new file mode 100644 index 0000000..f8a7bba Binary files /dev/null and b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@3x 1.png differ diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@3x 2.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@3x 2.png new file mode 100644 index 0000000..f8a7bba Binary files /dev/null and b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@3x 2.png differ diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@3x.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@3x.png new file mode 100644 index 0000000..f8a7bba Binary files /dev/null and b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_128x128@3x.png differ diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_512x512-1.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_512x512-1.png deleted file mode 100644 index 6209152..0000000 Binary files a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_512x512-1.png and /dev/null differ diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_512x512-2.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_512x512-2.png deleted file mode 100644 index 6209152..0000000 Binary files a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_512x512-2.png and /dev/null differ diff --git a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_512x512.png b/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_512x512.png deleted file mode 100644 index 6209152..0000000 Binary files a/Notification Agent Popups/Assets.xcassets/Common/default_icon.imageset/icon_512x512.png and /dev/null differ diff --git a/Notification Agent Popups/Controllers/SystemAlertController.swift b/Notification Agent Popups/Controllers/SystemAlertController.swift new file mode 100644 index 0000000..504a71a --- /dev/null +++ b/Notification Agent Popups/Controllers/SystemAlertController.swift @@ -0,0 +1,116 @@ +// +// SystemAlertController.swift +// Notification Agent +// +// Created by Simone Martorelli on 17/08/22. +// Copyright © 2022 IBM. All rights reserved. +// SPDX-License-Identifier: Apache2.0 +// + +import Foundation +import AppKit + +/// Handle the systemAlert UIs appearance and behavior. +final class SystemAlertController { + + // MARK: - Variables + + let notificationObject: NotificationObject + let alert: NSAlert + var suppressNotification: Bool + + // MARK: - Initializers + + init(_ object: NotificationObject) { + notificationObject = object + alert = NSAlert() + suppressNotification = false + setupAlert() + } + + // MARK: - Private Methods + + /// Setup the alert layout, components and appearance. + private func setupAlert() { + alert.alertStyle = .informational + + // Setting up labels. + alert.messageText = notificationObject.title ?? "" + alert.informativeText = notificationObject.subtitle ?? "" + + // Setting up alert icon. + if let iconPath = notificationObject.iconPath { + if FileManager.default.fileExists(atPath: iconPath), + let data = try? Data(contentsOf: URL(fileURLWithPath: iconPath)) { + let image = NSImage(data: data) + alert.icon = image + } else if iconPath.isValidURL, + let url = URL(string: iconPath), + let data = try? Data(contentsOf: url) { + let image = NSImage(data: data) + alert.icon = image + } else if let imageData = Data(base64Encoded: iconPath, options: .ignoreUnknownCharacters), + let image = NSImage(data: imageData) { + alert.icon = image + } else { + NALogger.shared.log("Unable to load image from %{public}@", [iconPath]) + } + } + + // Setting up buttons. + let mainButton = alert.addButton(withTitle: notificationObject.mainButton.label) + mainButton.action = #selector(self.didPressMainButton(_:)) + mainButton.target = self + if let secondaryButton = notificationObject.secondaryButton { + let secondaryButton = alert.addButton(withTitle: secondaryButton.label) + secondaryButton.action = #selector(self.didPressSecondaryButton(_:)) + secondaryButton.target = self + } + if let tertiaryButton = notificationObject.tertiaryButton { + let tertiaryButton = alert.addButton(withTitle: tertiaryButton.label) + tertiaryButton.action = #selector(self.didPressTertiaryButton(_:)) + tertiaryButton.target = self + } + + // Setting up help button. + alert.showsHelp = false + + // Setting up suppression button. + alert.showsSuppressionButton = notificationObject.showSuppressionButton ?? false + alert.suppressionButton?.action = #selector(didPressSuppressButton(_:)) + alert.suppressionButton?.target = self + } + + // MARK: - Public Methods + + /// Show the alert to the end user. + func showAlert() { + if !(notificationObject.silent ?? false) { + NSSound(named: .init("Funk"))?.play() + } + alert.runModal() + alert.window.setWindowPosition(notificationObject.position ?? .center) + } + + // MARK: - Actions + + @objc + private func didPressMainButton(_ sender: NSButton) { + if suppressNotification { print("suppress") } + ReplyHandler.shared.handleResponse(ofType: .main, for: notificationObject) + } + @objc + private func didPressSecondaryButton(_ sender: NSButton) { + if suppressNotification { print("suppress") } + ReplyHandler.shared.handleResponse(ofType: .secondary, for: notificationObject) + } + @objc + private func didPressTertiaryButton(_ sender: NSButton) { + if suppressNotification { print("suppress") } + ReplyHandler.shared.handleResponse(ofType: .tertiary, for: notificationObject) + } + @objc + private func didPressSuppressButton(_ sender: NSButton) { + suppressNotification = sender.state == .on + } +} diff --git a/Notification Agent Popups/Extensions/NotificationDispatch-Extension.swift b/Notification Agent Popups/Extensions/NotificationDispatch-Extension.swift index a7e1a69..54d0025 100644 --- a/Notification Agent Popups/Extensions/NotificationDispatch-Extension.swift +++ b/Notification Agent Popups/Extensions/NotificationDispatch-Extension.swift @@ -16,22 +16,32 @@ extension NotificationDispatch { @objc func receivedNotification(_ notification: Notification) { guard let object = notification.userInfo?["object"] as? NotificationObject else { return } - DispatchQueue.main.async { - let storyboard = NSStoryboard(name: "Main", bundle: nil) - guard let popUpViewController = storyboard.instantiateController(withIdentifier: PopUpViewController.identifier) as? PopUpViewController else { return } - popUpViewController.notificationObject = object - let window = NSWindow(contentViewController: popUpViewController) - if object.forceLightMode ?? false { - window.appearance = NSAppearance(named: .aqua) + switch object.type { + case .systemalert: + DispatchQueue.main.async { + let alertController = SystemAlertController(object) + alertController.showAlert() } - window.styleMask.remove(.resizable) - if !(object.isMiniaturizable ?? false) { - window.styleMask.remove(.miniaturizable) + case .popup: + DispatchQueue.main.async { + let storyboard = NSStoryboard(name: "Main", bundle: nil) + guard let popUpViewController = storyboard.instantiateController(withIdentifier: PopUpViewController.identifier) as? PopUpViewController else { return } + popUpViewController.notificationObject = object + let window = NSWindow(contentViewController: popUpViewController) + if object.forceLightMode ?? false { + window.appearance = NSAppearance(named: .aqua) + } + window.styleMask.remove(.resizable) + if !(object.isMiniaturizable ?? false) { + window.styleMask.remove(.miniaturizable) + } + window.styleMask.remove(.closable) + window.makeKeyAndOrderFront(self) + guard object.silent == false else { return } + NSSound(named: .init("Funk"))?.play() } - window.styleMask.remove(.closable) - window.makeKeyAndOrderFront(self) - guard object.silent == false else { return } - NSSound(named: .init("Funk"))?.play() + default: + Utils.applicationExit(withReason: .internalError) } } } diff --git a/Notification Agent Popups/Info.plist b/Notification Agent Popups/Info.plist index 3ea67e1..a76d44f 100644 --- a/Notification Agent Popups/Info.plist +++ b/Notification Agent Popups/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 96 ITSAppUsesNonExemptEncryption LSApplicationCategoryType diff --git a/Notification Agent Popups/Views/PopUpViewController.swift b/Notification Agent Popups/Views/PopUpViewController.swift index fb1981b..777ff51 100644 --- a/Notification Agent Popups/Views/PopUpViewController.swift +++ b/Notification Agent Popups/Views/PopUpViewController.swift @@ -206,7 +206,6 @@ class PopUpViewController: NSViewController { self.popupElementsStackView.insertView(progressBarAccessoryView, at: 0, in: .center) progressBarAccessoryView.progressBarDelegate = self progressBarAccessoryView.delegate = self - progressBarAccessoryView.startObservingForUpdates() self.accessoryViews.append(progressBarAccessoryView) self.shouldAllowCancel = progressBarAccessoryView.isUserInterruptionAllowed case .image: diff --git a/Notification Agent.xcodeproj/project.pbxproj b/Notification Agent.xcodeproj/project.pbxproj index be89606..ac56674 100644 --- a/Notification Agent.xcodeproj/project.pbxproj +++ b/Notification Agent.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 53; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -247,6 +247,8 @@ B9EDC98E24EC0D91004F6108 /* InfoSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9EDC98D24EC0D91004F6108 /* InfoSection.swift */; }; F47F553E249B8C1B006A0754 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F47F553D249B8C1B006A0754 /* Assets.xcassets */; }; F47F5541249B8C1B006A0754 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F47F553F249B8C1B006A0754 /* Main.storyboard */; }; + FD0D02D528D9F8AE00A167B7 /* SystemAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0D02D428D9F8AE00A167B7 /* SystemAlertController.swift */; }; + FD0D02D628D9F8AE00A167B7 /* SystemAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0D02D428D9F8AE00A167B7 /* SystemAlertController.swift */; }; FD1730032875B02F00781A69 /* OnboardingInteractiveEFCLController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1730022875B02F00781A69 /* OnboardingInteractiveEFCLController.swift */; }; FD1730042875B02F00781A69 /* OnboardingInteractiveEFCLController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1730022875B02F00781A69 /* OnboardingInteractiveEFCLController.swift */; }; FD1730072875B0D200781A69 /* PopupInteractiveEFCLController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1730062875B0D200781A69 /* PopupInteractiveEFCLController.swift */; }; @@ -750,6 +752,7 @@ F47F553D249B8C1B006A0754 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; F47F5540249B8C1B006A0754 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; F47F5542249B8C1B006A0754 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + FD0D02D428D9F8AE00A167B7 /* SystemAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemAlertController.swift; sourceTree = ""; }; FD1730022875B02F00781A69 /* OnboardingInteractiveEFCLController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingInteractiveEFCLController.swift; sourceTree = ""; }; FD1730062875B0D200781A69 /* PopupInteractiveEFCLController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupInteractiveEFCLController.swift; sourceTree = ""; }; FD1730092875B82000781A69 /* DynamicNotificationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicNotificationButton.swift; sourceTree = ""; }; @@ -970,6 +973,7 @@ B961A5B4267B451400657930 /* Main.storyboard */, B961A62B267C9DA200657930 /* Assets.xcassets */, FD1730062875B0D200781A69 /* PopupInteractiveEFCLController.swift */, + FD0D02D728D9F8B500A167B7 /* Controllers */, B9360EBA268A04A900217247 /* Extensions */, B961A5F8267B8B7800657930 /* Views */, ); @@ -1210,6 +1214,14 @@ path = "Notification Agent Core"; sourceTree = ""; }; + FD0D02D728D9F8B500A167B7 /* Controllers */ = { + isa = PBXGroup; + children = ( + FD0D02D428D9F8AE00A167B7 /* SystemAlertController.swift */, + ); + path = Controllers; + sourceTree = ""; + }; FD1730052875B04600781A69 /* Controllers */ = { isa = PBXGroup; children = ( @@ -1369,7 +1381,7 @@ isa = PBXNativeTarget; buildConfigurationList = F47F555C249B8C1C006A0754 /* Build configuration list for PBXNativeTarget "IBM Notifier" */; buildPhases = ( - B9710AB424A0A584000B5A67 /* ShellScript */, + B9710AB424A0A584000B5A67 /* SwiftLint */, F47F5532249B8C1B006A0754 /* Sources */, F47F5533249B8C1B006A0754 /* Frameworks */, F47F5534249B8C1B006A0754 /* Resources */, @@ -1743,8 +1755,9 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - B9710AB424A0A584000B5A67 /* ShellScript */ = { + B9710AB424A0A584000B5A67 /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1752,6 +1765,7 @@ ); inputPaths = ( ); + name = SwiftLint; outputFileListPaths = ( ); outputPaths = ( @@ -1864,6 +1878,7 @@ B961A52B267A4D8F00657930 /* DropDownAccessoryView.swift in Sources */, B961A533267A4DC700657930 /* HorizontalLine.swift in Sources */, B961A50F267A4D3C00657930 /* Environment.swift in Sources */, + FD0D02D528D9F8AE00A167B7 /* SystemAlertController.swift in Sources */, B961A527267A4D8F00657930 /* HTMLAccessoryView.swift in Sources */, FD1730182875B9A000781A69 /* InteractiveObjectProtocol.swift in Sources */, ); @@ -2184,6 +2199,7 @@ FDC3474B2858C4E8001F2212 /* PopUpViewController.swift in Sources */, FDC346A72858C42A001F2212 /* ReplyHandler.swift in Sources */, FDC346E12858C459001F2212 /* UserReplyType.swift in Sources */, + FD0D02D628D9F8AE00A167B7 /* SystemAlertController.swift in Sources */, FDC347062858C476001F2212 /* InfoPopOverStackItem.swift in Sources */, FDC347292858C494001F2212 /* Decodable-Extensions.swift in Sources */, FDC3473E2858C4AF001F2212 /* InteractiveEFCLContoller.swift in Sources */, @@ -2428,7 +2444,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 87; + CURRENT_PROJECT_VERSION = 97; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "Notification Agent Alerts/Info.plist"; @@ -2437,13 +2453,14 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 2.8.0; + MARKETING_VERSION = 2.9.1; PRODUCT_BUNDLE_IDENTIFIER = com.ibm.cio.notifier.alert; PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; @@ -2456,7 +2473,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 87; + CURRENT_PROJECT_VERSION = 97; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "Notification Agent Alerts/Info.plist"; @@ -2465,13 +2482,14 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 2.8.0; + MARKETING_VERSION = 2.9.1; PRODUCT_BUNDLE_IDENTIFIER = com.ibm.cio.notifier.alert; PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; @@ -2484,7 +2502,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 87; + CURRENT_PROJECT_VERSION = 97; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "Notification Agent Popups/Info.plist"; @@ -2493,13 +2511,14 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 2.8.0; + MARKETING_VERSION = 2.9.1; PRODUCT_BUNDLE_IDENTIFIER = com.ibm.cio.notifier.popup; PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; @@ -2512,7 +2531,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 87; + CURRENT_PROJECT_VERSION = 97; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "Notification Agent Popups/Info.plist"; @@ -2521,13 +2540,14 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 2.8.0; + MARKETING_VERSION = 2.9.1; PRODUCT_BUNDLE_IDENTIFIER = com.ibm.cio.notifier.popup; PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; @@ -2540,7 +2560,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 87; + CURRENT_PROJECT_VERSION = 97; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "Notification Agent Onboarding/Info.plist"; @@ -2549,12 +2569,13 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 2.8.0; + MARKETING_VERSION = 2.9.1; PRODUCT_BUNDLE_IDENTIFIER = com.ibm.cio.notifier.onboarding; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; @@ -2567,7 +2588,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 87; + CURRENT_PROJECT_VERSION = 97; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "Notification Agent Onboarding/Info.plist"; @@ -2576,12 +2597,13 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 2.8.0; + MARKETING_VERSION = 2.9.1; PRODUCT_BUNDLE_IDENTIFIER = com.ibm.cio.notifier.onboarding; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; @@ -2594,7 +2616,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 87; + CURRENT_PROJECT_VERSION = 97; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "Notification Agent Banners/Info.plist"; @@ -2603,13 +2625,14 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 2.8.0; + MARKETING_VERSION = 2.9.1; PRODUCT_BUNDLE_IDENTIFIER = com.ibm.cio.notifier.banner; PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; @@ -2622,7 +2645,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 87; + CURRENT_PROJECT_VERSION = 97; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "Notification Agent Banners/Info.plist"; @@ -2631,13 +2654,14 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 2.8.0; + MARKETING_VERSION = 2.9.1; PRODUCT_BUNDLE_IDENTIFIER = com.ibm.cio.notifier.banner; PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; @@ -2675,6 +2699,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 97; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -2694,6 +2719,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 2.9.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -2737,6 +2763,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 97; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -2750,6 +2777,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 2.9.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; @@ -2767,7 +2795,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 87; + CURRENT_PROJECT_VERSION = 97; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GCC_GENERATE_TEST_COVERAGE_FILES = YES; @@ -2777,12 +2805,13 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 2.8.0; + MARKETING_VERSION = 2.9.1; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.ibm.cio.notifier; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; @@ -2795,7 +2824,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 87; + CURRENT_PROJECT_VERSION = 97; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GCC_GENERATE_TEST_COVERAGE_FILES = YES; @@ -2805,12 +2834,13 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 2.8.0; + MARKETING_VERSION = 2.9.1; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = com.ibm.cio.notifier; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; @@ -2820,7 +2850,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2841,7 +2871,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2862,7 +2892,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2883,7 +2913,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2904,7 +2934,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2925,7 +2955,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2946,7 +2976,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2967,7 +2997,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2987,7 +3017,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -3007,7 +3037,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -3028,7 +3058,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -3049,7 +3079,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; DEVELOPMENT_TEAM = ""; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -3069,7 +3099,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 10.15; @@ -3087,7 +3117,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 96; ENABLE_TESTING_SEARCH_PATHS = YES; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 10.15; diff --git a/Shared/Controllers/EFCLController.swift b/Shared/Controllers/EFCLController.swift index 9167090..7e1b6e3 100644 --- a/Shared/Controllers/EFCLController.swift +++ b/Shared/Controllers/EFCLController.swift @@ -33,8 +33,9 @@ final class EFCLController { "miniaturizable", "force_light_mode", "hide_title_bar_buttons", - "retain_values" - ] + "retain_values", + "retain_values", + "show_suppression_button"] // MARK: - Variables diff --git a/Shared/Model/UIObjects/NotificationObject.swift b/Shared/Model/UIObjects/NotificationObject.swift index 8880035..35999ea 100644 --- a/Shared/Model/UIObjects/NotificationObject.swift +++ b/Shared/Model/UIObjects/NotificationObject.swift @@ -22,6 +22,7 @@ public final class NotificationObject: NSObject, Codable, NSSecureCoding { case banner // Temporary user notification banner. case onboarding // Onboarding window. case alert // Persistent user notification banner. + case systemalert // Standard system Alert pop-up. } /// A set of predefined workflow @@ -92,6 +93,8 @@ public final class NotificationObject: NSObject, Codable, NSSecureCoding { var hideTitleBarButtons: Bool? /// A boolean value that define if to print the available accessory view outputs on the secondary button click. var retainValues: Bool? + /// A boolean value that define if to show the suppress notification checkbox on the systemAlert UI. Works only with systemAlert UI type. + var showSuppressionButton: Bool? /// If defined the app should just run the predefined workflow var workflow: PredefinedWorkflow? @@ -206,6 +209,11 @@ public final class NotificationObject: NSObject, Codable, NSSecureCoding { } else { self.retainValues = false } + if let showSuppressionButtonRaw = dict["show_suppression_button"] as? String { + self.showSuppressionButton = showSuppressionButtonRaw.lowercased() == "true" + } else { + self.showSuppressionButton = false + } if let positionRaw = dict["position"] as? String { self.position = NSWindow.WindowPosition(rawValue: positionRaw) } @@ -222,7 +230,7 @@ public final class NotificationObject: NSObject, Codable, NSSecureCoding { private func checkObjectConsistency() throws { switch type { - case .popup, .banner, .alert: + case .popup, .banner, .alert, .systemalert: guard self.title != nil || self.subtitle != nil || !(self.accessoryViews?.isEmpty ?? true) || self.workflow != nil else { throw NAError.dataFormat(type: .noInfoToShow) } @@ -308,6 +316,7 @@ public final class NotificationObject: NSObject, Codable, NSSecureCoding { case popupReminder case hideTitleBarButtons case retainValues + case showSuppressionButton case workflow } @@ -340,6 +349,7 @@ public final class NotificationObject: NSObject, Codable, NSSecureCoding { self.hideTitleBarButtons = try container.decodeIfPresent(Bool.self, forKey: .hideTitleBarButtons) self.forceLightMode = try container.decodeIfPresent(Bool.self, forKey: .forceLightMode) self.retainValues = try container.decodeIfPresent(Bool.self, forKey: .retainValues) + self.showSuppressionButton = try container.decodeIfPresent(Bool.self, forKey: .showSuppressionButton) self.payload = try container.decodeIfPresent(OnboardingData.self, forKey: .payload) if let positionRawValue = try container.decodeIfPresent(String.self, forKey: .position) { self.position = NSWindow.WindowPosition(rawValue: positionRawValue) @@ -377,6 +387,7 @@ public final class NotificationObject: NSObject, Codable, NSSecureCoding { try container.encodeIfPresent(self.hideTitleBarButtons, forKey: .hideTitleBarButtons) try container.encodeIfPresent(self.forceLightMode, forKey: .forceLightMode) try container.encodeIfPresent(self.retainValues, forKey: .retainValues) + try container.encodeIfPresent(self.showSuppressionButton, forKey: .showSuppressionButton) try container.encodeIfPresent(self.payload, forKey: .payload) try container.encodeIfPresent(self.position?.rawValue, forKey: .position) try container.encodeIfPresent(self.popupReminder, forKey: .popupReminder) @@ -459,6 +470,10 @@ public final class NotificationObject: NSObject, Codable, NSSecureCoding { let number = NSNumber(booleanLiteral: retainValues) coder.encode(number, forKey: NOCodingKeys.retainValues.rawValue) } + if let showSuppressionButton = self.showSuppressionButton { + let number = NSNumber(booleanLiteral: showSuppressionButton) + coder.encode(number, forKey: NOCodingKeys.showSuppressionButton.rawValue) + } if let position = self.position?.rawValue { coder.encode(position, forKey: NOCodingKeys.position.rawValue) } @@ -496,6 +511,7 @@ public final class NotificationObject: NSObject, Codable, NSSecureCoding { self.hideTitleBarButtons = coder.decodeObject(of: NSNumber.self, forKey: NOCodingKeys.hideTitleBarButtons.rawValue) as? Bool self.forceLightMode = coder.decodeObject(of: NSNumber.self, forKey: NOCodingKeys.forceLightMode.rawValue) as? Bool self.retainValues = coder.decodeObject(of: NSNumber.self, forKey: NOCodingKeys.retainValues.rawValue) as? Bool + self.showSuppressionButton = coder.decodeObject(of: NSNumber.self, forKey: NOCodingKeys.showSuppressionButton.rawValue) as? Bool if let positionRawValue = coder.decodeObject(of: NSString.self, forKey: NOCodingKeys.position.rawValue) { self.position = NSWindow.WindowPosition(rawValue: positionRawValue as String) } diff --git a/Shared/Views/AccessoryViews/HTMLAccessoryView.swift b/Shared/Views/AccessoryViews/HTMLAccessoryView.swift index b615162..0df9f4a 100644 --- a/Shared/Views/AccessoryViews/HTMLAccessoryView.swift +++ b/Shared/Views/AccessoryViews/HTMLAccessoryView.swift @@ -153,7 +153,7 @@ final class HTMLAccessoryView: AccessoryView { let newHtml = #""# + html + "" let data = Data(newHtml.utf8) do { - let attributedString = try NSMutableAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .defaultAttributes: defaultAttributes], documentAttributes: nil) + let attributedString = try NSMutableAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .defaultAttributes: defaultAttributes, .textEncodingName: "UTF8"], documentAttributes: nil) self.textView.textStorage?.setAttributedString(attributedString) } catch { NALogger.shared.log("Unable to parse the given HTML. No text will be shown. %{public}@", [error.localizedDescription]) diff --git a/Shared/Views/AccessoryViews/InputAccessoryView.swift b/Shared/Views/AccessoryViews/InputAccessoryView.swift index 33747f7..3bce9c1 100644 --- a/Shared/Views/AccessoryViews/InputAccessoryView.swift +++ b/Shared/Views/AccessoryViews/InputAccessoryView.swift @@ -66,6 +66,7 @@ class InputAccessoryView: AccessoryView { override func adjustViewSize() { inputTextField.widthAnchor.constraint(equalToConstant: containerWidth).isActive = true + adjustTextAreaHeight() } override func configureAccessibilityElements() { @@ -109,6 +110,7 @@ class InputAccessoryView: AccessoryView { fieldTopAnchor.isActive = false fieldTopAnchor = inputTextField.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 4) fieldTopAnchor.isActive = true + fieldTopAnchor.priority = .defaultHigh hasTitle = true case "placeholder": self.inputTextField.placeholderString = value @@ -125,17 +127,22 @@ class InputAccessoryView: AccessoryView { } self.mainButtonState = (self.isRequired && self.inputTextField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) ? .disabled : .enabled } -} - -extension InputAccessoryView: NSTextFieldDelegate { - func controlTextDidChange(_ obj: Notification) { - self.mainButtonState = (inputTextField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && isRequired) ? .disabled : .enabled + + private func adjustTextAreaHeight() { let height = inputTextField.sizeThatFits(NSSize(width: inputTextField.bounds.width, height: 0)).height if height != inputTextField.bounds.height && !preventResize { self.textFieldHeightAnchor?.isActive = false self.textFieldHeightAnchor = self.inputTextField.heightAnchor.constraint(equalToConstant: min(height, 200)) self.textFieldHeightAnchor.isActive = true + self.textFieldHeightAnchor.priority = .required } + } +} + +extension InputAccessoryView: NSTextFieldDelegate { + func controlTextDidChange(_ obj: Notification) { + self.mainButtonState = (inputTextField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && isRequired) ? .disabled : .enabled + self.adjustTextAreaHeight() delegate?.accessoryViewStatusDidChange(self) } } diff --git a/Shared/Views/AccessoryViews/ProgressBarAccessoryView.swift b/Shared/Views/AccessoryViews/ProgressBarAccessoryView.swift index 3a81696..70b6d9d 100644 --- a/Shared/Views/AccessoryViews/ProgressBarAccessoryView.swift +++ b/Shared/Views/AccessoryViews/ProgressBarAccessoryView.swift @@ -59,7 +59,7 @@ class ProgressBarAccessoryView: AccessoryView, InteractiveObjectProtocol { } deinit { - NotificationCenter.default.removeObserver(self, name: Notification.Name(self.objectIdentifier), object: nil) + NotificationCenter.default.removeObserver(self) } // MARK: - Instance methods