From c1ff00c01bc2279d3669aae8be48ee583309ac04 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Fri, 2 Feb 2024 15:42:31 +0100 Subject: [PATCH] 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: --- js/ui/components/autorunManager.js | 76 +++++++----------------------- js/ui/components/networkAgent.js | 12 ++--- js/ui/main.js | 6 +-- js/ui/messageTray.js | 14 ------ js/ui/overview.js | 30 ++++-------- js/ui/shellMountOperation.js | 74 +++++++++++------------------ js/ui/status/network.js | 9 +--- js/ui/status/thunderbolt.js | 19 +------- tests/shell/basic.js | 3 +- 9 files changed, 65 insertions(+), 178 deletions(-) diff --git a/js/ui/components/autorunManager.js b/js/ui/components/autorunManager.js index 1ca58510f..d2b69d67e 100644 --- a/js/ui/components/autorunManager.js +++ b/js/ui/components/autorunManager.js @@ -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) diff --git a/js/ui/components/networkAgent.js b/js/ui/components/networkAgent.js index 2a31b3037..9a8e98d34 100644 --- a/js/ui/components/networkAgent.js +++ b/js/ui/components/networkAgent.js @@ -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); } diff --git a/js/ui/main.js b/js/ui/main.js index 23e36aba5..5e991aa66 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -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); diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js index a069f16d4..2d21e98f4 100644 --- a/js/ui/messageTray.js +++ b/js/ui/messageTray.js @@ -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(); - } -}); diff --git a/js/ui/overview.js b/js/ui/overview.js index 2b173a5e1..2d6c2a4ab 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -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); } } diff --git a/js/ui/shellMountOperation.js b/js/ui/shellMountOperation.js index d2ab86758..b02b65b6c 100644 --- a/js/ui/shellMountOperation.js +++ b/js/ui/shellMountOperation.js @@ -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 { diff --git a/js/ui/status/network.js b/js/ui/status/network.js index 4322c6761..fee315cf5 100644 --- a/js/ui/status/network.js +++ b/js/ui/status/network.js @@ -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', diff --git a/js/ui/status/thunderbolt.js b/js/ui/status/thunderbolt.js index 51b6e4ba6..f7cc64b8c 100644 --- a/js/ui/status/thunderbolt.js +++ b/js/ui/status/thunderbolt.js @@ -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; diff --git a/tests/shell/basic.js b/tests/shell/basic.js index 831382932..e5bd8c187 100644 --- a/tests/shell/basic.js +++ b/tests/shell/basic.js @@ -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',