notification: Use the same Source for showing system notifications

This drops all subclasses of `MessageTray.Source` that were used to
display system notifications. Now the `Source` returned from
`MessageTray.getSystemSource()` is used.
Ensure also that properties and methods that where set on the `Source`
are moved to the `Notification` object itself.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3156>
This commit is contained in:
Julian Sparber 2024-02-02 15:42:31 +01:00 committed by Marge Bot
parent 3faf1caead
commit c1ff00c01b
9 changed files with 65 additions and 178 deletions

View File

@ -6,7 +6,6 @@ import GObject from 'gi://GObject';
import St from 'gi://St';
import * as GnomeSession from '../../misc/gnomeSession.js';
import * as Main from '../main.js';
import * as MessageTray from '../messageTray.js';
Gio._promisify(Gio.Mount.prototype, 'guess_content_type');
@ -165,7 +164,7 @@ class AutorunManager {
class AutorunDispatcher {
constructor(manager) {
this._manager = manager;
this._sources = [];
this._notifications = new Map();
this._settings = new Gio.Settings({schema_id: SETTINGS_SCHEMA});
}
@ -185,26 +184,16 @@ class AutorunDispatcher {
return AutorunSetting.ASK;
}
_getSourceForMount(mount) {
let filtered = this._sources.filter(source => source.mount === mount);
// we always make sure not to add two sources for the same
// mount in addMount(), so it's safe to assume filtered.length
// is always either 1 or 0.
if (filtered.length === 1)
return filtered[0];
return null;
}
_addSource(mount, apps) {
// if we already have a source showing for this
// mount, return
if (this._getSourceForMount(mount))
_addNotification(mount, apps) {
// Only show a new notification if there isn't already an existing one
if (this._notifications.has(mount))
return;
// add a new source
this._sources.push(new AutorunSource(this._manager, mount, apps));
const source = MessageTray.getSystemSource();
const notification = new AutorunNotification(source, mount, apps);
notification.connect('destroy', () => this._notifications.delete(mount));
this._notifications.set(mount, notification);
source.showNotification(notification);
}
addMount(mount, apps, contentTypes) {
@ -241,58 +230,29 @@ class AutorunDispatcher {
// we fallback here also in case the settings did not specify 'ask',
// but we failed launching the default app or the default file manager
if (!success)
this._addSource(mount, apps);
this._addNotification(mount, apps);
}
removeMount(mount) {
let source = this._getSourceForMount(mount);
// if we aren't tracking this mount, don't do anything
if (!source)
return;
// destroy the notification source
source.destroy();
this._notifications.get(mount)?.destroy();
}
}
const AutorunSource = GObject.registerClass(
class AutorunSource extends MessageTray.Source {
constructor(manager, mount, apps) {
super({
title: mount.get_name(),
icon: mount.get_icon(),
});
this._manager = manager;
this.mount = mount;
this.apps = apps;
this._notification = new AutorunNotification(this._manager, this);
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.showNotification(this._notification);
}
_createPolicy() {
return new MessageTray.NotificationApplicationPolicy('org.gnome.Nautilus');
}
});
const AutorunNotification = GObject.registerClass(
class AutorunNotification extends MessageTray.Notification {
_init(manager, source) {
super._init(source, source.title);
constructor(source, mount, apps) {
super(source, mount.get_name());
this._manager = manager;
this._mount = source.mount;
this.gicon = mount.get_icon();
this._mount = mount;
this._apps = apps;
}
createBanner() {
let banner = new MessageTray.NotificationBanner(this);
this.source.apps.forEach(app => {
this._apps.forEach(app => {
let actor = this._buttonForApp(app);
if (actor)

View File

@ -11,7 +11,6 @@ import St from 'gi://St';
import * as Signals from '../../misc/signals.js';
import * as Dialog from '../dialog.js';
import * as Main from '../main.js';
import * as MessageTray from '../messageTray.js';
import * as ModalDialog from '../modalDialog.js';
import * as ShellEntry from '../shellEntry.js';
@ -740,12 +739,6 @@ class NetworkAgent {
}
_showNotification(requestId, connection, settingName, hints, flags) {
const source = new MessageTray.Source({
title: _('Network Manager'),
iconName: 'network-transmit-receive',
});
source.policy = new MessageTray.NotificationApplicationPolicy('gnome-network-panel');
let title, body;
let connectionSetting = connection.get_setting_connection();
@ -788,7 +781,9 @@ class NetworkAgent {
return;
}
let notification = new MessageTray.Notification(source, title, body);
const source = new MessageTray.getSystemSource();
const notification = new MessageTray.Notification(source, title, body);
notification.iconName = 'dialog-password-symbolic';
notification.connect('activated', () => {
notification.answered = true;
@ -802,7 +797,6 @@ class NetworkAgent {
delete this._notifications[requestId];
});
Main.messageTray.add(source);
source.showNotification(notification);
}

View File

@ -274,8 +274,7 @@ async function _initializeUI() {
if (lookingGlass?.isOpen)
return; // assume user action
const source = new MessageTray.SystemNotificationSource();
messageTray.add(source);
const source = MessageTray.getSystemSource();
const notification = new MessageTray.Notification(source,
_('System was put in unsafe mode'),
_('Apps now have unrestricted access'));
@ -615,8 +614,7 @@ export function loadTheme() {
* @param {string} details Additional information
*/
export function notify(msg, details) {
let source = new MessageTray.SystemNotificationSource();
messageTray.add(source);
const source = MessageTray.getSystemSource();
let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true);
source.showNotification(notification);

View File

@ -1348,17 +1348,3 @@ export function getSystemSource() {
return systemNotificationSource;
}
export const SystemNotificationSource = GObject.registerClass(
class SystemNotificationSource extends Source {
constructor() {
super({
title: _('System Information'),
iconName: 'dialog-information-symbolic',
});
}
open() {
this.destroy();
}
});

View File

@ -28,41 +28,29 @@ const DND_WINDOW_SWITCH_TIMEOUT = 750;
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
class ShellInfo {
constructor() {
this._source = null;
}
setMessage(text, options) {
options = Params.parse(options, {
undoCallback: null,
forFeedback: false,
});
const source = MessageTray.getSystemSource();
let undoCallback = options.undoCallback;
let forFeedback = options.forFeedback;
if (this._source == null) {
this._source = new MessageTray.SystemNotificationSource();
this._source.connect('destroy', () => {
this._source = null;
});
Main.messageTray.add(this._source);
}
let notification = null;
if (this._source.notifications.length === 0) {
notification = new MessageTray.Notification(this._source, text, null);
notification.setTransient(true);
notification.setForFeedback(forFeedback);
if (!this._notification) {
this._notification = new MessageTray.Notification(source, text, null);
this._notification.setTransient(true);
this._notification.setForFeedback(forFeedback);
this._notification.connect('destroy', () => delete this._notification);
} else {
notification = this._source.notifications[0];
notification.update(text, null, {clear: true});
this._notification.update(text, null, {clear: true});
}
if (undoCallback)
notification.addAction(_('Undo'), () => undoCallback());
this._notification.addAction(_('Undo'), () => undoCallback());
this._source.showNotification(notification);
source.showNotification(this._notification);
}
}

View File

@ -11,7 +11,6 @@ import St from 'gi://St';
import * as Animation from './animation.js';
import * as CheckBox from './checkBox.js';
import * as Dialog from './dialog.js';
import * as Main from './main.js';
import * as MessageTray from './messageTray.js';
import * as ModalDialog from './modalDialog.js';
import * as Params from '../misc/params.js';
@ -181,61 +180,44 @@ export class ShellMountOperation {
}
_onShowUnmountProgress(op, message, timeLeft, bytesLeft) {
if (!this._notifier)
this._notifier = new ShellUnmountNotifier();
if (bytesLeft === 0)
this._notifier.done(message);
this._showUnmountNotificationDone(message);
else
this._notifier.show(message);
this._showUnmountNotification(message);
}
borrowDialog() {
this._dialog?.disconnectObject(this);
return this._dialog;
}
_createNotification(title, body) {
this._notification?.destroy();
const source = MessageTray.getSystemSource();
this._notification = new MessageTray.Notification(source, title, body);
this._notification.setTransient(true);
this._notification.iconName = 'media-removable-symbolic';
this._notification.connect('destroy', () => delete this._notification);
source.showNotification(this._notification);
}
_showUnmountNotificationDone(message) {
if (message)
this._createNotification(message, null);
}
_showUnmountNotification(message) {
const [title, body] = message.split('\n', 2);
if (!this._notification)
this._createNotification(title, body);
else
this._notification.update(title, body);
}
}
const ShellUnmountNotifier = GObject.registerClass(
class ShellUnmountNotifier extends MessageTray.Source {
constructor() {
super({
iconName: 'media-removable',
});
this._notification = null;
Main.messageTray.add(this);
}
show(message) {
let [header, text] = message.split('\n', 2);
if (!this._notification) {
this._notification = new MessageTray.Notification(this, header, text);
this._notification.setTransient(true);
this._notification.setUrgency(MessageTray.Urgency.CRITICAL);
} else {
this._notification.update(header, text);
}
this.showNotification(this._notification);
}
done(message) {
if (this._notification) {
this._notification.destroy();
this._notification = null;
}
if (message) {
let notification = new MessageTray.Notification(this, message, null);
notification.setTransient(true);
this.showNotification(notification);
}
}
});
const ShellMountQuestionDialog = GObject.registerClass({
Signals: {'response': {param_types: [GObject.TYPE_INT]}},
}, class ShellMountQuestionDialog extends ModalDialog.ModalDialog {

View File

@ -2036,16 +2036,11 @@ class Indicator extends SystemIndicator {
_onActivationFailed() {
this._notification?.destroy();
const source = new MessageTray.Source({
title: _('Network Manager'),
iconName: 'network-error-symbolic',
});
source.policy =
new MessageTray.NotificationApplicationPolicy('gnome-network-panel');
const source = MessageTray.getSystemSource();
this._notification = new MessageTray.Notification(source,
_('Connection failed'),
_('Activation of network connection failed'));
this._notification.iconName = 'network-error-symbolic';
this._notification.setUrgency(MessageTray.Urgency.HIGH);
this._notification.setTransient(true);
this._notification.connect('destroy',

View File

@ -236,7 +236,6 @@ class Indicator extends SystemIndicator {
Main.sessionMode.connect('updated', this._sync.bind(this));
this._sync();
this._source = null;
this._perm = null;
this._createPermission();
}
@ -254,27 +253,13 @@ class Indicator extends SystemIndicator {
this._client.close();
}
_ensureSource() {
if (!this._source) {
this._source = new MessageTray.Source({
title: _('Thunderbolt'),
iconName: 'thunderbolt-symbolic',
});
this._source.connect('destroy', () => (this._source = null));
Main.messageTray.add(this._source);
}
return this._source;
}
_notify(title, body) {
if (this._notification)
this._notification.destroy();
let source = this._ensureSource();
const source = MessageTray.getSystemSource();
this._notification = new MessageTray.Notification(source, title, body);
this._notification.iconName = 'thunderbolt-symbolic';
this._notification.setUrgency(MessageTray.Urgency.HIGH);
this._notification.connect('destroy', () => {
this._notification = null;

View File

@ -50,8 +50,7 @@ export async function run() {
// notification
console.debug('Show notification message');
const source = new MessageTray.SystemNotificationSource();
Main.messageTray.add(source);
const source = new MessageTray.getSystemSource();
Scripting.scriptEvent('notificationShowStart');
source.connect('notification-show',