messageTray: Let the tray decide whether to show a banner

Always use the same code path to add new messages to a source, and
let the `MessageTray` decide whether it shows a banner.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3173>
This commit is contained in:
Julian Sparber 2024-02-12 12:29:35 +01:00 committed by Florian Müllner
parent 940b658071
commit 34f05b075b
13 changed files with 65 additions and 55 deletions

View File

@ -203,7 +203,7 @@ class AutorunDispatcher {
}); });
notification.connect('destroy', () => this._notifications.delete(mount)); notification.connect('destroy', () => this._notifications.delete(mount));
this._notifications.set(mount, notification); this._notifications.set(mount, notification);
source.showNotification(notification); source.addNotification(notification);
} }
addMount(mount, apps, contentTypes) { addMount(mount, apps, contentTypes) {

View File

@ -797,7 +797,7 @@ class NetworkAgent {
delete this._notifications[requestId]; delete this._notifications[requestId];
}); });
source.showNotification(notification); source.addNotification(notification);
} }
_newRequest(agent, requestId, connection, settingName, hints, flags) { _newRequest(agent, requestId, connection, settingName, hints, flags) {

View File

@ -354,7 +354,7 @@ export class ExtensionManager extends Signals.EventEmitter {
_('Extension updates are ready to be installed.')); _('Extension updates are ready to be installed.'));
notification.connect('activated', notification.connect('activated',
() => source.open()); () => source.open());
source.showNotification(notification); source.addNotification(notification);
} }
} }

View File

