Skip to content

Commit

Permalink
Merge pull request #167 from IBM/development
Browse files Browse the repository at this point in the history
Version 3.0.0 Build 104
  • Loading branch information
jvalentik authored Jul 24, 2023
2 parents 2dccbc0 + bb851c8 commit 832719c
Show file tree
Hide file tree
Showing 99 changed files with 4,096 additions and 3,215 deletions.
Binary file added Images/Popup/datepicker_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/Popup/datepicker_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions Notification Agent Alerts/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>96</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
Expand All @@ -32,7 +32,7 @@
<true/>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2021 IBM Inc. All rights reserved.</string>
<string>Copyright © 2021 IBM. All rights reserved.</string>
<key>NSMainStoryboardFile</key>
<string>Main</string>
<key>NSPrincipalClass</key>
Expand Down
6 changes: 2 additions & 4 deletions Notification Agent Banners/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
Expand All @@ -19,7 +17,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>96</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
Expand All @@ -34,7 +32,7 @@
<true/>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2021 IBM Inc. All rights reserved.</string>
<string>Copyright © 2021 IBM. All rights reserved.</string>
<key>NSMainStoryboardFile</key>
<string>Main</string>
<key>NSPrincipalClass</key>
Expand Down
8 changes: 4 additions & 4 deletions Notification Agent Core Tests/NACTriggersTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ class NACTriggersTests: XCTestCase {
["toBeRemoved", "-type", "banner", "-title", "This is a title"],
["toBeRemoved", "-type", "alert", "-title", "This is a title"],
["toBeRemoved", "-type", "popup", "-title", "This is a title", "-silent", "-subtitle", "This is a subtitle"]]
let processerUseCases = [URL(string: "macatibm:shownotification?type=popup&title=This%20is%20a%20title")!,
URL(string: "macatibm:shownotification?type=banner&title=This%20is%20a%20title")!,
URL(string: "macatibm:shownotification?type=alert&title=This%20is%20a%20title")!,
URL(string: "macatibm:shownotification?type=alert&title=title&silent&subtitle=This%20is%20a%20subtitle")!]
let processerUseCases = [URL(string: "ibmnotifier:shownotification?type=popup&title=This%20is%20a%20title")!,
URL(string: "ibmnotifier:shownotification?type=banner&title=This%20is%20a%20title")!,
URL(string: "ibmnotifier:shownotification?type=alert&title=This%20is%20a%20title")!,
URL(string: "ibmnotifier:shownotification?type=alert&title=title&silent&subtitle=This%20is%20a%20subtitle")!]

override func setUpWithError() throws {
worker.startObservation()
Expand Down
52 changes: 33 additions & 19 deletions Notification Agent Core/Controllers/HelpBuilder.swift

Large diffs are not rendered by default.

25 changes: 10 additions & 15 deletions Notification Agent Core/Controllers/NALogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,26 @@ import os.log
/// A simple class based on Apple os.log that handle normal and verbose logs.
public final class NALogger {

// MARK: - Static Variables

static let shared = NALogger()

// MARK: - Methods

func log(_ type: OSLogType, _ message: StaticString, _ args: [String] = []) {
if #available(OSX 11.0, *) {
Logger().log(level: type,
"\(String(format: message.description.replacingOccurrences(of: "{public}", with: ""), arguments: args), privacy: .public)")
} else {
os_log(type, message, args)
}
Logger().log(level: type,
"\(String(format: message.description.replacingOccurrences(of: "{public}", with: ""), arguments: args), privacy: .public)")
if Context.main.sharedSettings.isVerboseModeEnabled || type == .error {
self.verbose(type, message, args)
}
}

func log(_ message: StaticString, _ args: [String] = []) {
if #available(OSX 11.0, *) {
Logger().log(level: .default,
"\(String(format: message.description.replacingOccurrences(of: "{public}", with: ""), arguments: args), privacy: .public)")
} else {
os_log(message, args)
}
Logger().log(level: .default,
"\(String(format: message.description.replacingOccurrences(of: "{public}", with: ""), arguments: args), privacy: .public)")
if Context.main.sharedSettings.isVerboseModeEnabled {
self.verbose(.default, message, args)
}
}

