Fix notification focus/read handling and add split surface control
This commit is contained in:
parent
bc074d20c1
commit
5acb4e47b1
6 changed files with 384 additions and 17 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import AppKit
|
||||
import CoreServices
|
||||
import UserNotifications
|
||||
|
||||
final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {
|
||||
|
|
@ -6,6 +7,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
|
||||
weak var tabManager: TabManager?
|
||||
weak var notificationStore: TerminalNotificationStore?
|
||||
private var workspaceObserver: NSObjectProtocol?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
|
@ -13,6 +15,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
}
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
registerLaunchServicesBundle()
|
||||
enforceSingleInstance()
|
||||
observeDuplicateLaunches()
|
||||
configureUserNotifications()
|
||||
}
|
||||
|
||||
|
|
@ -41,6 +46,52 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
center.delegate = self
|
||||
}
|
||||
|
||||
private func registerLaunchServicesBundle() {
|
||||
let bundleURL = Bundle.main.bundleURL.standardizedFileURL
|
||||
let registerStatus = LSRegisterURL(bundleURL as CFURL, true)
|
||||
if registerStatus != noErr {
|
||||
NSLog("LaunchServices registration failed (status: \(registerStatus)) for \(bundleURL.path)")
|
||||
}
|
||||
}
|
||||
|
||||
private func enforceSingleInstance() {
|
||||
guard let bundleId = Bundle.main.bundleIdentifier else { return }
|
||||
let currentPid = ProcessInfo.processInfo.processIdentifier
|
||||
let currentURL = Bundle.main.bundleURL.standardizedFileURL
|
||||
|
||||
for app in NSRunningApplication.runningApplications(withBundleIdentifier: bundleId) {
|
||||
guard app.processIdentifier != currentPid else { continue }
|
||||
if let url = app.bundleURL?.standardizedFileURL, url == currentURL { continue }
|
||||
app.terminate()
|
||||
if !app.isTerminated {
|
||||
_ = app.forceTerminate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func observeDuplicateLaunches() {
|
||||
guard let bundleId = Bundle.main.bundleIdentifier else { return }
|
||||
let currentPid = ProcessInfo.processInfo.processIdentifier
|
||||
let currentURL = Bundle.main.bundleURL.standardizedFileURL
|
||||
|
||||
workspaceObserver = NSWorkspace.shared.notificationCenter.addObserver(
|
||||
forName: NSWorkspace.didLaunchApplicationNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] notification in
|
||||
guard self != nil else { return }
|
||||
guard let app = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication else { return }
|
||||
guard app.bundleIdentifier == bundleId, app.processIdentifier != currentPid else { return }
|
||||
if let url = app.bundleURL?.standardizedFileURL, url == currentURL { return }
|
||||
|
||||
app.terminate()
|
||||
if !app.isTerminated {
|
||||
_ = app.forceTerminate()
|
||||
}
|
||||
NSRunningApplication.current.activate(options: [.activateAllWindows, .activateIgnoringOtherApps])
|
||||
}
|
||||
}
|
||||
|
||||
func userNotificationCenter(
|
||||
_ center: UNUserNotificationCenter,
|
||||
didReceive response: UNNotificationResponse,
|
||||
|
|
@ -55,7 +106,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
willPresent notification: UNNotification,
|
||||
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
|
||||
) {
|
||||
completionHandler([.banner, .sound])
|
||||
completionHandler([.banner, .sound, .list])
|
||||
}
|
||||
|
||||
private func handleNotificationResponse(_ response: UNNotificationResponse) {
|
||||
|
|
@ -73,13 +124,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
switch response.actionIdentifier {
|
||||
case UNNotificationDefaultActionIdentifier, TerminalNotificationStore.actionShowIdentifier:
|
||||
DispatchQueue.main.async {
|
||||
if let notificationId = UUID(uuidString: response.notification.request.identifier) {
|
||||
self.notificationStore?.markRead(id: notificationId)
|
||||
} else if let notificationIdString = response.notification.request.content.userInfo["notificationId"] as? String,
|
||||
let notificationId = UUID(uuidString: notificationIdString) {
|
||||
self.notificationStore?.markRead(id: notificationId)
|
||||
}
|
||||
self.tabManager?.focusTab(tabId, surfaceId: surfaceId)
|
||||
self.markReadIfFocused(response: response, tabId: tabId, surfaceId: surfaceId)
|
||||
}
|
||||
case UNNotificationDismissActionIdentifier:
|
||||
DispatchQueue.main.async {
|
||||
|
|
@ -95,4 +141,28 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
}
|
||||
}
|
||||
|
||||
private func markReadIfFocused(response: UNNotificationResponse, tabId: UUID, surfaceId: UUID?) {
|
||||
let notificationId: UUID? = {
|
||||
if let id = UUID(uuidString: response.notification.request.identifier) {
|
||||
return id
|
||||
}
|
||||
if let idString = response.notification.request.content.userInfo["notificationId"] as? String,
|
||||
let id = UUID(uuidString: idString) {
|
||||
return id
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
|
||||
guard let notificationId else { return }
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
|
||||
guard let tabManager = self.tabManager else { return }
|
||||
guard tabManager.selectedTabId == tabId else { return }
|
||||
if let surfaceId {
|
||||
guard tabManager.focusedSurfaceId(for: tabId) == surfaceId else { return }
|
||||
}
|
||||
self.notificationStore?.markRead(id: notificationId)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue