
Braces are optional for single-line arrow functions, but there's a subtle difference: Without braces, the expression is implicitly used as return value; with braces, the function returns nothing unless there's an explicit return. We currently reflect that in our style by only omitting braces when the function is expected to have a return value, but that's not very obvious, not an important differentiation to make, and not easy to express in an automatic rule. So just omit braces consistently as mandated by gjs' coding style. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/608
154 lines
5.4 KiB
JavaScript
154 lines
5.4 KiB
JavaScript
const { Clutter, Gio, GLib, GObject, Shell } = imports.gi;
|
|
|
|
const CheckBox = imports.ui.checkBox;
|
|
const Dialog = imports.ui.dialog;
|
|
const ModalDialog = imports.ui.modalDialog;
|
|
|
|
const { loadInterfaceXML } = imports.misc.fileUtils;
|
|
|
|
const RequestIface = loadInterfaceXML('org.freedesktop.impl.portal.Request');
|
|
const AccessIface = loadInterfaceXML('org.freedesktop.impl.portal.Access');
|
|
|
|
var DialogResponse = {
|
|
OK: 0,
|
|
CANCEL: 1,
|
|
CLOSED: 2
|
|
};
|
|
|
|
var AccessDialog = GObject.registerClass(
|
|
class AccessDialog extends ModalDialog.ModalDialog {
|
|
_init(invocation, handle, title, subtitle, body, options) {
|
|
super._init({ styleClass: 'access-dialog' });
|
|
|
|
this._invocation = invocation;
|
|
this._handle = handle;
|
|
|
|
this._requestExported = false;
|
|
this._request = Gio.DBusExportedObject.wrapJSObject(RequestIface, this);
|
|
|
|
for (let option in options)
|
|
options[option] = options[option].deep_unpack();
|
|
|
|
this._buildLayout(title, subtitle, body, options);
|
|
}
|
|
|
|
_buildLayout(title, subtitle, body, options) {
|
|
// No support for non-modal system dialogs, so ignore the option
|
|
//let modal = options['modal'] || true;
|
|
let denyLabel = options['deny_label'] || _("Deny Access");
|
|
let grantLabel = options['grant_label'] || _("Grant Access");
|
|
let iconName = options['icon'] || null;
|
|
let choices = options['choices'] || [];
|
|
|
|
let contentParams = { title, subtitle, body };
|
|
if (iconName)
|
|
contentParams.icon = new Gio.ThemedIcon({ name: iconName });
|
|
let content = new Dialog.MessageDialogContent(contentParams);
|
|
this.contentLayout.add_actor(content);
|
|
|
|
this._choices = new Map();
|
|
|
|
for (let i = 0; i < choices.length; i++) {
|
|
let [id, name, opts, selected] = choices[i];
|
|
if (opts.length > 0)
|
|
continue; // radio buttons, not implemented
|
|
|
|
let check = new CheckBox.CheckBox();
|
|
check.getLabelActor().text = name;
|
|
check.actor.checked = selected == "true";
|
|
content.insertBeforeBody(check.actor);
|
|
|
|
this._choices.set(id, check);
|
|
}
|
|
|
|
this.addButton({ label: denyLabel,
|
|
action: () => {
|
|
this._sendResponse(DialogResponse.CANCEL);
|
|
},
|
|
key: Clutter.KEY_Escape });
|
|
this.addButton({ label: grantLabel,
|
|
action: () => {
|
|
this._sendResponse(DialogResponse.OK);
|
|
} });
|
|
}
|
|
|
|
open() {
|
|
super.open();
|
|
|
|
let connection = this._invocation.get_connection();
|
|
this._requestExported = this._request.export(connection, this._handle);
|
|
}
|
|
|
|
CloseAsync(invocation, params) {
|
|
if (this._invocation.get_sender() != invocation.get_sender()) {
|
|
invocation.return_error_literal(Gio.DBusError,
|
|
Gio.DBusError.ACCESS_DENIED,
|
|
'');
|
|
return;
|
|
}
|
|
|
|
this._sendResponse(DialogResponse.CLOSED);
|
|
}
|
|
|
|
_sendResponse(response) {
|
|
if (this._requestExported)
|
|
this._request.unexport();
|
|
this._requestExported = false;
|
|
|
|
let results = {};
|
|
if (response == DialogResponse.OK) {
|
|
for (let [id, check] of this._choices) {
|
|
let checked = check.actor.checked ? 'true' : 'false';
|
|
results[id] = new GLib.Variant('s', checked);
|
|
}
|
|
}
|
|
|
|
// Delay actual response until the end of the close animation (if any)
|
|
this.connect('closed', () => {
|
|
this._invocation.return_value(new GLib.Variant('(ua{sv})',
|
|
[response, results]));
|
|
});
|
|
this.close();
|
|
}
|
|
});
|
|
|
|
var AccessDialogDBus = class {
|
|
constructor() {
|
|
this._accessDialog = null;
|
|
|
|
this._windowTracker = Shell.WindowTracker.get_default();
|
|
|
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AccessIface, this);
|
|
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/portal/desktop');
|
|
|
|
Gio.DBus.session.own_name('org.freedesktop.impl.portal.desktop.gnome', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
|
}
|
|
|
|
AccessDialogAsync(params, invocation) {
|
|
if (this._accessDialog) {
|
|
invocation.return_error_literal(Gio.DBusError,
|
|
Gio.DBusError.LIMITS_EXCEEDED,
|
|
'Already showing a system access dialog');
|
|
return;
|
|
}
|
|
|
|
let [handle, appId, parentWindow, title, subtitle, body, options] = params;
|
|
// We probably want to use parentWindow and global.display.focus_window
|
|
// for this check in the future
|
|
if (appId && appId + '.desktop' != this._windowTracker.focus_app.id) {
|
|
invocation.return_error_literal(Gio.DBusError,
|
|
Gio.DBusError.ACCESS_DENIED,
|
|
'Only the focused app is allowed to show a system access dialog');
|
|
return;
|
|
}
|
|
|
|
let dialog = new AccessDialog(invocation, handle, title,
|
|
subtitle, body, options);
|
|
dialog.open();
|
|
|
|
dialog.connect('closed', () => this._accessDialog = null);
|
|
|
|
this._accessDialog = dialog;
|
|
}
|
|
};
|