@ -280,7 +280,7 @@ async function _initializeUI() {
notification.addAction(_('Undo'), notification.addAction(_('Undo'),
() => (global.context.unsafe_mode = false)); () => (global.context.unsafe_mode = false));
notification.setTransient(true); notification.setTransient(true);
source.showNotification(notification); source.addNotification(notification);
}); });
// Provide the bus object for gnome-session to // Provide the bus object for gnome-session to
@ -616,7 +616,7 @@ export function notify(msg, details) {
const source = MessageTray.getSystemSource(); const source = MessageTray.getSystemSource();
let notification = new MessageTray.Notification(source, msg, details); let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true); notification.setTransient(true);
source.showNotification(notification); source.addNotification(notification);
} }
/** /**

View File

@ -590,7 +590,7 @@ export const Source = GObject.registerClass({
'destroy': {param_types: [GObject.TYPE_UINT]}, 'destroy': {param_types: [GObject.TYPE_UINT]},
'notification-added': {param_types: [Notification.$gtype]}, 'notification-added': {param_types: [Notification.$gtype]},
'notification-removed': {param_types: [Notification.$gtype]}, 'notification-removed': {param_types: [Notification.$gtype]},
'notification-show': {param_types: [Notification.$gtype]}, 'notification-request-banner': {param_types: [Notification.$gtype]},
}, },
}, class Source extends MessageList.Source { }, class Source extends MessageList.Source {
constructor(params) { constructor(params) {
@ -647,7 +647,7 @@ export const Source = GObject.registerClass({
this.destroy(); this.destroy();
} }
pushNotification(notification) { addNotification(notification) {
if (this.notifications.includes(notification)) if (this.notifications.includes(notification))
return; return;
@ -655,22 +655,17 @@ export const Source = GObject.registerClass({
this.notifications.shift().destroy(NotificationDestroyedReason.EXPIRED); this.notifications.shift().destroy(NotificationDestroyedReason.EXPIRED);
notification.connect('destroy', this._onNotificationDestroy.bind(this)); notification.connect('destroy', this._onNotificationDestroy.bind(this));
notification.connect('notify::acknowledged', this.countUpdated.bind(this)); notification.connect('notify::acknowledged', () => {
this.countUpdated();
// If acknowledged was set to false try to show the notification again
if (!notification.acknowledged)
this.emit('notification-request-banner', notification);
});
this.notifications.push(notification); this.notifications.push(notification);
this.emit('notification-added', notification); this.emit('notification-added', notification);
this.emit('notification-request-banner', notification);
this.countUpdated();
}
showNotification(notification) {
notification.acknowledged = false;
this.pushNotification(notification);
if (notification.urgency === Urgency.LOW)
return;
if (this.policy.showBanners || notification.urgency === Urgency.CRITICAL)
this.emit('notification-show', notification);
} }
destroy(reason) { destroy(reason) {
@ -876,7 +871,7 @@ export const MessageTray = GObject.registerClass({
this._sources.add(source); this._sources.add(source);
source.connectObject( source.connectObject(
'notification-show', this._onNotificationShow.bind(this), 'notification-request-banner', this._onNotificationRequestBanner.bind(this),
'notification-removed', this._onNotificationRemoved.bind(this), 'notification-removed', this._onNotificationRemoved.bind(this),
'destroy', () => this._removeSource(source), this); 'destroy', () => this._removeSource(source), this);
@ -923,7 +918,17 @@ export const MessageTray = GObject.registerClass({
} }
} }
_onNotificationShow(_source, notification) { _onNotificationRequestBanner(_source, notification) {
// We never display a banner for already acknowledged notifications
if (notification.acknowledged)
return;
if (notification.urgency === Urgency.LOW)
return;
if (!notification.source.policy.showBanners && notification.urgency !== Urgency.CRITICAL)
return;
if (this._notification === notification) { if (this._notification === notification) {
// If a notification that is being shown is updated, we update // If a notification that is being shown is updated, we update
// how it is shown and extend the time until it auto-hides. // how it is shown and extend the time until it auto-hides.

View File

@ -200,6 +200,7 @@ class FdoNotificationDaemon {
const soundFile = 'sound-file' in hints const soundFile = 'sound-file' in hints
? Gio.File.new_for_path(hints['sound-file']) : null; ? Gio.File.new_for_path(hints['sound-file']) : null;
notification.acknowledged = false;
notification.update(summary, body, { notification.update(summary, body, {
gicon, gicon,
bannerMarkup: true, bannerMarkup: true,
@ -355,10 +356,12 @@ class FdoNotificationDaemonSource extends MessageTray.Source {
} }
let tracker = Shell.WindowTracker.get_default(); let tracker = Shell.WindowTracker.get_default();
// Acknowledge notifications that are resident and their app has the
// current focus so that we don't show a banner.
if (notification.resident && this.app && tracker.focus_app === this.app) if (notification.resident && this.app && tracker.focus_app === this.app)
this.pushNotification(notification); notification.acknowledged = true;
else
this.showNotification(notification); this.addNotification(notification);
} }
open() { open() {
@ -402,9 +405,10 @@ const PRIORITY_URGENCY_MAP = {
const GtkNotificationDaemonNotification = GObject.registerClass( const GtkNotificationDaemonNotification = GObject.registerClass(
class GtkNotificationDaemonNotification extends MessageTray.Notification { class GtkNotificationDaemonNotification extends MessageTray.Notification {
_init(source, notification) { _init(source, id, notification) {
super._init(source); super._init(source);
this._serialized = GLib.Variant.new('a{sv}', notification); this._serialized = GLib.Variant.new('a{sv}', notification);
this.id = id;
const { const {
title, title,
@ -499,15 +503,12 @@ class GtkNotificationDaemonAppSource extends MessageTray.Source {
this._notificationPending = false; this._notificationPending = false;
} }
_createNotification(params) {
return new GtkNotificationDaemonNotification(this, params);
}
activateAction(actionId, target) { activateAction(actionId, target) {
const params = target ? GLib.Variant.new('av', [target]) : null; const params = target ? GLib.Variant.new('av', [target]) : null;
this._app.activate_action(actionId, params, 0, -1, null).catch(error => { this._app.activate_action(actionId, params, 0, -1, null).catch(error => {
logError(error, `Failed to activate action for ${this._appId}`); logError(error, `Failed to activate action for ${this._appId}`);
}); });
Main.overview.hide(); Main.overview.hide();
Main.panel.closeCalendar(); Main.panel.closeCalendar();
} }
@ -518,22 +519,18 @@ class GtkNotificationDaemonAppSource extends MessageTray.Source {
Main.panel.closeCalendar(); Main.panel.closeCalendar();
} }
addNotification(notificationId, notificationParams, showBanner) { addNotification(notification) {
this._notificationPending = true; this._notificationPending = true;
if (this._notifications[notificationId]) this._notifications[notification.id]?.destroy(
this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.REPLACED); MessageTray.NotificationDestroyedReason.REPLACED);
let notification = this._createNotification(notificationParams);
notification.connect('destroy', () => { notification.connect('destroy', () => {
delete this._notifications[notificationId]; delete this._notifications[notification.id];
}); });
this._notifications[notificationId] = notification; this._notifications[notification.id] = notification;
if (showBanner) super.addNotification(notification);
this.showNotification(notification);
else
this.pushNotification(notification);
this._notificationPending = false; this._notificationPending = false;
} }
@ -609,8 +606,13 @@ class GtkNotificationDaemon {
throw e; throw e;
} }
notifications.forEach(([notificationId, notification]) => { notifications.forEach(([notificationId, notificationPacked]) => {
source.addNotification(notificationId, notification.deepUnpack(), false); const notification = new GtkNotificationDaemonNotification(source,
notificationId,
notificationPacked.deepUnpack());
// Acknowledge all stored notification so that we don't show a banner again
notification.acknowledged = true;
source.addNotification(notification);
}); });
}); });
} }
@ -635,7 +637,7 @@ class GtkNotificationDaemon {
} }
AddNotificationAsync(params, invocation) { AddNotificationAsync(params, invocation) {
let [appId, notificationId, notification] = params; let [appId, notificationId, notificationSerialized] = params;
let source; let source;
try { try {
@ -651,9 +653,12 @@ class GtkNotificationDaemon {
} }
let timestamp = GLib.DateTime.new_now_local().to_unix(); let timestamp = GLib.DateTime.new_now_local().to_unix();
notification['timestamp'] = new GLib.Variant('x', timestamp); notificationSerialized['timestamp'] = new GLib.Variant('x', timestamp);
source.addNotification(notificationId, notification, true); const notification = new GtkNotificationDaemonNotification(source,
notificationId,
notificationSerialized);
source.addNotification(notification);
invocation.return_value(null); invocation.return_value(null);
} }

View File

@ -50,7 +50,7 @@ class ShellInfo {
if (undoCallback) if (undoCallback)
this._notification.addAction(_('Undo'), () => undoCallback()); this._notification.addAction(_('Undo'), () => undoCallback());
source.showNotification(this._notification); source.addNotification(this._notification);
} }
} }

View File

@ -2122,7 +2122,7 @@ export const ScreenshotUI = GObject.registerClass({
} }
Main.messageTray.add(source); Main.messageTray.add(source);
source.showNotification(notification); source.addNotification(notification);
} }
get screencast_in_progress() { get screencast_in_progress() {
@ -2364,7 +2364,7 @@ function _storeScreenshot(bytes, pixbuf) {
notification.setTransient(true); notification.setTransient(true);
Main.messageTray.add(source); Main.messageTray.add(source);
source.showNotification(notification); source.addNotification(notification);
return file; return file;
} }

View File

@ -200,7 +200,7 @@ export class ShellMountOperation {
this._notification.setTransient(true); this._notification.setTransient(true);
this._notification.iconName = 'media-removable-symbolic'; this._notification.iconName = 'media-removable-symbolic';
this._notification.connect('destroy', () => delete this._notification); this._notification.connect('destroy', () => delete this._notification);
source.showNotification(this._notification); source.addNotification(this._notification);
} }
_showUnmountNotificationDone(message) { _showUnmountNotificationDone(message) {

View File

@ -2039,7 +2039,7 @@ class Indicator extends SystemIndicator {
this._notification.connect('destroy', this._notification.connect('destroy',
() => (this._notification = null)); () => (this._notification = null));
source.showNotification(this._notification); source.addNotification(this._notification);
} }
_syncMainConnection() { _syncMainConnection() {

View File

@ -268,7 +268,7 @@ class Indicator extends SystemIndicator {
const app = Shell.AppSystem.get_default().lookup_app('gnome-thunderbolt-panel.desktop'); const app = Shell.AppSystem.get_default().lookup_app('gnome-thunderbolt-panel.desktop');
app?.activate(); app?.activate();
}); });
source.showNotification(this._notification); source.addNotification(this._notification);
} }
/* Session callbacks */ /* Session callbacks */

View File

@ -45,7 +45,7 @@ export class WindowAttentionHandler {
}); });
notification.setForFeedback(true); notification.setForFeedback(true);
source.showNotification(notification); source.addNotification(notification);
window.connectObject('notify::title', () => { window.connectObject('notify::title', () => {
[title, banner] = this._getTitleAndBanner(app, window); [title, banner] = this._getTitleAndBanner(app, window);

View File

@ -53,12 +53,12 @@ export async function run() {
const source = new MessageTray.getSystemSource(); const source = new MessageTray.getSystemSource();
Scripting.scriptEvent('notificationShowStart'); Scripting.scriptEvent('notificationShowStart');
source.connect('notification-show', source.connect('notification-request-banner',
() => Scripting.scriptEvent('notificationShowDone')); () => Scripting.scriptEvent('notificationShowDone'));
const notification = new MessageTray.Notification(source, const notification = new MessageTray.Notification(source,
'A test notification'); 'A test notification');
source.showNotification(notification); source.addNotification(notification);
await Scripting.sleep(400); await Scripting.sleep(400);
console.debug('Show date menu'); console.debug('Show date menu');