diff --git a/Color Picker.xcodeproj/project.pbxproj b/Color Picker.xcodeproj/project.pbxproj index 40480aa..88080e3 100644 --- a/Color Picker.xcodeproj/project.pbxproj +++ b/Color Picker.xcodeproj/project.pbxproj @@ -21,7 +21,6 @@ E3A3B11D25904E7B001B4D0C /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3A3B11C25904E7B001B4D0C /* App.swift */; }; E3A3B12125904E7D001B4D0C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E3A3B12025904E7D001B4D0C /* Assets.xcassets */; }; E3DFA8C92662514800D2623E /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3DFA8C82662514800D2623E /* Events.swift */; }; - E3E14062259A0D97004FC89F /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = E3E14061259A0D97004FC89F /* Defaults */; }; E3E7D79B27218903009D71F4 /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E3E7D79A27218903009D71F4 /* Intents.framework */; platformFilter = maccatalyst; }; E3E7D79E27218903009D71F4 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E7D79D27218903009D71F4 /* IntentHandler.swift */; }; E3E7D7A227218903009D71F4 /* IntentsExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E3E7D79927218903009D71F4 /* IntentsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -29,7 +28,8 @@ E3E7D7AA27218959009D71F4 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = E3E7D7A827218959009D71F4 /* Intents.intentdefinition */; }; E3E7D7AC27218C03009D71F4 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38D4332263AB24E00701B82 /* Utilities.swift */; }; E3E7D7AE27218C8A009D71F4 /* Regex in Frameworks */ = {isa = PBXBuildFile; productRef = E3E7D7AD27218C8A009D71F4 /* Regex */; }; - E3E9F9AD2642B75100AE6450 /* AppCenterCrashes in Frameworks */ = {isa = PBXBuildFile; productRef = E3E9F9AC2642B75100AE6450 /* AppCenterCrashes */; }; + E3F4BC872788A5780075DC52 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = E3F4BC862788A5780075DC52 /* Sentry */; }; + E3F4BC892788A64F0075DC52 /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = E3F4BC882788A64F0075DC52 /* Defaults */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -85,11 +85,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + E3F4BC872788A5780075DC52 /* Sentry in Frameworks */, E394DAAE263E95D900F5B042 /* LaunchAtLogin in Frameworks */, - E3E9F9AD2642B75100AE6450 /* AppCenterCrashes in Frameworks */, E394DAB2263E965500F5B042 /* KeyboardShortcuts in Frameworks */, E394DAAB263D73AD00F5B042 /* Regex in Frameworks */, - E3E14062259A0D97004FC89F /* Defaults in Frameworks */, + E3F4BC892788A64F0075DC52 /* Defaults in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -193,11 +193,11 @@ ); name = "Color Picker"; packageProductDependencies = ( - E3E14061259A0D97004FC89F /* Defaults */, E394DAAA263D73AD00F5B042 /* Regex */, E394DAAD263E95D900F5B042 /* LaunchAtLogin */, E394DAB1263E965500F5B042 /* KeyboardShortcuts */, - E3E9F9AC2642B75100AE6450 /* AppCenterCrashes */, + E3F4BC862788A5780075DC52 /* Sentry */, + E3F4BC882788A64F0075DC52 /* Defaults */, ); productName = "Color Picker"; productReference = E3A3B11925904E7B001B4D0C /* Color Picker.app */; @@ -254,7 +254,7 @@ E394DAA9263D73AD00F5B042 /* XCRemoteSwiftPackageReference "Regex" */, E394DAAC263E95D900F5B042 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */, E394DAB0263E965500F5B042 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */, - E3E9F9AB2642B75100AE6450 /* XCRemoteSwiftPackageReference "appcenter-sdk-apple" */, + E3F4BC852788A5780075DC52 /* XCRemoteSwiftPackageReference "sentry-cocoa" */, ); productRefGroup = E3A3B11A25904E7B001B4D0C /* Products */; projectDirPath = ""; @@ -653,12 +653,12 @@ minimumVersion = 6.1.0; }; }; - E3E9F9AB2642B75100AE6450 /* XCRemoteSwiftPackageReference "appcenter-sdk-apple" */ = { + E3F4BC852788A5780075DC52 /* XCRemoteSwiftPackageReference "sentry-cocoa" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/microsoft/appcenter-sdk-apple"; + repositoryURL = "https://github.com/getsentry/sentry-cocoa"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 4.3.0; + minimumVersion = 7.8.0; }; }; /* End XCRemoteSwiftPackageReference section */ @@ -679,20 +679,20 @@ package = E394DAB0263E965500F5B042 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */; productName = KeyboardShortcuts; }; - E3E14061259A0D97004FC89F /* Defaults */ = { - isa = XCSwiftPackageProductDependency; - package = E3E14060259A0D97004FC89F /* XCRemoteSwiftPackageReference "Defaults" */; - productName = Defaults; - }; E3E7D7AD27218C8A009D71F4 /* Regex */ = { isa = XCSwiftPackageProductDependency; package = E394DAA9263D73AD00F5B042 /* XCRemoteSwiftPackageReference "Regex" */; productName = Regex; }; - E3E9F9AC2642B75100AE6450 /* AppCenterCrashes */ = { + E3F4BC862788A5780075DC52 /* Sentry */ = { isa = XCSwiftPackageProductDependency; - package = E3E9F9AB2642B75100AE6450 /* XCRemoteSwiftPackageReference "appcenter-sdk-apple" */; - productName = AppCenterCrashes; + package = E3F4BC852788A5780075DC52 /* XCRemoteSwiftPackageReference "sentry-cocoa" */; + productName = Sentry; + }; + E3F4BC882788A64F0075DC52 /* Defaults */ = { + isa = XCSwiftPackageProductDependency; + package = E3E14060259A0D97004FC89F /* XCRemoteSwiftPackageReference "Defaults" */; + productName = Defaults; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Color Picker/App.swift b/Color Picker/App.swift index 52c41cd..bfc4865 100644 --- a/Color Picker/App.swift +++ b/Color Picker/App.swift @@ -117,6 +117,7 @@ struct AppMain: App { } } +@MainActor private final class AppDelegate: NSObject, NSApplicationDelegate { func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { true } diff --git a/Color Picker/AppState.swift b/Color Picker/AppState.swift index b2d6c1f..589ffcb 100644 --- a/Color Picker/AppState.swift +++ b/Color Picker/AppState.swift @@ -1,9 +1,9 @@ import SwiftUI import Combine import Defaults -import AppCenter -import AppCenterCrashes +import Sentry +@MainActor final class AppState: ObservableObject { static let shared = AppState() @@ -42,14 +42,14 @@ final class AppState: ObservableObject { let menu = NSMenu() if Defaults[.menuBarItemClickAction] != .showColorSampler { - menu.addCallbackItem("Pick Color") { [self] _ in + menu.addCallbackItem("Pick Color") { [self] in pickColor() } .setShortcut(for: .pickColor) } if Defaults[.menuBarItemClickAction] != .toggleWindow { - menu.addCallbackItem("Toggle Window") { [self] _ in + menu.addCallbackItem("Toggle Window") { [self] in colorPanel.toggle() } .setShortcut(for: .toggleWindow) @@ -61,7 +61,7 @@ final class AppState: ObservableObject { menu.addHeader("Recently Picked Colors") for color in colors { - let menuItem = menu.addCallbackItem(color.stringRepresentation) { _ in + let menuItem = menu.addCallbackItem(color.stringRepresentation) { color.stringRepresentation.copyToPasteboard() } @@ -75,7 +75,7 @@ final class AppState: ObservableObject { menu.addSeparator() - menu.addCallbackItem("Send Feedback…") { _ in + menu.addCallbackItem("Send Feedback…") { SSApp.openSendFeedbackPage() } @@ -93,10 +93,10 @@ final class AppState: ObservableObject { let item = $0 - $0.button!.onAction { [self] _ in + $0.button!.onAction { [self] in let event = NSApp.currentEvent! - func showMenu() { + let showMenu = { item.menu = createMenu() item.button!.performClick(nil) item.menu = nil @@ -126,12 +126,7 @@ final class AppState: ObservableObject { } init() { - AppCenter.start( - withAppSecret: "f44a0ef2-9271-4bdb-8320-dcceaa857c36", - services: [ - Crashes.self - ] - ) + setUpConfig() DispatchQueue.main.async { [self] in didLaunch() @@ -150,6 +145,15 @@ final class AppState: ObservableObject { #endif } + private func setUpConfig() { + #if !DEBUG + SentrySDK.start { + $0.dsn = "https://e89cb93d693444ee8829f521ab75025a@o844094.ingest.sentry.io/6139060" + $0.enableSwizzling = false + } + #endif + } + private func fixStuff() { // Make the invisible native SwitUI window not block access to the desktop. (macOS 12.0) // https://github.com/feedback-assistant/reports/issues/253 @@ -192,11 +196,8 @@ final class AppState: ObservableObject { } func pickColor() { - NSColorSampler().show { [weak self] in - guard - let self = self, - let color = $0 - else { + Task { + guard let color = await NSColorSampler().sample() else { return } diff --git a/Color Picker/ColorPickerView.swift b/Color Picker/ColorPickerView.swift index c887e26..502aa1e 100644 --- a/Color Picker/ColorPickerView.swift +++ b/Color Picker/ColorPickerView.swift @@ -182,6 +182,9 @@ struct ColorPickerView: View { hexColor.copyToPasteboard() } label: { Image(systemName: "doc.on.doc.fill") + // TODO: Enable when targeting macOS 12 and remove `.fill` in the string. Also on the other buttons. +// .symbolVariant(.fill) +// .symbolRenderingMode(.hierarchical) } .buttonStyle(.borderless) .contentShape(.rectangle) diff --git a/Color Picker/Events.swift b/Color Picker/Events.swift index 91be4a3..768fb6e 100644 --- a/Color Picker/Events.swift +++ b/Color Picker/Events.swift @@ -50,6 +50,7 @@ extension AppState { } // We use this instead of `applicationShouldHandleReopen` because of the macOS bug. + // https://github.com/feedback-assistant/reports/issues/246 NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification) .sink { [self] _ in handleAppReopen() diff --git a/Color Picker/InternetAccessPolicy.json b/Color Picker/InternetAccessPolicy.json index df33ffb..7ffd5e4 100644 --- a/Color Picker/InternetAccessPolicy.json +++ b/Color Picker/InternetAccessPolicy.json @@ -4,7 +4,7 @@ "Website": "https://sindresorhus.com/system-color-picker", "Connections" : [ { - "Host" : "*.appcenter.ms", + "Host" : "*.sentry.io", "NetworkProtocol" : "TCP", "Port" : "443", "Purpose" : "System Color Picker sends crash reports to this server.", diff --git a/Color Picker/Utilities.swift b/Color Picker/Utilities.swift index 551dec7..1023b3a 100644 --- a/Color Picker/Utilities.swift +++ b/Color Picker/Utilities.swift @@ -78,7 +78,10 @@ typealias XColor = UIColor func delay(seconds: TimeInterval, closure: @escaping () -> Void) { - DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: closure) + Task.detached { + try? await Task.sleep(seconds: seconds) + closure() + } } @@ -114,16 +117,17 @@ enum SSApp { static var isDarkMode: Bool { NSApp?.effectiveAppearance.isDarkMode ?? false } static func quit() { - DispatchQueue.main.async { + Task { @MainActor in NSApp.terminate(nil) } } static func relaunch() { - let configuration = NSWorkspace.OpenConfiguration() - configuration.createsNewApplicationInstance = true + Task { @MainActor in + let configuration = NSWorkspace.OpenConfiguration() + configuration.createsNewApplicationInstance = true - NSWorkspace.shared.openApplication(at: url, configuration: configuration) { _, _ in + _ = try? await NSWorkspace.shared.openApplication(at: url, configuration: configuration) quit() } } @@ -1104,8 +1108,8 @@ struct NativeTextField: NSViewRepresentable { } private func unfocus() { - self.parent.isFocused = false - self.blur() + parent.isFocused = false + blur() } } @@ -1126,7 +1130,7 @@ struct NativeTextField: NSViewRepresentable { return } - // We give the text field time required to transition into a new state. + // The text field needs some time to transition into a new state. DispatchQueue.main.async { [self] in parent.isFocused = textField.isCurrentFirstResponder } @@ -1328,18 +1332,16 @@ protocol ControlActionClosureProtocol: NSObjectProtocol { var action: Selector? { get set } } -private final class ActionTrampoline: NSObject { - let action: (T) -> Void +private final class ActionTrampoline: NSObject { + private let action: () -> Void - init(action: @escaping (T) -> Void) { + init(action: @escaping () -> Void) { self.action = action } @objc - func action(sender: AnyObject) { - // This is safe as it can only be `T`. - // swiftlint:disable:next force_cast - action(sender as! T) + fileprivate func handleAction(_ sender: AnyObject) { + action() } } @@ -1350,15 +1352,15 @@ extension ControlActionClosureProtocol { ``` let button = NSButton(title: "Unicorn", target: nil, action: nil) - button.onAction { sender in - print("Button action: \(sender)") + button.onAction { + print("Button action") } ``` */ - func onAction(_ action: @escaping (Self) -> Void) { + func onAction(_ action: @escaping () -> Void) { let trampoline = ActionTrampoline(action: action) target = trampoline - self.action = #selector(ActionTrampoline.action(sender:)) + self.action = #selector(ActionTrampoline.handleAction) objc_setAssociatedObject(self, &controlActionClosureProtocolAssociatedObjectKey, trampoline, .OBJC_ASSOCIATION_RETAIN) } } @@ -1389,16 +1391,17 @@ final class CallbackMenuItem: NSMenuItem { validateCallback = callback } + private let callback: () -> Void + init( _ title: String, key: String = "", keyModifiers: NSEvent.ModifierFlags? = nil, - data: Any? = nil, isEnabled: Bool = true, isHidden: Bool = false, - callback: @escaping (NSMenuItem) -> Void + action: @escaping () -> Void ) { - self.callback = callback + self.callback = action super.init(title: title, action: #selector(action(_:)), keyEquivalent: key) self.target = self self.isEnabled = isEnabled @@ -1414,11 +1417,9 @@ final class CallbackMenuItem: NSMenuItem { fatalError() // swiftlint:disable:this fatal_error_message } - private let callback: (NSMenuItem) -> Void - @objc - func action(_ sender: NSMenuItem) { - callback(sender) + private func action(_ sender: NSMenuItem) { + callback() } } @@ -1435,20 +1436,18 @@ extension NSMenu { _ title: String, key: String = "", keyModifiers: NSEvent.ModifierFlags? = nil, - data: Any? = nil, isEnabled: Bool = true, isChecked: Bool = false, isHidden: Bool = false, - callback: @escaping (NSMenuItem) -> Void + action: @escaping () -> Void ) -> NSMenuItem { let menuItem = CallbackMenuItem( title, key: key, keyModifiers: keyModifiers, - data: data, isEnabled: isEnabled, isHidden: isHidden, - callback: callback + action: action ) addItem(menuItem) return menuItem @@ -1477,7 +1476,7 @@ extension NSMenu { @discardableResult func addSettingsItem() -> NSMenuItem { - addCallbackItem("Preferences…", key: ",") { _ in + addCallbackItem("Preferences…", key: ",") { SSApp.showSettingsWindow() } } @@ -1486,7 +1485,7 @@ extension NSMenu { func addQuitItem() -> NSMenuItem { addSeparator() - return addCallbackItem("Quit \(SSApp.name)", key: "q") { _ in + return addCallbackItem("Quit \(SSApp.name)", key: "q") { SSApp.quit() } } @@ -2258,8 +2257,8 @@ extension Colors { // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html // swiftlint:disable identifier_name - let κ = 24_389.0 / 27.0; // 29^3 / 3^3 - let ε = 216.0 / 24_389.0; // 6^3 / 29^3 + let κ = 24_389.0 / 27.0 // 29^3 / 3^3 + let ε = 216.0 / 24_389.0 // 6^3 / 29^3 // swiftlint:enable identifier_name // Compute f, starting with the luminance-related term. @@ -2800,3 +2799,10 @@ extension View { ) } } + + +extension Task where Success == Never, Failure == Never { + public static func sleep(seconds: TimeInterval) async throws { + try await sleep(nanoseconds: UInt64(seconds * Double(NSEC_PER_SEC))) + } +} diff --git a/IntentsExtension/IntentHandler.swift b/IntentsExtension/IntentHandler.swift index e9802db..33906fe 100644 --- a/IntentsExtension/IntentHandler.swift +++ b/IntentsExtension/IntentHandler.swift @@ -13,13 +13,13 @@ extension Color_ { image: thumbnail.inImage ) - self.hex = sRGBColor.format(.hex()) - self.hexNumber = sRGBColor.hex as NSNumber - self.hsl = sRGBColor.format(.cssHSL) - self.rgb = sRGBColor.format(.cssRGB) - self.lch = nsColor.format(.cssLCH) - self.hslLegacy = sRGBColor.format(.cssHSLLegacy) - self.rgbLegacy = sRGBColor.format(.cssRGBLegacy) + hex = sRGBColor.format(.hex()) + hexNumber = sRGBColor.hex as NSNumber + hsl = sRGBColor.format(.cssHSL) + rgb = sRGBColor.format(.cssRGB) + lch = nsColor.format(.cssLCH) + hslLegacy = sRGBColor.format(.cssHSLLegacy) + rgbLegacy = sRGBColor.format(.cssRGBLegacy) } }