messages: Move message widgets from calendar.js to messageList.js

The widgets `NotificationMessage` and `NotificationSection` in `calendar.js`
aren't used only by the calendar.

Move the two widget to messageList.js since once we add by-source grouping for
messages (which will happen in a future commit) we need a much tighter
coupling between them and the rest of the MessageList. In future the
`NotificationSection` will need to be removed to make expanding of
groups work.

This also removes a circular import of files: `calender.js` imports
`messageTray.js` and it imports `calender.js`.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3429>
This commit is contained in:
Julian Sparber 2024-05-10 10:05:45 +02:00
parent e77b716793
commit 64aadeece3
3 changed files with 146 additions and 148 deletions

View File

@ -7,7 +7,6 @@ import St from 'gi://St';
import * as Main from './main.js'; import * as Main from './main.js';
import * as MessageList from './messageList.js'; import * as MessageList from './messageList.js';
import * as MessageTray from './messageTray.js';
import * as Mpris from './mpris.js'; import * as Mpris from './mpris.js';
import * as PopupMenu from './popupMenu.js'; import * as PopupMenu from './popupMenu.js';
import {ensureActorVisibleInScrollView} from '../misc/animationUtils.js'; import {ensureActorVisibleInScrollView} from '../misc/animationUtils.js';
@ -16,7 +15,6 @@ import {formatDateWithCFormatString} from '../misc/dateUtils.js';
import {loadInterfaceXML} from '../misc/fileUtils.js'; import {loadInterfaceXML} from '../misc/fileUtils.js';
const SHOW_WEEKDATE_KEY = 'show-weekdate'; const SHOW_WEEKDATE_KEY = 'show-weekdate';
const MAX_NOTIFICATION_BUTTONS = 3;
const NC_ = (context, str) => `${context}\u0004${str}`; const NC_ = (context, str) => `${context}\u0004${str}`;
@ -763,149 +761,6 @@ export const Calendar = GObject.registerClass({
} }
}); });
export const NotificationMessage = GObject.registerClass(
class NotificationMessage extends MessageList.Message {
constructor(notification) {
super(notification.source);
this.notification = notification;
this.connect('close', () => {
this._closed = true;
if (this.notification)
this.notification.destroy(MessageTray.NotificationDestroyedReason.DISMISSED);
});
notification.connectObject(
'action-added', (_, action) => this._addAction(action),
'action-removed', (_, action) => this._removeAction(action),
'destroy', () => {
this.notification = null;
if (!this._closed)
this.close();
}, this);
notification.bind_property('title',
this, 'title',
GObject.BindingFlags.SYNC_CREATE);
notification.bind_property('body',
this, 'body',
GObject.BindingFlags.SYNC_CREATE);
notification.bind_property('use-body-markup',
this, 'use-body-markup',
GObject.BindingFlags.SYNC_CREATE);
notification.bind_property('datetime',
this, 'datetime',
GObject.BindingFlags.SYNC_CREATE);
notification.bind_property('gicon',
this, 'icon',
GObject.BindingFlags.SYNC_CREATE);
this._actions = new Map();
this.notification.actions.forEach(action => {
this._addAction(action);
});
}
vfunc_clicked() {
this.notification.activate();
}
canClose() {
return true;
}
_addAction(action) {
if (!this._buttonBox) {
this._buttonBox = new St.BoxLayout({
x_expand: true,
style_class: 'notification-buttons-bin',
});
this.setActionArea(this._buttonBox);
global.focus_manager.add_group(this._buttonBox);
}
if (this._buttonBox.get_n_children() >= MAX_NOTIFICATION_BUTTONS)
return;
const button = new St.Button({
style_class: 'notification-button',
x_expand: true,
label: action.label,
});
button.connect('clicked', () => action.activate());
this._actions.set(action, button);
this._buttonBox.add_child(button);
}
_removeAction(action) {
this._actions.get(action)?.destroy();
this._actions.delete(action);
}
});
const NotificationSection = GObject.registerClass(
class NotificationSection extends MessageList.MessageListSection {
_init() {
super._init();
this._nUrgent = 0;
Main.messageTray.connect('source-added', this._sourceAdded.bind(this));
Main.messageTray.getSources().forEach(source => {
this._sourceAdded(Main.messageTray, source);
});
}
get allowed() {
return Main.sessionMode.hasNotifications &&
!Main.sessionMode.isGreeter;
}
_sourceAdded(tray, source) {
source.connectObject('notification-added',
this._onNotificationAdded.bind(this), this);
}
_onNotificationAdded(source, notification) {
let message = new NotificationMessage(notification);
let isUrgent = notification.urgency === MessageTray.Urgency.CRITICAL;
notification.connectObject(
'destroy', () => {
if (isUrgent)
this._nUrgent--;
},
'notify::datetime', () => {
// The datetime property changes whenever the notification is updated
this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.mapped);
}, this);
if (isUrgent) {
// Keep track of urgent notifications to keep them on top
this._nUrgent++;
} else if (this.mapped) {
// Only acknowledge non-urgent notifications in case it
// has important actions that are inaccessible when not
// shown as banner
notification.acknowledged = true;
}
let index = isUrgent ? 0 : this._nUrgent;
this.addMessageAtIndex(message, index, this.mapped);
}
vfunc_map() {
this._messages.forEach(message => {
if (message.notification.urgency !== MessageTray.Urgency.CRITICAL)
message.notification.acknowledged = true;
});
super.vfunc_map();
}
});
const Placeholder = GObject.registerClass( const Placeholder = GObject.registerClass(
class Placeholder extends St.BoxLayout { class Placeholder extends St.BoxLayout {
_init() { _init() {
@ -1025,7 +880,7 @@ class CalendarMessageList extends St.Widget {
this._mediaSection = new Mpris.MediaSection(); this._mediaSection = new Mpris.MediaSection();
this._addSection(this._mediaSection); this._addSection(this._mediaSection);
this._notificationSection = new NotificationSection(); this._notificationSection = new MessageList.NotificationSection();
this._addSection(this._notificationSection); this._addSection(this._notificationSection);
Main.sessionMode.connect('updated', this._sync.bind(this)); Main.sessionMode.connect('updated', this._sync.bind(this));

View File

@ -14,6 +14,7 @@ import * as MessageTray from './messageTray.js';
import * as Util from '../misc/util.js'; import * as Util from '../misc/util.js';
import {formatTimeSpan} from '../misc/dateUtils.js'; import {formatTimeSpan} from '../misc/dateUtils.js';
const MAX_NOTIFICATION_BUTTONS = 3;
const MESSAGE_ANIMATION_TIME = 100; const MESSAGE_ANIMATION_TIME = 100;
const DEFAULT_EXPAND_LINES = 6; const DEFAULT_EXPAND_LINES = 6;
@ -656,6 +657,88 @@ export const Message = GObject.registerClass({
} }
}); });
export const NotificationMessage = GObject.registerClass(
class NotificationMessage extends Message {
constructor(notification) {
super(notification.source);
this.notification = notification;
this.connect('close', () => {
this._closed = true;
if (this.notification)
this.notification.destroy(MessageTray.NotificationDestroyedReason.DISMISSED);
});
notification.connectObject(
'action-added', (_, action) => this._addAction(action),
'action-removed', (_, action) => this._removeAction(action),
'destroy', () => {
this.notification = null;
if (!this._closed)
this.close();
}, this);
notification.bind_property('title',
this, 'title',
GObject.BindingFlags.SYNC_CREATE);
notification.bind_property('body',
this, 'body',
GObject.BindingFlags.SYNC_CREATE);
notification.bind_property('use-body-markup',
this, 'use-body-markup',
GObject.BindingFlags.SYNC_CREATE);
notification.bind_property('datetime',
this, 'datetime',
GObject.BindingFlags.SYNC_CREATE);
notification.bind_property('gicon',
this, 'icon',
GObject.BindingFlags.SYNC_CREATE);
this._actions = new Map();
this.notification.actions.forEach(action => {
this._addAction(action);
});
}
vfunc_clicked() {
this.notification.activate();
}
canClose() {
return true;
}
_addAction(action) {
if (!this._buttonBox) {
this._buttonBox = new St.BoxLayout({
x_expand: true,
style_class: 'notification-buttons-bin',
});
this.setActionArea(this._buttonBox);
global.focus_manager.add_group(this._buttonBox);
}
if (this._buttonBox.get_n_children() >= MAX_NOTIFICATION_BUTTONS)
return;
const button = new St.Button({
style_class: 'notification-button',
x_expand: true,
label: action.label,
});
button.connect('clicked', () => action.activate());
this._actions.set(action, button);
this._buttonBox.add_child(button);
}
_removeAction(action) {
this._actions.get(action)?.destroy();
this._actions.delete(action);
}
});
export const MessageListSection = GObject.registerClass({ export const MessageListSection = GObject.registerClass({
Properties: { Properties: {
'can-clear': GObject.ParamSpec.boolean( 'can-clear': GObject.ParamSpec.boolean(
@ -864,3 +947,64 @@ export const MessageListSection = GObject.registerClass({
this.visible = this.allowed && this._shouldShow(); this.visible = this.allowed && this._shouldShow();
} }
}); });
export const NotificationSection = GObject.registerClass(
class NotificationSection extends MessageListSection {
_init() {
super._init();
this._nUrgent = 0;
Main.messageTray.connect('source-added', this._sourceAdded.bind(this));
Main.messageTray.getSources().forEach(source => {
this._sourceAdded(Main.messageTray, source);
});
}
get allowed() {
return Main.sessionMode.hasNotifications &&
!Main.sessionMode.isGreeter;
}
_sourceAdded(tray, source) {
source.connectObject('notification-added',
this._onNotificationAdded.bind(this), this);
}
_onNotificationAdded(source, notification) {
let message = new NotificationMessage(notification);
let isUrgent = notification.urgency === MessageTray.Urgency.CRITICAL;
notification.connectObject(
'destroy', () => {
if (isUrgent)
this._nUrgent--;
},
'notify::datetime', () => {
// The datetime property changes whenever the notification is updated
this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.mapped);
}, this);
if (isUrgent) {
// Keep track of urgent notifications to keep them on top
this._nUrgent++;
} else if (this.mapped) {
// Only acknowledge non-urgent notifications in case it
// has important actions that are inaccessible when not
// shown as banner
notification.acknowledged = true;
}
let index = isUrgent ? 0 : this._nUrgent;
this.addMessageAtIndex(message, index, this.mapped);
}
vfunc_map() {
this._messages.forEach(message => {
if (message.notification.urgency !== MessageTray.Urgency.CRITICAL)
message.notification.acknowledged = true;
});
super.vfunc_map();
}
});

View File

@ -6,7 +6,6 @@ import Meta from 'gi://Meta';
import Shell from 'gi://Shell'; import Shell from 'gi://Shell';
import St from 'gi://St'; import St from 'gi://St';
import * as Calendar from './calendar.js';
import * as GnomeSession from '../misc/gnomeSession.js'; import * as GnomeSession from '../misc/gnomeSession.js';
import * as Layout from './layout.js'; import * as Layout from './layout.js';
import * as Main from './main.js'; import * as Main from './main.js';
@ -1119,7 +1118,7 @@ export const MessageTray = GObject.registerClass({
this.idleMonitor.add_user_active_watch(this._onIdleMonitorBecameActive.bind(this)); this.idleMonitor.add_user_active_watch(this._onIdleMonitorBecameActive.bind(this));
} }
this._banner = new Calendar.NotificationMessage(this._notification); this._banner = new MessageList.NotificationMessage(this._notification);
this._banner.can_focus = false; this._banner.can_focus = false;
this._banner._header.expandButton.visible = false; this._banner._header.expandButton.visible = false;
this._banner.add_style_class_name('notification-banner'); this._banner.add_style_class_name('notification-banner');