func deprecationLog(since version: AppVersion, deprecatedArgument: String) {
if version.isFinalDeprecatedVersion() {
self.log(.error, "The following argument has been deprecated: %{public}@. Please update your workflow.", [deprecatedArgument])
Expand All @@ -47,12 +41,13 @@ public final class NALogger {
}
}

// MARK: - Private Methods

private func verbose(_ type: OSLogType, _ message: StaticString, _ args: [String] = []) {
let message = type == .error ?
message.description.replacingOccurrences(of: "{public}", with: "").red() :
message.description.replacingOccurrences(of: "{public}", with: "").yellow()

print(String(format: message, arguments: args))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Foundation

extension EFCLController {

// MARK: - Internal methods
// MARK: - Internal Methods

/// Exit the app with the related reason code.
/// - Parameter reason: reason why the application should exit.
Expand Down Expand Up @@ -50,7 +50,7 @@ extension EFCLController {
}
}

// MARK: - Private methods
// MARK: - Private Methods

/// Check if the passed arguments are configuration.
/// - Parameter arguments: the app's launch arguments.
Expand Down
2 changes: 1 addition & 1 deletion Notification Agent Core/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>96</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
Expand Down
2 changes: 1 addition & 1 deletion Notification Agent Onboarding Tests/NAOTriggersTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class NAOTriggersTests: XCTestCase {
]
]
]
],
] as [String : Any],
[
"title": "This is a title",
"subtitle": "This is a subtitle",
Expand Down
37 changes: 29 additions & 8 deletions Notification Agent Onboarding UI Tests/NAOUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,44 @@
//
// Created by Simone Martorelli on 08/06/22.
// Copyright © 2022 IBM. All rights reserved.
// SPDX-License-Identifier: Apache2.0
//

import XCTest

class NAOUITests: XCTestCase {

/// Testing simple Pop-up with:
/// Title: This is a title
/// Main Button: Ok
/// Testing simple Onboarding UI
func test1Onboarding() throws {
let useCase = "eyJub3RpZmljYXRpb24iOnsidG9waWNJRCI6InVudHJhY2tlZCIsIm1haW5CdXR0b24iOnsibGFiZWwiOiJPSyIsImNhbGxUb0FjdGlvblR5cGUiOiJub25lIiwiY2FsbFRvQWN0aW9uUGF5bG9hZCI6IiJ9LCJub3RpZmljYXRpb25JRCI6InVudHJhY2tlZCIsInJldGFpblZhbHVlcyI6ZmFsc2UsImFjY2Vzc29yeVZpZXdzIjpbXSwicGF5bG9hZCI6eyJwYWdlcyI6W3siYm9keSI6IlNvbWUgYm9keSIsInRpdGxlIjoiVGhpcyBpcyBhIHRpdGxlIiwic3VidGl0bGUiOiJUaGlzIGlzIGEgc3VidGl0bGUiLCJhY2Nlc3NvcnlWaWV3cyI6W1t7InR5cGUiOiJpbnB1dCIsInBheWxvYWQiOiJcL3BsYWNlaG9sZGVyIFNvbWUgXC90aXRsZSBGaXJzdCJ9LHsidHlwZSI6ImlucHV0IiwicGF5bG9hZCI6IlwvcGxhY2Vob2xkZXIgU29tZSBcL3RpdGxlIFNlY29uZCJ9XV19LHsiYm9keSI6IlNvbWUgYm9keSIsInRpdGxlIjoiVGhpcyBpcyBhIHRpdGxlIiwic3VidGl0bGUiOiJUaGlzIGlzIGEgc3VidGl0bGUiLCJhY2Nlc3NvcnlWaWV3cyI6W1t7InR5cGUiOiJpbnB1dCIsInBheWxvYWQiOiJcL3BsYWNlaG9sZGVyIFNvbWUgXC90aXRsZSBGaXJzdCJ9LHsidHlwZSI6ImlucHV0IiwicGF5bG9hZCI6IlwvcGxhY2Vob2xkZXIgU29tZSBcL3RpdGxlIFNlY29uZCJ9XV19XX0sImFsd2F5c09uVG9wIjpmYWxzZSwidHlwZSI6Im9uYm9hcmRpbmciLCJzaWxlbnQiOmZhbHNlLCJtaW5pYXR1cml6YWJsZSI6ZmFsc2UsImJhclRpdGxlIjoiTWFjQElCTSBOb3RpZmljYXRpb25zIiwiZm9yY2VMaWdodE1vZGUiOmZhbHNlLCJoaWRlVGl0bGVCYXJCdXR0b25zIjpmYWxzZX0sInNldHRpbmdzIjp7ImVudmlyb25tZW50IjoicHJvZCIsImlzUmVzdENsaWVudEVuYWJsZWQiOmZhbHNlLCJpc0FuYWx5dGljc0VuYWJsZWQiOmZhbHNlLCJpc1ZlcmJvc2VNb2RlRW5hYmxlZCI6ZmFsc2V9fQ==" // pragma: allowlist-secret
let useCase = "eyJub3RpZmljYXRpb24iOnsidG9waWNJRCI6InVudHJhY2tlZCIsIm1haW5CdXR0b24iOnsibGFiZWwiOiJPSyIsImNhbGxUb0FjdGlvblR5cGUiOiJub25lIiwiY2FsbFRvQWN0aW9uUGF5bG9hZCI6IiJ9LCJub3RpZmljYXRpb25JRCI6InVudHJhY2tlZCIsInJldGFpblZhbHVlcyI6ZmFsc2UsImFjY2Vzc29yeVZpZXdzIjpbXSwicGF5bG9hZCI6eyJwYWdlcyI6W3siYm9keSI6IkZpcnN0IHBhZ2UncyBib2R5IiwidG9wSWNvbiI6InNxdWFyZS5hbmQuYXJyb3cudXAiLCJ0aXRsZSI6IkZpcnN0IHBhZ2UncyB0aXRsZSIsInN1YnRpdGxlIjoiRmlyc3QgcGFnZSdzIHN1YnRpdGxlIiwiaW5mb1NlY3Rpb24iOnsiZmllbGRzIjpbeyJpZCI6IlNvbWUgRGVzY3JpcHRpb24gU29tZSIsImxhYmVsIjoiU29tZSBEZXNjcmlwdGlvbiBTb21lIn0seyJpZCI6IlNvbWUgRGVzY3JpcHRpb24gU29tZSIsImxhYmVsIjoiU29tZSBEZXNjcmlwdGlvbiBTb21lIn0seyJpZCI6IlNvbWUgRGVzY3JpcHRpb24gU29tZSIsImxhYmVsIjoiU29tZSBEZXNjcmlwdGlvbiBTb21lIn1dfX0seyJzdWJ0aXRsZSI6IlNlY29uZCBwYWdlJ3Mgc3VidGl0bGUiLCJzaW5nbGVDaGFuZ2UiOnRydWUsInByaW1hcnlCdXR0b25MYWJlbCI6IlNvbWUiLCJ0aXRsZSI6IlNlY29uZCBwYWdlJ3MgdGl0bGUiLCJ0ZXJ0aWFyeUJ1dHRvbiI6eyJsYWJlbCI6IlRlcnRpYXJ5IiwiY2FsbFRvQWN0aW9uVHlwZSI6ImxpbmsiLCJjYWxsVG9BY3Rpb25QYXlsb2FkIjoiaHR0cHM6XC9cL3d3dy5nb29nbGUuY29tIn0sImluZm9TZWN0aW9uIjp7ImZpZWxkcyI6W3siaWQiOiJGaXJzdCBsYWJlbCBvbmx5IiwibGFiZWwiOiJGaXJzdCBsYWJlbCBvbmx5In0seyJpZCI6IlNlY29uZCBsYWJlbCBvbmx5IiwibGFiZWwiOiJTZWNvbmQgbGFiZWwgb25seSJ9LHsiaWQiOiJUaGlyZCBsYWJlbCBvbmx5IiwibGFiZWwiOiJUaGlyZCBsYWJlbCBvbmx5In1dfSwiYm9keSI6IlNlY29uZCBwYWdlJ3MgYm9keSJ9LHsiYm9keSI6IlRoaXJkIHBhZ2UncyBib2R5IiwidGl0bGUiOiJUaGlyZCBwYWdlJ3MgdGl0bGUiLCJzdWJ0aXRsZSI6IlRoaXJkIHBhZ2UncyBzdWJ0aXRsZSIsInNpbmdsZUNoYW5nZSI6dHJ1ZX0seyJ0aXRsZSI6IkZvdXJ0aCBwYWdlJ3MgdGl0bGUiLCJzdWJ0aXRsZSI6IkZvdXJ0aCBwYWdlJ3Mgc3VidGl0bGUiLCJib2R5IjoiRm91cnRoIHBhZ2UncyBib2R5In1dLCJwcm9ncmVzc0JhclBheWxvYWQiOiJhdXRvbWF0aWMifSwiaXNNb3ZhYmxlIjp0cnVlLCJhbHdheXNPblRvcCI6ZmFsc2UsInR5cGUiOiJvbmJvYXJkaW5nIiwic2lsZW50IjpmYWxzZSwic2hvd1N1cHByZXNzaW9uQnV0dG9uIjpmYWxzZSwibWluaWF0dXJpemFibGUiOmZhbHNlLCJiYXJUaXRsZSI6Ik1hY0BJQk0gTm90aWZpY2F0aW9ucyIsImZvcmNlTGlnaHRNb2RlIjpmYWxzZSwiaGlkZVRpdGxlQmFyQnV0dG9ucyI6ZmFsc2V9LCJzZXR0aW5ncyI6eyJpc1ZlcmJvc2VNb2RlRW5hYmxlZCI6ZmFsc2UsImVudmlyb25tZW50IjoicHJvZCJ9fQ==" // pragma: allowlist-secret
let app = XCUIApplication()
app.launchArguments = [useCase]
app.launch()
XCTAssert(app.buttons["onboarding_accessibility_button_right"].exists)
app.buttons["onboarding_accessibility_button_right"].tap()
XCTAssert(app.buttons["onboarding_accessibility_button_right"].exists)
XCTAssert(app.buttons["onboarding_accessibility_button_left"].exists)
XCTAssert(app.staticTexts["onboarding_title"].exists)
XCTAssert(app.staticTexts["onboarding_subtitle"].exists)
XCTAssert(app.staticTexts["onboarding_body"].exists)
XCTAssert(app.buttons["main_button"].exists)
XCTAssert(!app.buttons["secondary_button"].exists)
XCTAssert(app.buttons["help_button"].exists)
app.buttons["main_button"].tap()
XCTAssert(app.staticTexts["onboarding_title"].exists)
XCTAssert(app.staticTexts["onboarding_subtitle"].exists)
XCTAssert(app.staticTexts["onboarding_body"].exists)
XCTAssert(app.buttons["main_button"].exists)
XCTAssert(app.buttons["secondary_button"].exists)
XCTAssert(app.buttons["tertiary_button"].exists)
XCTAssert(app.buttons["help_button"].exists)
app.buttons["main_button"].tap()
XCTAssert(app.staticTexts["onboarding_title"].exists)
XCTAssert(app.staticTexts["onboarding_subtitle"].exists)
XCTAssert(app.staticTexts["onboarding_body"].exists)
XCTAssert(app.buttons["main_button"].exists)
XCTAssert(!app.buttons["secondary_button"].exists)
app.buttons["main_button"].tap()
XCTAssert(app.staticTexts["onboarding_title"].exists)
XCTAssert(app.staticTexts["onboarding_subtitle"].exists)
XCTAssert(app.staticTexts["onboarding_body"].exists)
XCTAssert(app.buttons["main_button"].exists)
XCTAssert(!app.buttons["secondary_button"].exists)
}
}
10 changes: 10 additions & 0 deletions Notification Agent Onboarding/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

func applicationDidFinishLaunching(_ aNotification: Notification) {
// Intercept the command+q shortcut and modify the exit value to reflect a manual user dismission.
NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in
switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {
case [.command] where event.characters == "q":
Utils.applicationExit(withReason: .userDismissedOnboarding)
default:
return event
}
return event
}
configureApp()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ final class OnboardingInteractiveEFCLController: InteractiveEFCLController {
value.removeLast()
}
switch argument {
case "percent", "top_message", "bottom_message", "user_interaction_enabled", "user_interruption_allowed", "exit_on_completion":
case "percent", "top_message", "bottom_message", "user_interaction_enabled", "user_interruption_allowed", "exit_on_completion", "end":
NotificationCenter.default.post(name: Notification.Name("progressbar_interactive_updates"), object: nil, userInfo: ["data" : inputData])
default:
continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import Foundation
import Cocoa
import SwiftUI

extension NotificationDispatch {
/// Handle the received notification and send the notification object to the correct controller.
Expand All @@ -17,23 +18,42 @@ extension NotificationDispatch {
func receivedNotification(_ notification: Notification) {
guard let object = notification.userInfo?["object"] as? NotificationObject else { return }
guard let onboardingData = object.payload else { return }
let onboardingViewController = OnboardingViewController(with: onboardingData, alwaysOnTop: object.alwaysOnTop ?? false)
let window = NSWindow(contentViewController: onboardingViewController)
window.styleMask.remove(.resizable)
if object.payload?.progressBarPayload != nil {
window.styleMask.remove(.closable)
window.styleMask.remove(.miniaturizable)
} else if object.hideTitleBarButtons ?? false {
window.styleMask.remove(.closable)
window.styleMask.remove(.miniaturizable)
}
if object.forceLightMode ?? false {
window.appearance = NSAppearance(named: .aqua)
DispatchQueue.main.async {
var mainWindow = NSWindow()
mainWindow = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 812, height: 600), styleMask: .titled, backing: .buffered, defer: false)
guard let viewModel = OnboardingViewModel(onboardingData, window: mainWindow, position: object.position, timeout: object.timeout) else { return }
mainWindow.delegate = viewModel
let contentView = OnboardingView(viewModel: viewModel)
mainWindow.contentView = NSHostingView(rootView: contentView)
mainWindow.setWindowPosition(object.position ?? .center)
mainWindow.styleMask.remove(.resizable)
if object.payload?.progressBarPayload != nil {
mainWindow.styleMask.remove(.closable)
mainWindow.styleMask.remove(.miniaturizable)
} else if object.hideTitleBarButtons ?? false {
mainWindow.styleMask.remove(.closable)
mainWindow.styleMask.remove(.miniaturizable)
}
mainWindow.canBecomeVisibleWithoutLogin = true

if object.forceLightMode ?? false {
NSApp.appearance = NSAppearance(named: .aqua)
}
mainWindow.title = ""
mainWindow.titlebarAppearsTransparent = true

if let backgroundPanelStyle = object.backgroundPanel {
mainWindow.level = .init(Int(CGWindowLevelForKey(.maximumWindow)) + 2)
mainWindow.isMovable = false
mainWindow.collectionBehavior = [.stationary, .canJoinAllSpaces]
Context.main.backgroundPanelsController = BackPanelController(backgroundPanelStyle)
Context.main.backgroundPanelsController?.showBackgroundWindows()
} else {
mainWindow.isMovable = object.isMovable
mainWindow.level = object.alwaysOnTop ?? false ? .floating : .normal
}

mainWindow.makeKeyAndOrderFront(self)
}
window.title = ""
window.titlebarAppearsTransparent = true
window.center()
window.delegate = onboardingViewController
window.makeKeyAndOrderFront(self)
}
}
6 changes: 2 additions & 4 deletions Notification Agent Onboarding/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
Expand All @@ -19,7 +17,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>96</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
Expand All @@ -34,7 +32,7 @@
<true/>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2021 IBM Inc. All rights reserved.</string>
<string>Copyright © 2021 IBM. All rights reserved.</string>
<key>NSMainStoryboardFile</key>
<string>Main</string>
<key>NSPrincipalClass</key>
Expand Down
Loading

0 comments on commit 832719c

Please sign in to comment.