From 348f2809a1e4cb9360947fe3ec1447104b9e3b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 7 Nov 2024 12:47:50 +0800 Subject: [PATCH] Implement send notification --- .../Views/Dashboard/DashboardView.swift | 17 ++++++---- .../Views/Dashboard/ExtensionStatusView.swift | 5 +-- Library/Network/CommandClient.swift | 14 +------- .../Network/ExtensionPlatformInterface.swift | 25 ++++++++++++++ MacLibrary/ApplicationDelegate.swift | 33 ++++++++++++++++++- SFI/ApplicationDelegate.swift | 33 ++++++++++++++++++- 6 files changed, 102 insertions(+), 25 deletions(-) diff --git a/ApplicationLibrary/Views/Dashboard/DashboardView.swift b/ApplicationLibrary/Views/Dashboard/DashboardView.swift index a3fd932..c7c851a 100644 --- a/ApplicationLibrary/Views/Dashboard/DashboardView.swift +++ b/ApplicationLibrary/Views/Dashboard/DashboardView.swift @@ -138,16 +138,21 @@ public struct DashboardView: View { private func loopShowDeprecateNotes(_ reports: any LibboxDeprecatedNoteIteratorProtocol) { if reports.hasNext() { let report = reports.next()! - NSLog("show next") alert = Alert( title: Text("Deprecated Warning"), message: Text(report.message()), - primaryButton: .cancel(Text("Ok")) { - loopShowDeprecateNotes(reports) - }, - secondaryButton: .default(Text("Documentation")) { + primaryButton: .default(Text("Documentation")) { openURL(URL(string: report.migrationLink)!) - loopShowDeprecateNotes(reports) + Task.detached { + try await Task.sleep(nanoseconds: 300 * MSEC_PER_SEC) + await loopShowDeprecateNotes(reports) + } + }, + secondaryButton: .cancel(Text("Ok")) { + Task.detached { + try await Task.sleep(nanoseconds: 300 * MSEC_PER_SEC) + await loopShowDeprecateNotes(reports) + } } ) } diff --git a/ApplicationLibrary/Views/Dashboard/ExtensionStatusView.swift b/ApplicationLibrary/Views/Dashboard/ExtensionStatusView.swift index e1719ed..3ab21f1 100644 --- a/ApplicationLibrary/Views/Dashboard/ExtensionStatusView.swift +++ b/ApplicationLibrary/Views/Dashboard/ExtensionStatusView.swift @@ -4,7 +4,6 @@ import SwiftUI public struct ExtensionStatusView: View { @Environment(\.scenePhase) private var scenePhase - @Environment(\.openURL) private var openURL @StateObject private var commandClient = CommandClient(.status) @State private var columnCount: Int = 4 @@ -91,9 +90,7 @@ public struct ExtensionStatusView: View { .padding([.top, .leading, .trailing]) } .onAppear { - commandClient.connect { urlString in - openURL(URL(string: urlString)!) - } + commandClient.connect() } .onDisappear { commandClient.disconnect() diff --git a/Library/Network/CommandClient.swift b/Library/Network/CommandClient.swift index d7d358f..bf235f7 100644 --- a/Library/Network/CommandClient.swift +++ b/Library/Network/CommandClient.swift @@ -14,8 +14,6 @@ public class CommandClient: ObservableObject { private let logMaxLines: Int private var commandClient: LibboxCommandClient? private var connectTask: Task? - private var openURLFunc: ((String) -> Void)? - @Published public var isConnected: Bool @Published public var status: LibboxStatusMessage? @Published public var groups: [LibboxOutboundGroup]? @@ -31,15 +29,13 @@ public class CommandClient: ObservableObject { public init(_ connectionType: ConnectionType, logMaxLines: Int = 300) { self.connectionType = connectionType self.logMaxLines = logMaxLines - openURLFunc = nil logList = [] clashModeList = [] clashMode = "" isConnected = false } - public func connect(_ openURLFunc: ((String) -> Void)? = nil) { - self.openURLFunc = openURLFunc + public func connect() { if isConnected { return } @@ -52,7 +48,6 @@ public class CommandClient: ObservableObject { } public func disconnect() { - openURLFunc = nil if let connectTask { connectTask.cancel() self.connectTask = nil @@ -227,13 +222,6 @@ public class CommandClient: ObservableObject { commandClient.connections = connections } } - - func openURL(_ url: String?) { - guard let url else { - return - } - commandClient.openURLFunc?(url) - } } } diff --git a/Library/Network/ExtensionPlatformInterface.swift b/Library/Network/ExtensionPlatformInterface.swift index 8c390a1..2f315b4 100644 --- a/Library/Network/ExtensionPlatformInterface.swift +++ b/Library/Network/ExtensionPlatformInterface.swift @@ -1,6 +1,7 @@ import Foundation import Libbox import NetworkExtension +import UserNotifications #if canImport(CoreWLAN) import CoreWLAN #endif @@ -338,4 +339,28 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc func reset() { networkSettings = nil } + + public func send(_ notification: LibboxNotification?) throws { + #if !os(tvOS) + guard let notification else { + return + } + let center = UNUserNotificationCenter.current() + let content = UNMutableNotificationContent() + + content.title = notification.title + content.subtitle = notification.subtitle + content.body = notification.body + if !notification.openURL.isEmpty { + content.userInfo["OPEN_URL"] = notification.openURL + content.categoryIdentifier = "OPEN_URL" + } + content.interruptionLevel = .active + let request = UNNotificationRequest(identifier: notification.identifier, content: content, trigger: nil) + try runBlocking { + try await center.requestAuthorization(options: [.alert]) + try await center.add(request) + } + #endif + } } diff --git a/MacLibrary/ApplicationDelegate.swift b/MacLibrary/ApplicationDelegate.swift index 9cbe221..078bc0d 100644 --- a/MacLibrary/ApplicationDelegate.swift +++ b/MacLibrary/ApplicationDelegate.swift @@ -3,11 +3,25 @@ import ApplicationLibrary import Foundation import Libbox import Library +import UserNotifications -open class ApplicationDelegate: NSObject, NSApplicationDelegate { +open class ApplicationDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate { public func applicationDidFinishLaunching(_: Notification) { NSLog("Here I stand") LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, false) + let notificationCenter = UNUserNotificationCenter.current() + notificationCenter.setNotificationCategories([ + UNNotificationCategory( + identifier: "OPEN_URL", + actions: [ + UNNotificationAction(identifier: "COPY_URL", title: "Copy URL", options: .foreground, icon: UNNotificationActionIcon(systemImageName: "clipboard.fill")), + UNNotificationAction(identifier: "OPEN_URL", title: "Open", options: .foreground, icon: UNNotificationActionIcon(systemImageName: "safari.fill")), + ], + intentIdentifiers: [] + ), + ] + ) + notificationCenter.delegate = self let event = NSAppleEventManager.shared().currentAppleEvent let launchedAsLogInItem = event?.eventID == kAEOpenApplication && @@ -34,6 +48,23 @@ open class ApplicationDelegate: NSObject, NSApplicationDelegate { } } + public func userNotificationCenter(_: UNUserNotificationCenter, willPresent _: UNNotification) async -> UNNotificationPresentationOptions { + .banner + } + + public func userNotificationCenter(_: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { + if let url = response.notification.request.content.userInfo["OPEN_URL"] as? String { + switch response.actionIdentifier { + case "COPY_URL": + NSPasteboard.general.setString(url, forType: .URL) + case "OPEN_URL": + fallthrough + default: + NSWorkspace.shared.open(URL(string: url)!) + } + } + } + public func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool { SharedPreferences.inDebug || !SharedPreferences.menuBarExtraInBackground.getBlocking() } diff --git a/SFI/ApplicationDelegate.swift b/SFI/ApplicationDelegate.swift index 0120242..6ab06c7 100644 --- a/SFI/ApplicationDelegate.swift +++ b/SFI/ApplicationDelegate.swift @@ -4,17 +4,48 @@ import Libbox import Library import Network import UIKit +import UserNotifications -class ApplicationDelegate: NSObject, UIApplicationDelegate { +class ApplicationDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate { private var profileServer: ProfileServer? func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { NSLog("Here I stand") LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, false) + let notificationCenter = UNUserNotificationCenter.current() + notificationCenter.setNotificationCategories([ + UNNotificationCategory( + identifier: "OPEN_URL", + actions: [ + UNNotificationAction(identifier: "COPY_URL", title: "Copy URL", options: .foreground, icon: UNNotificationActionIcon(systemImageName: "clipboard.fill")), + UNNotificationAction(identifier: "OPEN_URL", title: "Open", options: .foreground, icon: UNNotificationActionIcon(systemImageName: "safari.fill")), + ], + intentIdentifiers: [] + ), + ] + ) + notificationCenter.delegate = self setup() return true } + func userNotificationCenter(_: UNUserNotificationCenter, willPresent _: UNNotification) async -> UNNotificationPresentationOptions { + .banner + } + + func userNotificationCenter(_: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { + if let url = response.notification.request.content.userInfo["OPEN_URL"] as? String { + switch response.actionIdentifier { + case "COPY_URL": + UIPasteboard.general.string = url + case "OPEN_URL": + fallthrough + default: + await UIApplication.shared.open(URL(string: url)!) + } + } + } + private func setup() { do { try UIProfileUpdateTask.configure()