253 lines
6.8 KiB
JavaScript
Executable File
253 lines
6.8 KiB
JavaScript
Executable File
// SPDX-FileCopyrightText: GSConnect Developers https://github.com/GSConnect
|
|
//
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
import GLib from 'gi://GLib';
|
|
import Gio from 'gi://Gio';
|
|
import GObject from 'gi://GObject';
|
|
import Gtk from 'gi://Gtk';
|
|
|
|
import system from 'system';
|
|
|
|
import Config from '../../config.js';
|
|
|
|
|
|
/*
|
|
* Issue Header
|
|
*/
|
|
const ISSUE_HEADER = `
|
|
GSConnect: ${Config.PACKAGE_VERSION} (${Config.IS_USER ? 'user' : 'system'})
|
|
GJS: ${system.version}
|
|
Session: ${GLib.getenv('XDG_SESSION_TYPE')}
|
|
OS: ${GLib.get_os_info('PRETTY_NAME')}
|
|
`;
|
|
|
|
|
|
/**
|
|
* A dialog for selecting a device
|
|
*/
|
|
export const DeviceChooser = GObject.registerClass({
|
|
GTypeName: 'GSConnectServiceDeviceChooser',
|
|
Properties: {
|
|
'action-name': GObject.ParamSpec.string(
|
|
'action-name',
|
|
'Action Name',
|
|
'The name of the associated action, like "sendFile"',
|
|
GObject.ParamFlags.READWRITE,
|
|
null
|
|
),
|
|
'action-target': GObject.param_spec_variant(
|
|
'action-target',
|
|
'Action Target',
|
|
'The parameter for action invocations',
|
|
new GLib.VariantType('*'),
|
|
null,
|
|
GObject.ParamFlags.READWRITE
|
|
),
|
|
},
|
|
Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/service-device-chooser.ui',
|
|
Children: ['device-list', 'cancel-button', 'select-button'],
|
|
}, class DeviceChooser extends Gtk.Dialog {
|
|
|
|
_init(params = {}) {
|
|
super._init({
|
|
use_header_bar: true,
|
|
application: Gio.Application.get_default(),
|
|
});
|
|
this.set_keep_above(true);
|
|
|
|
// HeaderBar
|
|
this.get_header_bar().subtitle = params.title;
|
|
|
|
// Dialog Action
|
|
this.action_name = params.action_name;
|
|
this.action_target = params.action_target;
|
|
|
|
// Device List
|
|
this.device_list.set_sort_func(this._sortDevices);
|
|
|
|
this._devicesChangedId = this.application.settings.connect(
|
|
'changed::devices',
|
|
this._onDevicesChanged.bind(this)
|
|
);
|
|
this._onDevicesChanged();
|
|
}
|
|
|
|
vfunc_response(response_id) {
|
|
if (response_id === Gtk.ResponseType.OK) {
|
|
try {
|
|
const device = this.device_list.get_selected_row().device;
|
|
device.activate_action(this.action_name, this.action_target);
|
|
} catch (e) {
|
|
logError(e);
|
|
}
|
|
}
|
|
|
|
this.destroy();
|
|
}
|
|
|
|
get action_name() {
|
|
if (this._action_name === undefined)
|
|
this._action_name = null;
|
|
|
|
return this._action_name;
|
|
}
|
|
|
|
set action_name(name) {
|
|
this._action_name = name;
|
|
}
|
|
|
|
get action_target() {
|
|
if (this._action_target === undefined)
|
|
this._action_target = null;
|
|
|
|
return this._action_target;
|
|
}
|
|
|
|
set action_target(variant) {
|
|
this._action_target = variant;
|
|
}
|
|
|
|
_onDeviceActivated(box, row) {
|
|
this.response(Gtk.ResponseType.OK);
|
|
}
|
|
|
|
_onDeviceSelected(box) {
|
|
this.set_response_sensitive(
|
|
Gtk.ResponseType.OK,
|
|
(box.get_selected_row())
|
|
);
|
|
}
|
|
|
|
_onDevicesChanged() {
|
|
// Collect known devices
|
|
const devices = {};
|
|
|
|
for (const [id, device] of this.application.manager.devices.entries())
|
|
devices[id] = device;
|
|
|
|
// Prune device rows
|
|
this.device_list.foreach(row => {
|
|
if (!devices.hasOwnProperty(row.name))
|
|
row.destroy();
|
|
else
|
|
delete devices[row.name];
|
|
});
|
|
|
|
// Add new devices
|
|
for (const device of Object.values(devices)) {
|
|
const action = device.lookup_action(this.action_name);
|
|
|
|
if (action === null)
|
|
continue;
|
|
|
|
const row = new Gtk.ListBoxRow({
|
|
visible: action.enabled,
|
|
});
|
|
row.set_name(device.id);
|
|
row.device = device;
|
|
|
|
action.bind_property(
|
|
'enabled',
|
|
row,
|
|
'visible',
|
|
Gio.SettingsBindFlags.DEFAULT
|
|
);
|
|
|
|
const grid = new Gtk.Grid({
|
|
column_spacing: 12,
|
|
margin: 6,
|
|
visible: true,
|
|
});
|
|
row.add(grid);
|
|
|
|
const icon = new Gtk.Image({
|
|
icon_name: device.icon_name,
|
|
pixel_size: 32,
|
|
visible: true,
|
|
});
|
|
grid.attach(icon, 0, 0, 1, 1);
|
|
|
|
const name = new Gtk.Label({
|
|
label: device.name,
|
|
halign: Gtk.Align.START,
|
|
hexpand: true,
|
|
visible: true,
|
|
});
|
|
grid.attach(name, 1, 0, 1, 1);
|
|
|
|
this.device_list.add(row);
|
|
}
|
|
|
|
if (this.device_list.get_selected_row() === null)
|
|
this.device_list.select_row(this.device_list.get_row_at_index(0));
|
|
}
|
|
|
|
_sortDevices(row1, row2) {
|
|
return row1.device.name.localeCompare(row2.device.name);
|
|
}
|
|
});
|
|
|
|
|
|
/**
|
|
* A dialog for reporting an error.
|
|
*/
|
|
export const ErrorDialog = GObject.registerClass({
|
|
GTypeName: 'GSConnectServiceErrorDialog',
|
|
Template: 'resource:///org/gnome/Shell/Extensions/GSConnect/ui/service-error-dialog.ui',
|
|
Children: [
|
|
'error-stack',
|
|
'expander-arrow',
|
|
'gesture',
|
|
'report-button',
|
|
'revealer',
|
|
],
|
|
}, class ErrorDialog extends Gtk.Window {
|
|
|
|
_init(error) {
|
|
super._init({
|
|
application: Gio.Application.get_default(),
|
|
title: `GSConnect: ${error.name}`,
|
|
});
|
|
this.set_keep_above(true);
|
|
|
|
this.error = error;
|
|
this.error_stack.buffer.text = `${error.message}\n\n${error.stack}`;
|
|
this.gesture.connect('released', this._onReleased.bind(this));
|
|
}
|
|
|
|
_onClicked(button) {
|
|
if (this.report_button === button) {
|
|
const uri = this._buildUri(this.error.message, this.error.stack);
|
|
Gio.AppInfo.launch_default_for_uri_async(uri, null, null, null);
|
|
}
|
|
|
|
this.destroy();
|
|
}
|
|
|
|
_onReleased(gesture, n_press) {
|
|
if (n_press === 1)
|
|
this.revealer.reveal_child = !this.revealer.reveal_child;
|
|
}
|
|
|
|
_onRevealChild(revealer, pspec) {
|
|
this.expander_arrow.icon_name = this.revealer.reveal_child
|
|
? 'pan-down-symbolic'
|
|
: 'pan-end-symbolic';
|
|
}
|
|
|
|
_buildUri(message, stack) {
|
|
const body = `\`\`\`${ISSUE_HEADER}\n${stack}\n\`\`\``;
|
|
const titleQuery = encodeURIComponent(message).replace('%20', '+');
|
|
const bodyQuery = encodeURIComponent(body).replace('%20', '+');
|
|
const uri = `${Config.PACKAGE_BUGREPORT}?title=${titleQuery}&body=${bodyQuery}`;
|
|
|
|
// Reasonable URI length limit
|
|
if (uri.length > 2000)
|
|
return uri.substr(0, 2000);
|
|
|
|
return uri;
|
|
}
|
|
});
|
|
|