macos: dismiss notifications on focus, application exit
I've only recently been using programs that use user notifications heavily
and this commit addresses a number of annoyances I've encountered.
1. Notifications dispatched while the source terminal surface is
focused are now only shown for a short time (3 seconds hardcoded)
and then automatically dismiss.
2. Notifications are dismissed when the target surface becomes focused
from an unfocused state. This dismissal happens immediately (no
delay).
3. Notifications are dismissed when the application exits.
4. This fixes a bug where notification callbacks were modifying view
state, but the notification center doesn't guarantee that the
callback is called on the main thread. We now ensure that
the callback is always called on the main thread.
pull/7531/head
parent
08101b0bc5
commit
70f030e3c2
|
|
@ -316,6 +316,13 @@ class AppDelegate: NSObject,
|
|||
}
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ notification: Notification) {
|
||||
// We have no notifications we want to persist after death,
|
||||
// so remove them all now. In the future we may want to be
|
||||
// more selective and only remove surface-targeted notifications.
|
||||
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
|
||||
}
|
||||
|
||||
/// This is called when the application is already open and someone double-clicks the icon
|
||||
/// or clicks the dock icon.
|
||||
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
||||
|
|
|
|||
|
|
@ -306,6 +306,14 @@ extension Ghostty {
|
|||
|
||||
// We unset our bell state if we gained focus
|
||||
bell = false
|
||||
|
||||
// Remove any notifications for this surface once we gain focus.
|
||||
if !notificationIdentifiers.isEmpty {
|
||||
UNUserNotificationCenter.current()
|
||||
.removeDeliveredNotifications(
|
||||
withIdentifiers: Array(notificationIdentifiers))
|
||||
self.notificationIdentifiers = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1388,13 +1396,29 @@ extension Ghostty {
|
|||
trigger: nil
|
||||
)
|
||||
|
||||
UNUserNotificationCenter.current().add(request) { error in
|
||||
// Note the callback may be executed on a background thread as documented
|
||||
// so we need @MainActor since we're reading/writing view state.
|
||||
UNUserNotificationCenter.current().add(request) { @MainActor error in
|
||||
if let error = error {
|
||||
AppDelegate.logger.error("Error scheduling user notification: \(error)")
|
||||
return
|
||||
}
|
||||
|
||||
// We need to keep track of this notification so we can remove it
|
||||
// under certain circumstances
|
||||
self.notificationIdentifiers.insert(uuid)
|
||||
|
||||
// If we're focused then we schedule to remove the notification
|
||||
// after a few seconds. If we gain focus we automatically remove it
|
||||
// in focusDidChange.
|
||||
if (self.focused) {
|
||||
Task { @MainActor [weak self] in
|
||||
try await Task.sleep(for: .seconds(3))
|
||||
self?.notificationIdentifiers.remove(uuid)
|
||||
UNUserNotificationCenter.current()
|
||||
.removeDeliveredNotifications(withIdentifiers: [uuid])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue