Skip to content

Commit

Permalink
extension/notifications: fix race
Browse files Browse the repository at this point in the history
Between "missing dependencies" and "process exited" notifications.

Create "missing dependencies" notification synchronously.
  • Loading branch information
amezin committed Feb 10, 2024
1 parent 124e288 commit 21deeef
Showing 1 changed file with 100 additions and 88 deletions.
188 changes: 100 additions & 88 deletions ddterm/shell/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,62 +78,21 @@ const Notification = GObject.registerClass({
}
});

export const Notifications = GObject.registerClass({
Properties: {
'gettext-context': GObject.ParamSpec.jsobject(
'gettext-context',
'',
'',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY
),
},
}, class DDTermNotifications extends GObject.Object {
_init(params) {
super._init(params);

this._source = null;
this._version_mismatch_notifications = new Set();
this._missing_dependencies_notifications = new Set();
}

create_source() {
if (this._source)
return this._source;

this._source =
new MessageTray.Source(this.gettext_context.gettext('ddterm'), 'utilities-terminal');

this._source.connect('destroy', () => {
this._source = null;
});

Main.messageTray.add(this._source);
return this._source;
}

show_version_mismatch() {
const banner = this.gettext_context.gettext(
const VersionMismatchNotification = GObject.registerClass({
}, class DDTermVersionMismatchNotification extends Notification {
_init(source, gettext_context) {
const banner = gettext_context.gettext(
'Warning: ddterm version has changed. ' +
'Log out, then log in again to load the updated extension.'
);

const source = this.create_source();
const notification = new Notification(source, source.title, banner);
source.showNotification(notification);

this._version_mismatch_notifications.add(notification);

notification.connect('destroy', () => {
this._version_mismatch_notifications.delete(notification);
});
super._init(source, source.title, banner);
}
});

show_error(message, trace) {
if (this._missing_dependencies_notifications.size > 0)
return;

const source = this.create_source();

const ErrorNotification = GObject.registerClass({
}, class DDTermErrorNotification extends Notification {
_init(source, message, trace, gettext_context) {
if (message instanceof Error || message instanceof GLib.Error)
message = message.message;

Expand All @@ -143,9 +102,7 @@ export const Notifications = GObject.registerClass({
message = `${message}`;

if (!trace?.trim()) {
const notification = new Notification(source, source.title, message);
notification.setUrgency(MessageTray.Urgency.CRITICAL);
source.showNotification(notification);
super._init(source, source.title, message);
return;
}

Expand All @@ -156,70 +113,125 @@ export const Notifications = GObject.registerClass({
GLib.markup_escape_text(trace, -1),
].join('\n');

const notification =
new Notification(source, source.title, markup, { bannerMarkup: true });
super._init(source, source.title, markup, { bannerMarkup: true });

notification.addAction(
this.gettext_context.gettext('Copy to Clipboard'),
this.addAction(
gettext_context.gettext('Copy to Clipboard'),
() => St.Clipboard.get_default().set_text(St.ClipboardType.CLIPBOARD, plain)
);

for (const version_mismatch_notification of this._version_mismatch_notifications)
version_mismatch_notification.setUrgency(MessageTray.Urgency.CRITICAL);

notification.setUrgency(MessageTray.Urgency.CRITICAL);
source.showNotification(notification);
}
});

async show_missing_dependencies(packages, files, app_id) {
const MissingDependenciesNotification = GObject.registerClass({
}, class DDTermMissingDependenciesNotification extends Notification {
_init(source, packages, files, app_id, gettext_context) {
const lines = [
this.gettext_context.gettext('ddterm needs additional packages to run.'),
gettext_context.gettext('ddterm needs additional packages to run.'),
];

if (packages.length > 0) {
lines.push(
this.gettext_context.gettext('Please install the following packages:'),
gettext_context.gettext('Please install the following packages:'),
packages.join(' ')
);
}

if (files.length > 0) {
lines.push(
this.gettext_context.gettext(
gettext_context.gettext(
'Please install packages that provide the following files:'
),
files.join(' ')
);
}

super._init(source, source.title, lines.join('\n'));

if (packages.length === 0)
return;

const cancellable = new Gio.Cancellable();

this.connect('destroy', () => {
cancellable.cancel();
});

find_package_installer(cancellable).then(installer => {
this.addAction(gettext_context.gettext('Install'), () => {
installer(packages, app_id);
});

this.update(this.title, this.bannerBodyText, {});
});
}
});

export const Notifications = GObject.registerClass({
Properties: {
'gettext-context': GObject.ParamSpec.jsobject(
'gettext-context',
'',
'',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY
),
},
}, class DDTermNotifications extends GObject.Object {
_init(params) {
super._init(params);

this._source = null;
}

create_source() {
if (this._source)
return this._source;

this._source =
new MessageTray.Source(this.gettext_context.gettext('ddterm'), 'utilities-terminal');

this._source.connect('destroy', () => {
this._source = null;
});

Main.messageTray.add(this._source);
return this._source;
}

show_version_mismatch() {
const source = this.create_source();
const notification = new Notification(source, source.title, lines.join('\n'));
const notification = new VersionMismatchNotification(source, this.gettext_context);
source.showNotification(notification);
}

if (packages.length > 0) {
const cancellable = new Gio.Cancellable();
const cancel_handler = source.connect('destroy', () => cancellable.cancel());

try {
const installer = await find_package_installer(cancellable);

notification.setForFeedback(true);
notification.addAction(
this.gettext_context.gettext('Install'),
() => installer(packages, app_id)
);
} finally {
source.disconnect(cancel_handler);
}
}
show_error(message, trace) {
const source = this.create_source();

if (source.notifications.some(n => n instanceof MissingDependenciesNotification))
return;

const notification = new ErrorNotification(source, message, trace, this.gettext_context);

source.notifications.filter(n => n instanceof VersionMismatchNotification).forEach(n => {
n.setUrgency(MessageTray.Urgency.CRITICAL);
});

notification.setUrgency(MessageTray.Urgency.CRITICAL);
source.showNotification(notification);
}

this._missing_dependencies_notifications.add(notification);
show_missing_dependencies(packages, files, app_id) {
const source = this.create_source();
const notification = new MissingDependenciesNotification(
source,
packages,
files,
app_id,
this.gettext_context
);

notification.connect('destroy', () => {
this._missing_dependencies_notifications.delete(notification);
});
notification.setUrgency(MessageTray.Urgency.CRITICAL);
notification.setForFeedback(true);
source.showNotification(notification);
}

destroy(reason = MessageTray.NotificationDestroyedReason.SOURCE_CLOSED) {
Expand Down

0 comments on commit 21deeef

Please sign in to comment.