[polari] js: Promisify async operations
- From: Florian Müllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [polari] js: Promisify async operations
- Date: Wed, 2 Dec 2020 21:34:50 +0000 (UTC)
commit 042d62b66e34c8e15db5c4e48ffc8e2d313b7292
Author: Florian Müllner <fmuellner gnome org>
Date: Mon Aug 20 00:51:19 2018 +0200
js: Promisify async operations
Promises make asynchronous operations easier to manage, in particular
when used through the async/await syntax that allows for asynchronous
code to closely resemble synchronous one.
gjs has included a Gio._promisify() helper for a while now, which
monkey-patches methods that follow GIO's async pattern to return a
Promise when called without a callback argument.
Use that to get rid of all those GAsyncReadyCallbacks!
https://gitlab.gnome.org/GNOME/polari/-/merge_requests/169
src/accountsMonitor.js | 26 +++--
src/application.js | 108 +++++++++---------
src/chatView.js | 14 ++-
src/connections.js | 53 +++++----
src/entryArea.js | 32 +++---
src/initialSetup.js | 10 +-
src/ircParser.js | 71 ++++++------
src/pasteManager.js | 82 ++++++--------
src/serverRoomManager.js | 22 ++--
src/telepathyClient.js | 282 +++++++++++++++++++++++++----------------------
src/utils.js | 197 ++++++++++++++-------------------
11 files changed, 429 insertions(+), 468 deletions(-)
---
diff --git a/src/accountsMonitor.js b/src/accountsMonitor.js
index 1ba238b9..e95b072f 100644
--- a/src/accountsMonitor.js
+++ b/src/accountsMonitor.js
@@ -5,6 +5,10 @@ const Signals = imports.signals;
const { NetworksManager } = imports.networksManager;
+Gio._promisify(Tp.AccountManager.prototype, 'prepare_async', 'prepare_finish');
+Gio._promisify(Tp.Account.prototype,
+ 'request_presence_async', 'request_presence_finish');
+
var AccountsMonitor = class {
static getDefault() {
if (!this._singleton)
@@ -95,17 +99,17 @@ var AccountsMonitor = class {
this.visibleAccounts.forEach(a => this._updateAccountReachable(a));
}
- _onPrepareShutdown() {
- let presence = Tp.ConnectionPresenceType.OFFLINE;
- this.accounts.filter(a => a.requested_presence_type !== presence).forEach(a => {
- this._app.hold();
- a.request_presence_async(presence, 'offline', '', (o, res) => {
- try {
- a.request_presence_finish(res);
- } catch (e) { }
- this._app.release();
- });
- });
+ async _onPrepareShutdown() {
+ const presence = Tp.ConnectionPresenceType.OFFLINE;
+ const onlineAccounts =
+ this.accounts.filter(a => a.requested_presence_type !== presence);
+
+ this._app.hold();
+
+ await Promise.all(onlineAccounts.map(
+ a => a.request_presence_async(presence, 'offline', '')));
+
+ this._app.release();
}
_shouldMonitorAccount(account) {
diff --git a/src/application.js b/src/application.js
index 762c6ded..5b40ca70 100644
--- a/src/application.js
+++ b/src/application.js
@@ -15,6 +15,18 @@ const { TelepathyClient } = imports.telepathyClient;
const { UserStatusMonitor } = imports.userTracker;
const Utils = imports.utils;
+Gio._promisify(Tp.AccountRequest.prototype,
+ 'create_account_async', 'create_account_finish');
+Gio._promisify(Tp.Account.prototype, 'remove_async', 'remove_finish');
+Gio._promisify(Tp.Account.prototype,
+ 'request_presence_async', 'request_presence_finish');
+Gio._promisify(Tp.Account.prototype,
+ 'set_enabled_async', 'set_enabled_finish');
+Gio._promisify(Tp.Account.prototype,
+ 'set_nickname_async', 'set_nickname_finish');
+Gio._promisify(Tp.Account.prototype,
+ 'update_parameters_vardict_async', 'update_parameters_vardict_finish');
+
const MAX_RETRIES = 3;
const IRC_SCHEMA_REGEX = /^(irc?:\/\/)([\da-z.-]+):?(\d+)?\/(?:%23)?([\w.+-]+)/i;
@@ -96,10 +108,8 @@ var Application = GObject.registerClass({
}
// Small wrapper to mark user-requested nick changes
- setAccountNick(account, nick) {
- account.set_nickname_async(nick, (a, res) => {
- account.set_nickname_finish(res);
- });
+ async setAccountNick(account, nick) {
+ await account.set_nickname_async(nick);
this._untrackNominalNick(account);
}
@@ -352,9 +362,7 @@ var Application = GObject.registerClass({
this._accountsMonitor.connect('account-added', (am, account) => {
// Reset nickname at startup
let accountName = this._getTrimmedAccountName(account);
- account.set_nickname_async(accountName, (a, res) => {
- a.set_nickname_finish(res);
- });
+ account.set_nickname_async(accountName);
});
this._accountsMonitor.connect('account-removed', (am, account) => {
// Make sure we don't 'inject' outdated data into
@@ -448,7 +456,7 @@ var Application = GObject.registerClass({
});
let joinAction = this.lookup_action('join-room');
- uris.forEach(uri => {
+ uris.forEach(async uri => {
let [success, server, port, room] = this._parseURI(uri);
if (!success)
return;
@@ -459,20 +467,16 @@ var Application = GObject.registerClass({
map[a].service === matchedId;
});
+ let accountPath;
if (matches.length) {
- joinAction.activate(new GLib.Variant('(ssu)', [
- matches[0], `#${room}`, time,
- ]));
+ accountPath = matches[0];
} else {
- this._createAccount(matchedId, server, port, a => {
- if (a) {
- joinAction.activate(new GLib.Variant('(ssu)', [
- a.get_object_path(),
- `#${room}`, time,
- ]));
- }
- });
+ const account =
+ await this._createAccount(matchedId, server, port);
+ accountPath = account.get_object_path();
}
+
+ joinAction.activate(new GLib.Variant('(ssu)', [accountPath, `#${room}`, time]));
});
}
@@ -492,7 +496,7 @@ var Application = GObject.registerClass({
return [success, server, port, room];
}
- _createAccount(id, server, port, callback) {
+ async _createAccount(id, server, port) {
let params, name;
if (id) {
@@ -522,14 +526,12 @@ var Application = GObject.registerClass({
for (let prop in params)
req.set_parameter(prop, params[prop]);
- req.create_account_async((r, res) => {
- let account = req.create_account_finish(res);
+ const account = await req.create_account_async();
- Utils.clearAccountPassword(account);
- Utils.clearIdentifyPassword(account);
+ Utils.clearAccountPassword(account);
+ Utils.clearIdentifyPassword(account);
- callback(account);
- });
+ return account;
}
_needsInitialSetup() {
@@ -602,9 +604,7 @@ var Application = GObject.registerClass({
return;
this._untrackNominalNick(account);
- account.set_nickname_async(nominalNick, (a, res) => {
- a.set_nickname_finish(res);
- });
+ account.set_nickname_async(nominalNick);
});
this._nickTrackData.set(account, { tracker, contactsChangedId });
}
@@ -649,15 +649,15 @@ var Application = GObject.registerClass({
let accountName = this._getTrimmedAccountName(account);
let params = { account: new GLib.Variant('s', accountName) };
let asv = new GLib.Variant('a{sv}', params);
- account.update_parameters_vardict_async(asv, [], null);
+ account.update_parameters_vardict_async(asv, []);
}
- _retryWithParams(account, params) {
- account.update_parameters_vardict_async(params, [], () => {
- let presence = Tp.ConnectionPresenceType.AVAILABLE;
- let msg = account.requested_status_message;
- account.request_presence_async(presence, 'available', msg, null);
- });
+ async _retryWithParams(account, params) {
+ await account.update_parameters_vardict_async(params, []);
+
+ const presence = Tp.ConnectionPresenceType.AVAILABLE;
+ const msg = account.requested_status_message;
+ account.request_presence_async(presence, 'available', msg);
}
_retryNickRequest(account) {
@@ -722,7 +722,7 @@ var Application = GObject.registerClass({
// Connection failed, keep tp from retrying over and over
let presence = Tp.ConnectionPresenceType.OFFLINE;
let msg = account.requested_status_message;
- account.request_presence_async(presence, 'offline', msg, null);
+ account.request_presence_async(presence, 'offline', msg);
}
}
@@ -750,32 +750,26 @@ var Application = GObject.registerClass({
action.change_state(GLib.Variant.new('b', !state.get_boolean()));
}
- _onRemoveConnection(action, parameter) {
+ async _onRemoveConnection(action, parameter) {
let accountPath = parameter.deep_unpack();
let account = this._accountsMonitor.lookupAccount(accountPath);
- account.set_enabled_async(false, (a, res) => {
- account.set_enabled_finish(res);
- account.visible = false;
+ await account.set_enabled_async(false);
+ account.visible = false;
- let label = _('%s removed.').format(account.display_name);
- let n = new AppNotifications.UndoNotification(label);
- this.notificationQueue.addNotification(n);
+ const label = _('%s removed.').format(account.display_name);
+ const n = new AppNotifications.UndoNotification(label);
+ this.notificationQueue.addNotification(n);
- n.connect('closed', () => {
- account.remove_async((o, r) => {
- a.remove_finish(r); // TODO: Check for errors
+ n.connect('closed', async () => {
+ await account.remove_async(); // TODO: Check for errors
- Utils.clearAccountPassword(a);
- Utils.clearIdentifyPassword(a);
- });
- });
- n.connect('undo', () => {
- account.set_enabled_async(true, (o, r) => {
- account.set_enabled_finish(r);
- account.visible = true;
- });
- });
+ Utils.clearAccountPassword(account);
+ Utils.clearIdentifyPassword(account);
+ });
+ n.connect('undo', async () => {
+ await account.set_enabled_async(true);
+ account.visible = true;
});
}
diff --git a/src/chatView.js b/src/chatView.js
index a7d2406a..34f12b5c 100644
--- a/src/chatView.js
+++ b/src/chatView.js
@@ -11,6 +11,9 @@ const { UserStatusMonitor } = imports.userTracker;
const { URLPreview } = imports.urlPreview;
const Utils = imports.utils;
+Gio._promisify(Tpl.LogWalker.prototype,
+ 'get_events_async', 'get_events_finish');
+
var MAX_NICK_CHARS = 8;
const IGNORE_STATUS_TIME = 5;
@@ -359,8 +362,7 @@ var ChatView = GObject.registerClass({
Tpl.EventTypeMask.TEXT, null);
this._fetchingBacklog = true;
- this._logWalker.get_events_async(NUM_INITIAL_LOG_EVENTS,
- this._onLogEventsReady.bind(this));
+ this._getLogEvents(NUM_INITIAL_LOG_EVENTS);
this._autoscroll = true;
@@ -549,11 +551,12 @@ var ChatView = GObject.registerClass({
this._logWalker = null;
}
- _onLogEventsReady(lw, res) {
+ async _getLogEvents(num) {
+ const [events] = await this._logWalker.get_events_async(num);
+
this._hideLoadingIndicator();
this._fetchingBacklog = false;
- let [, events] = lw.get_events_finish(res);
let messages = events.map(e => this._createMessage(e));
this._pendingLogs = messages.concat(this._pendingLogs);
this._insertPendingLogs();
@@ -744,8 +747,7 @@ var ChatView = GObject.registerClass({
this._fetchingBacklog = true;
this._showLoadingIndicator();
this._backlogTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
- this._logWalker.get_events_async(NUM_LOG_EVENTS,
- this._onLogEventsReady.bind(this));
+ this._getLogEvents(NUM_LOG_EVENTS);
this._backlogTimeoutId = 0;
return GLib.SOURCE_REMOVE;
});
diff --git a/src/connections.js b/src/connections.js
index 7e2264d4..e42a5f78 100644
--- a/src/connections.js
+++ b/src/connections.js
@@ -1,11 +1,18 @@
/* exported ConnectionProperties ConnectionDetails ConnectionsList */
-const { GLib, GObject, Gtk, TelepathyGLib: Tp } = imports.gi;
+const { Gio, GLib, GObject, Gtk, TelepathyGLib: Tp } = imports.gi;
const { AccountsMonitor } = imports.accountsMonitor;
const { NetworksManager } = imports.networksManager;
const Utils = imports.utils;
+Gio._promisify(Tp.Account.prototype,
+ 'set_display_name_async', 'set_display_name_finish');
+Gio._promisify(Tp.Account.prototype,
+ 'update_parameters_vardict_async', 'update_parameters_vardict_finish');
+Gio._promisify(Tp.AccountRequest.prototype,
+ 'create_account_async', 'create_account_finish');
+
const DEFAULT_PORT = 6667;
const DEFAULT_SSL_PORT = 6697;
@@ -196,7 +203,7 @@ var ConnectionsList = GObject.registerClass({
});
}
- _onRowActivated(list, row) {
+ async _onRowActivated(list, row) {
let name = this._networksManager.getNetworkName(row.id);
let req = new Tp.AccountRequest({
account_manager: Tp.AccountManager.dup(),
@@ -212,17 +219,14 @@ var ConnectionsList = GObject.registerClass({
for (let prop in details)
req.set_parameter(prop, details[prop]);
- req.create_account_async((r, res) => {
- let account = req.create_account_finish(res);
- if (!account) // TODO: Handle errors
- return;
+ this.emit('account-selected');
- Utils.clearAccountPassword(account);
- Utils.clearIdentifyPassword(account);
+ const account = await req.create_account_async();
- this.emit('account-created', account);
- });
- this.emit('account-selected');
+ Utils.clearAccountPassword(account);
+ Utils.clearIdentifyPassword(account);
+
+ this.emit('account-created', account);
}
_setAccountRowSensitive(account, sensitive) {
@@ -432,7 +436,7 @@ var ConnectionDetails = GObject.registerClass({
this._createAccount();
}
- _createAccount() {
+ async _createAccount() {
let params = this._getParams();
let accountManager = Tp.AccountManager.dup();
let req = new Tp.AccountRequest({
@@ -448,32 +452,25 @@ var ConnectionDetails = GObject.registerClass({
for (let prop in details)
req.set_parameter(prop, details[prop]);
- req.create_account_async((r, res) => {
- let account = req.create_account_finish(res);
- if (!account) // TODO: Handle errors
- return;
+ const account = await req.create_account_async();
- Utils.clearAccountPassword(account);
- Utils.clearIdentifyPassword(account);
+ Utils.clearAccountPassword(account);
+ Utils.clearIdentifyPassword(account);
- this.emit('account-created', account);
- });
+ this.emit('account-created', account);
}
- _updateAccount() {
+ async _updateAccount() {
let params = this._getParams();
let account = this._account;
let oldDetails = account.dup_parameters_vardict().deep_unpack();
let [details, removed] = this._detailsFromParams(params, oldDetails);
let vardict = GLib.Variant.new('a{sv}', details);
- account.update_parameters_vardict_async(vardict, removed, (a, res) => {
- a.update_parameters_vardict_finish(res); // TODO: Check for errors
- });
-
- account.set_display_name_async(params.name, (a, res) => {
- a.set_display_name_finish(res); // TODO: Check for errors
- });
+ await Promise.all([
+ account.update_parameters_vardict_async(vardict, removed),
+ account.set_display_name_async(params.name),
+ ]);
}
_detailsFromParams(params, oldDetails) {
diff --git a/src/entryArea.js b/src/entryArea.js
index a2ddefa7..6e7a6583 100644
--- a/src/entryArea.js
+++ b/src/entryArea.js
@@ -12,6 +12,8 @@ const { TabCompletion } = imports.tabCompletion;
const MAX_NICK_UPDATE_TIME = 5; /* s */
const MAX_LINES = 5;
+Gio._promisify(Gio._LocalFilePrototype,
+ 'query_info_async', 'query_info_finish');
var ChatEntry = GObject.registerClass({
Implements: [DropTargetIface],
@@ -383,18 +385,13 @@ var EntryArea = GObject.registerClass({
this._setPasteContent(pixbuf);
}
- pasteFile(file) {
- file.query_info_async(
- Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
- Gio.FileQueryInfoFlags.NONE,
- GLib.PRIORITY_DEFAULT, null,
- this._onFileInfoReady.bind(this));
- }
-
- _onFileInfoReady(file, res) {
+ async pasteFile(file) {
let fileInfo = null;
try {
- fileInfo = file.query_info_finish(res);
+ fileInfo = await file.query_info_async(
+ Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_DEFAULT, null);
} catch (e) {
return;
}
@@ -407,7 +404,7 @@ var EntryArea = GObject.registerClass({
this._setPasteContent(file);
}
- _onPasteClicked() {
+ async _onPasteClicked() {
let title;
let nick = this._room.channel.connection.self_contact.alias;
if (this._room.type === Tp.HandleType.ROOM)
@@ -415,24 +412,23 @@ var EntryArea = GObject.registerClass({
title = _('%s in #%s').format(nick, this._room.display_name);
else
title = _('Paste from %s').format(nick);
+ this._confirmLabel.hide();
this._confirmLabel.hide();
this._uploadSpinner.start();
let app = Gio.Application.get_default();
try {
- app.pasteManager.pasteContent(this._pasteContent, title, url => {
- // TODO: handle errors
- this._uploadSpinner.stop();
- this._setPasteContent(null);
- if (url)
- this._chatEntry.emit('insert-at-cursor', url);
- });
+ const url =
+ await app.pasteManager.pasteContent(this._pasteContent, title);
+ this._setPasteContent(null);
+ this._chatEntry.emit('insert-at-cursor', url);
} catch (e) {
let type = typeof this._pasteContent;
if (type === 'object')
type = this._pasteContent.toString();
debug(`Failed to paste content of type ${type}`);
+ } finally {
this._uploadSpinner.stop();
}
}
diff --git a/src/initialSetup.js b/src/initialSetup.js
index 9e63a4b3..82831a00 100644
--- a/src/initialSetup.js
+++ b/src/initialSetup.js
@@ -1,9 +1,11 @@
/* exported InitialSetupWindow */
-const { Gio, GLib, GObject, Gtk } = imports.gi;
+const { Gio, GLib, GObject, Gtk, TelepathyGLib: Tp } = imports.gi;
const Utils = imports.utils;
+Gio._promisify(Tp.Account.prototype, 'remove_async', 'remove_finish');
+
const SetupPage = {
CONNECTION: 0,
ROOM: 1,
@@ -94,13 +96,11 @@ var InitialSetupWindow = GObject.registerClass({
this._updateNextSensitivity();
}
- _unsetAccount() {
+ async _unsetAccount() {
if (!this._currentAccount)
return;
- this._currentAccount.remove_async((a, res) => {
- a.remove_finish(res);
- });
+ await this._currentAccount.remove_async();
this._currentAccount = null;
}
diff --git a/src/ircParser.js b/src/ircParser.js
index 71c9e272..75909076 100644
--- a/src/ircParser.js
+++ b/src/ircParser.js
@@ -6,6 +6,9 @@ const Signals = imports.signals;
const AppNotifications = imports.appNotifications;
const { RoomManager } = imports.roomManager;
const Utils = imports.utils;
+
+Gio._promisify(Tp.Account.prototype,
+ 'request_presence_async', 'request_presence_finish');
Gio._promisify(Tp.Connection.prototype,
'dup_contact_by_id_async', 'dup_contact_by_id_finish');
Gio._promisify(Tp.Contact.prototype,
@@ -100,17 +103,14 @@ var IrcParser = class {
retval = false;
break;
}
- this._room.channel.connection.dup_contact_by_id_async(nick, [],
- (c, res) => {
- let contact;
- try {
- contact = c.dup_contact_by_id_finish(res);
- } catch (e) {
- logError(e, `Failed to get contact for ${nick}`);
- return;
- }
- this._room.add_member(contact);
- });
+ try {
+ let connection = this._room.channel.connection;
+ let contact = await connection.dup_contact_by_id_async(nick);
+ this._room.add_member(contact);
+ } catch (e) {
+ logError(e, `Failed to get contact for ${nick}`);
+ retval = false;
+ }
break;
}
case 'J':
@@ -141,17 +141,14 @@ var IrcParser = class {
retval = false;
break;
}
- this._room.channel.connection.dup_contact_by_id_async(nick, [],
- (c, res) => {
- let contact;
- try {
- contact = c.dup_contact_by_id_finish(res);
- } catch (e) {
- logError(e, `Failed to get contact for ${nick}`);
- return;
- }
- this._room.remove_member(contact);
- });
+ try {
+ let connection = this._room.channel.connection;
+ let contact = await connection.dup_contact_by_id_async(nick);
+ this._room.remove_member(contact);
+ } catch (e) {
+ logError(e, `Failed to get contact for ${nick}`);
+ retval = false;
+ }
break;
}
case 'ME': {
@@ -247,14 +244,12 @@ var IrcParser = class {
case 'QUIT': {
let presence = Tp.ConnectionPresenceType.OFFLINE;
let message = stripCommand(text);
- this._room.account.request_presence_async(presence, 'offline', message,
- (a, res) => {
- try {
- a.request_presence_finish(res);
- } catch (e) {
- logError(e, 'Failed to disconnect');
- }
- });
+ try {
+ await this._room.account.request_presence_async(presence, 'offline', message);
+ } catch (e) {
+ logError(e, 'Failed to disconnect');
+ retval = false;
+ }
break;
}
case 'SAY': {
@@ -318,15 +313,13 @@ var IrcParser = class {
this._sendMessage(message);
}
- _sendMessage(message) {
- this._room.channel.send_message_async(message, 0, (c, res) => {
- try {
- c.send_message_finish(res);
- } catch (e) {
- // TODO: propagate to user
- logError(e, 'Failed to send message');
- }
- });
+ async _sendMessage(message) {
+ try {
+ await this._room.channel.send_message_async(message, 0);
+ } catch (e) {
+ // TODO: propagate to user
+ logError(e, 'Failed to send message');
+ }
}
};
Signals.addSignalMethods(IrcParser.prototype);
diff --git a/src/pasteManager.js b/src/pasteManager.js
index dd2493d0..7ddf82ae 100644
--- a/src/pasteManager.js
+++ b/src/pasteManager.js
@@ -4,6 +4,14 @@ const { Gdk, GdkPixbuf, Gio, GLib, GObject, Gtk, Polari } = imports.gi;
const Utils = imports.utils;
+Gio._promisify(Gio._LocalFilePrototype,
+ 'load_contents_async', 'load_contents_finish');
+Gio._promisify(Gio._LocalFilePrototype,
+ 'query_info_async', 'query_info_finish');
+Gio._promisify(Gio._LocalFilePrototype, 'read_async', 'read_finish');
+Gio._promisify(GdkPixbuf.Pixbuf.prototype,
+ 'new_from_stream_async', 'new_from_stream_finish');
+
const DndTargetType = {
URI_LIST: 1,
@@ -22,50 +30,36 @@ function _getTargetForContentType(contentType) {
var PasteManager = class {
- pasteContent(content, title, callback) {
+ pasteContent(content, title) {
if (typeof content === 'string')
- Utils.gpaste(content, title, callback);
+ return Utils.gpaste(content, title);
else if (content instanceof GdkPixbuf.Pixbuf)
- Utils.imgurPaste(content, title, callback);
+ return Utils.imgurPaste(content, title);
else if (content.query_info_async)
- this._pasteFile(content, title, callback);
+ return this._pasteFile(content, title);
else
throw new Error('Unhandled content type');
}
- _pasteFile(file, title, callback) {
- file.query_info_async(
+ async _pasteFile(file, title) {
+ const fileInfo = await file.query_info_async(
Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
Gio.FileQueryInfoFlags.NONE,
- GLib.PRIORITY_DEFAULT, null, (f, res) => {
- try {
- let fileInfo = file.query_info_finish(res);
- this._handleFilePaste(file, fileInfo, title, callback);
- } catch (e) {
- callback(null);
- }
- });
- }
+ GLib.PRIORITY_DEFAULT, null);
- _handleFilePaste(file, fileInfo, title, callback) {
let contentType = fileInfo.get_content_type();
let targetType = _getTargetForContentType(contentType);
if (targetType === DndTargetType.TEXT) {
- file.load_contents_async(null, (f, res) => {
- let [, contents] = f.load_contents_finish(res);
- Utils.gpaste(contents.toString(), title, callback);
- });
+ const [, contents] = await file.load_contents_async(null);
+ return Utils.gpaste(contents.toString(), title);
} else if (targetType === DndTargetType.IMAGE) {
- file.read_async(GLib.PRIORITY_DEFAULT, null, (f, res) => {
- let stream = f.read_finish(res);
- GdkPixbuf.Pixbuf.new_from_stream_async(stream, null, (s, r) => {
- let pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(r);
- Utils.imgurPaste(pixbuf, title, callback);
- });
- });
+ const stream = await file.read_async(GLib.PRIORITY_DEFAULT, null);
+ const pixbuf =
+ await GdkPixbuf.Pixbuf.new_from_stream_async(stream, null);
+ return Utils.imgurPaste(pixbuf, title);
} else {
- callback(null);
+ throw new Error('Unhandled content type');
}
}
};
@@ -149,7 +143,7 @@ var DropTargetIface = GObject.registerClass({
}
- _onDragDataReceived(_widget, context, _x, _y, data, info, time) {
+ async _onDragDataReceived(_widget, context, _x, _y, data, info, time) {
if (info === DndTargetType.URI_LIST) {
let uris = data.get_uris();
if (!uris) {
@@ -158,14 +152,19 @@ var DropTargetIface = GObject.registerClass({
}
// TODO: handle multiple files ...
- let file = Gio.File.new_for_uri(uris[0]);
+ const file = Gio.File.new_for_uri(uris[0]);
+ const attr = Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
+ const flags = Gio.FileQueryInfoFlags.NONE;
+ const priority = GLib.PRIORITY_DEFAULT;
try {
- this._lookupFileInfo(file, targetType => {
- let canHandle = targetType !== 0;
- if (canHandle)
- this.emit('file-dropped', file);
- Gtk.drag_finish(context, canHandle, false, time);
- });
+ const fileInfo =
+ await file.query_info_async(attr, flags, priority, null);
+ const contentType = fileInfo.get_content_type();
+ const targetType = _getTargetForContentType(contentType);
+ const canHandle = targetType !== 0;
+ if (canHandle)
+ this.emit('file-dropped', file);
+ Gtk.drag_finish(context, canHandle, false, time);
} catch (e) {
Gtk.drag_finish(context, false, false, time);
}
@@ -184,15 +183,4 @@ var DropTargetIface = GObject.registerClass({
Gtk.drag_finish(context, success, false, time);
}
}
-
- _lookupFileInfo(file, callback) {
- let attr = Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
- let flags = Gio.FileQueryInfoFlags.NONE;
- let priority = GLib.PRIORITY_DEFAULT;
- file.query_info_async(attr, flags, priority, null, (f, res) => {
- let fileInfo = file.query_info_finish(res);
- let contentType = fileInfo.get_content_type();
- callback(_getTargetForContentType(contentType));
- });
- }
});
diff --git a/src/serverRoomManager.js b/src/serverRoomManager.js
index 25a3ea1b..315ef3e7 100644
--- a/src/serverRoomManager.js
+++ b/src/serverRoomManager.js
@@ -1,12 +1,14 @@
/* exported ServerRoomManager ServerRoomList */
-const { Gdk, GLib, GObject, Gtk, TelepathyGLib: Tp } = imports.gi;
+const { Gdk, Gio, GLib, GObject, Gtk, TelepathyGLib: Tp } = imports.gi;
const Signals = imports.signals;
const { AccountsMonitor } = imports.accountsMonitor;
const { RoomManager } = imports.roomManager;
const Utils = imports.utils;
+Gio._promisify(Tp.RoomList.prototype, 'init_async', 'init_finish');
+
const MS_PER_IDLE = 10; // max time spend in idle
const MS_PER_FILTER_IDLE = 5; // max time spend in idle while filtering
@@ -46,7 +48,7 @@ var ServerRoomManager = class {
return roomList.list.listing;
}
- _onAccountStatusChanged(mon, account) {
+ async _onAccountStatusChanged(mon, account) {
if (account.connection_status === Tp.ConnectionStatus.CONNECTING)
this.emit('loading-changed', account);
@@ -57,18 +59,16 @@ var ServerRoomManager = class {
return;
let roomList = new Tp.RoomList({ account });
- roomList.init_async(GLib.PRIORITY_DEFAULT, null, (o, res) => {
- try {
- roomList.init_finish(res);
- } catch (e) {
- this._roomLists.delete(account);
- return;
- }
- roomList.start();
- });
roomList.connect('got-room', this._onGotRoom.bind(this));
roomList.connect('notify::listing', this._onListingChanged.bind(this));
this._roomLists.set(account, { list: roomList, rooms: [] });
+
+ try {
+ await roomList.init_async(GLib.PRIORITY_DEFAULT, null);
+ roomList.start();
+ } catch (e) {
+ this._roomLists.delete(account);
+ }
}
_onAccountRemoved(mon, account) {
diff --git a/src/telepathyClient.js b/src/telepathyClient.js
index 180c8b27..751974f7 100644
--- a/src/telepathyClient.js
+++ b/src/telepathyClient.js
@@ -7,6 +7,22 @@ const { RoomManager } = imports.roomManager;
const { UserStatusMonitor } = imports.userTracker;
const Utils = imports.utils;
+Gio._promisify(Gio.DBusConnection.prototype, 'call', 'call_finish');
+Gio._promisify(Polari.Room.prototype,
+ 'send_identify_message_async', 'send_identify_message_finish');
+Gio._promisify(Tp.Account.prototype, 'reconnect_async', 'reconnect_finish');
+Gio._promisify(Tp.Account.prototype,
+ 'request_presence_async', 'request_presence_finish');
+Gio._promisify(Tp.Account.prototype,
+ 'set_enabled_async', 'set_enabled_finish');
+Gio._promisify(Tp.Account.prototype,
+ 'update_parameters_vardict_async', 'update_parameters_vardict_finish');
+Gio._promisify(Tp.AccountChannelRequest.prototype,
+ 'ensure_and_observe_channel_async', 'ensure_and_observe_channel_finish');
+Gio._promisify(Tp.Channel.prototype, 'leave_async', 'leave_finish');
+Gio._promisify(Tp.TextChannel.prototype,
+ 'send_message_async', 'send_message_finish');
+
const SHELL_CLIENT_PREFIX = 'org.freedesktop.Telepathy.Client.GnomeShell';
const SASLAuthenticationIface = '<node> \
@@ -54,19 +70,16 @@ class SASLAuthHandler {
this._onProxyReady.bind(this));
}
- _onProxyReady() {
+ async _onProxyReady() {
this._proxy.connectSignal('SASLStatusChanged',
this._onSASLStatusChanged.bind(this));
let account = this._channel.connection.get_account();
- Utils.lookupAccountPassword(account, this._onPasswordReady.bind(this));
- }
-
- _onPasswordReady(password) {
- if (password) {
+ try {
+ const password = await Utils.lookupAccountPassword(account);
this._proxy.StartMechanismWithDataRemote(
'X-TELEPATHY-PASSWORD', password);
- } else {
+ } catch (e) {
this._proxy.AbortSASLRemote(
SASLAbortReason.USER_ABORT,
'Password not available',
@@ -97,15 +110,13 @@ class SASLAuthHandler {
}
}
- _resetPrompt() {
+ async _resetPrompt() {
let account = this._channel.connection.get_account();
let prompt = new GLib.Variant('b', false);
let params = new GLib.Variant('a{sv}', { 'password-prompt': prompt });
- account.update_parameters_vardict_async(params, [], (a, res) => {
- a.update_parameters_vardict_finish(res);
- account.request_presence_async(Tp.ConnectionPresenceType.AVAILABLE,
- 'available', '', null);
- });
+ await account.update_parameters_vardict_async(params, []);
+ await account.request_presence_async(Tp.ConnectionPresenceType.AVAILABLE,
+ 'available', '', null);
}
}
@@ -143,7 +154,7 @@ class TelepathyClient extends Tp.BaseClient {
this._monitorShellClient();
}
- _monitorShellClient() {
+ async _monitorShellClient() {
// Track whether gnome-shell's built-in chat client is
// running; unfortunately it uses :uniquify-name, so
// we cannot simply use Gio.watch_bus_name()
@@ -160,28 +171,25 @@ class TelepathyClient extends Tp.BaseClient {
this._shellHandlesPrivateChats = newOwner !== '';
});
- conn.call(
- 'org.freedesktop.DBus',
- '/org/freedesktop/DBus',
- 'org.freedesktop.DBus',
- 'ListNames',
- null, /* params */
- new GLib.VariantType('(as)'),
- Gio.DBusCallFlags.NONE,
- -1,
- null, /* cancellable */
- (_o, res) => {
- let names = [];
-
- try {
- [names] = conn.call_finish(res).deep_unpack();
- } catch (e) {
- debug(`Failed to list bus names: ${e}`);
- }
-
- this._shellHandlesPrivateChats =
- names.some(n => n.startsWith(SHELL_CLIENT_PREFIX));
- });
+ let names = [];
+ try {
+ const result = await conn.call(
+ 'org.freedesktop.DBus',
+ '/org/freedesktop/DBus',
+ 'org.freedesktop.DBus',
+ 'ListNames',
+ null, /* params */
+ new GLib.VariantType('(as)'),
+ Gio.DBusCallFlags.NONE,
+ -1,
+ null);
+ [names] = result.deep_unpack();
+ } catch (e) {
+ debug(`Failed to list bus names: ${e}`);
+ }
+
+ this._shellHandlesPrivateChats =
+ names.some(n => n.startsWith(SHELL_CLIENT_PREFIX));
}
_onPrepared() {
@@ -271,23 +279,23 @@ class TelepathyClient extends Tp.BaseClient {
this._setAccountPresence(account, presence);
}
- _onAccountStatusChanged(mon, account) {
+ async _onAccountStatusChanged(mon, account) {
if (account.connection_status !== Tp.ConnectionStatus.CONNECTED)
return;
- Utils.lookupIdentifyPassword(account, password => {
- if (password)
- this._sendIdentify(account, password);
- else
- this._connectRooms(account);
- });
+ try {
+ const password = await Utils.lookupIdentifyPassword(account);
+ this._sendIdentify(account, password);
+ } catch (e) {
+ this._connectRooms(account);
+ }
}
_connectAccount(account) {
this._setAccountPresence(account, Tp.ConnectionPresenceType.AVAILABLE);
}
- _setAccountPresence(account, presence) {
+ async _setAccountPresence(account, presence) {
if (!account.enabled)
return;
@@ -298,13 +306,11 @@ class TelepathyClient extends Tp.BaseClient {
let accountName = account.display_name;
debug(`Setting presence of account "${accountName}" to ${status}`);
- account.request_presence_async(presence, status, msg, (o, res) => {
- try {
- account.request_presence_finish(res);
- } catch (e) {
- log(`Connection failed: ${e.message}`);
- }
- });
+ try {
+ await account.request_presence_async(presence, status, msg);
+ } catch (e) {
+ log(`Connection failed: ${e.message}`);
+ }
}
_connectRooms(account) {
@@ -314,13 +320,16 @@ class TelepathyClient extends Tp.BaseClient {
});
}
- _connectRoom(room) {
- this._requestChannel(room.account, room.type, room.channel_name, null);
+ async _connectRoom(room) {
+ try {
+ await this._requestChannel(
+ room.account, room.type, room.channel_name, null);
+ } catch (e) {}
}
- _requestChannel(account, targetType, targetId, callback) {
+ async _requestChannel(account, targetType, targetId) {
if (!account || !account.enabled)
- return;
+ return null;
let roomId = Polari.create_room_id(account, targetId, targetType);
@@ -336,28 +345,28 @@ class TelepathyClient extends Tp.BaseClient {
req.set_target_id(targetType, targetId);
req.set_delegate_to_preferred_handler(true);
- let preferredHandler = `${Tp.CLIENT_BUS_NAME_BASE}Polari`;
- req.ensure_and_observe_channel_async(preferredHandler, cancellable,
- (o, res) => {
- let channel = null;
- let room = this._roomManager.lookupRoom(roomId);
- try {
- channel = req.ensure_and_observe_channel_finish(res);
- room.channel_error = '';
- } catch (e) {
- debug(`Failed to ensure channel: ${e.message}`);
-
- if (room)
- room.channel_error = Tp.error_get_dbus_name(e.code);
- }
-
- if (callback)
- callback(channel);
- this._pendingRequests.delete(roomId);
- });
+ const room = this._roomManager.lookupRoom(roomId);
+ const preferredHandler = `${Tp.CLIENT_BUS_NAME_BASE}Polari`;
+ let channel = null;
+ try {
+ channel = await req.ensure_and_observe_channel_async(
+ preferredHandler, cancellable);
+ room.channel_error = '';
+ } catch (e) {
+ debug(`Failed to ensure channel: ${e.message}`);
+
+ if (room)
+ room.channel_error = Tp.error_get_dbus_name(e.code);
+
+ throw e;
+ } finally {
+ this._pendingRequests.delete(roomId);
+ }
+
+ return channel;
}
- _sendIdentify(account, password) {
+ async _sendIdentify(account, password) {
let { settings } = account;
let params = account.dup_parameters_vardict().deep_unpack();
@@ -366,41 +375,41 @@ class TelepathyClient extends Tp.BaseClient {
let alwaysSendUsername = settings.get_boolean('identify-username-supported');
let contactName = settings.get_string('identify-botname');
let command = settings.get_string('identify-command');
- this._requestChannel(account, Tp.HandleType.CONTACT, contactName,
- channel => {
- if (!channel)
- return;
- let room = this._roomManager.lookupRoomByChannel(channel);
- let activeNick = room.channel.connection.self_contact.alias;
- // Omit username parameter when it matches the default, to
- // support NickServ bots that don't support the parameter at all
- if (!alwaysSendUsername && activeNick === username)
- username = null;
- room.send_identify_message_async(command, username, password, (r, res) => {
- try {
- r.send_identify_message_finish(res);
- } catch (e) {
- log(`Failed to send identify message: ${e.message}`);
- }
- this._connectRooms(account);
- });
- });
+ let channel = null;
+ try {
+ channel = await this._requestChannel(
+ account, Tp.HandleType.CONTACT, contactName);
+ } catch (e) {
+ return;
+ }
+
+ const room = this._roomManager.lookupRoomByChannel(channel);
+ const activeNick = room.channel.connection.self_contact.alias;
+ // Omit username parameter when it matches the default, to
+ // support NickServ bots that don't support the parameter at all
+ if (!alwaysSendUsername && activeNick === username)
+ username = null;
+
+ try {
+ await room.send_identify_message_async(command, username, password);
+ } catch (e) {
+ log(`Failed to send identify message: ${e.message}`);
+ }
+ this._connectRooms(account);
}
- _sendMessage(channel, message) {
+ async _sendMessage(channel, message) {
if (!message || !channel)
return;
let type = Tp.ChannelTextMessageType.NORMAL;
- channel.send_message_async(Tp.ClientMessage.new_text(type, message), 0,
- (c, res) => {
- try {
- c.send_message_finish(res);
- } catch (e) {
- log(`Failed to send message: ${e.message}`);
- }
- });
+ try {
+ await channel.send_message_async(
+ Tp.ClientMessage.new_text(type, message), 0);
+ } catch (e) {
+ log(`Failed to send message: ${e.message}`);
+ }
}
_onConnectAccountActivated(action, parameter) {
@@ -409,50 +418,57 @@ class TelepathyClient extends Tp.BaseClient {
if (account.enabled)
this._connectAccount(account);
else
- account.set_enabled_async(true, () => {});
+ account.set_enabled_async(true);
}
- _onDisconnectAccountActivated(action, parameter) {
+ async _onDisconnectAccountActivated(action, parameter) {
let accountPath = parameter.deep_unpack();
let account = this._accountsMonitor.lookupAccount(accountPath);
- account.set_enabled_async(false, () => {
- this._setAccountPresence(account, Tp.ConnectionPresenceType.OFFLINE);
- });
+ await account.set_enabled_async(false);
+ this._setAccountPresence(account, Tp.ConnectionPresenceType.OFFLINE);
}
_onReconnectAccountActivated(action, parameter) {
let accountPath = parameter.deep_unpack();
let account = this._accountsMonitor.lookupAccount(accountPath);
- account.reconnect_async((a, res) => a.reconnect_finish(res));
+ account.reconnect_async();
}
- _onAuthenticateAccountActivated(action, parameter) {
+ async _onAuthenticateAccountActivated(action, parameter) {
let [accountPath, password] = parameter.deep_unpack();
let account = this._accountsMonitor.lookupAccount(accountPath);
let prompt = new GLib.Variant('b', password.length > 0);
let params = GLib.Variant.new('a{sv}', { 'password-prompt': prompt });
- account.update_parameters_vardict_async(params, [], (a, res) => {
- a.update_parameters_vardict_finish(res);
- Utils.storeAccountPassword(a, password, () => {
- a.reconnect_async(null);
- });
- });
+ await account.update_parameters_vardict_async(params, []);
+ await Utils.storeAccountPassword(account, password);
+ await account.reconnect_async();
}
- _onQueryActivated(action, parameter) {
+ async _onQueryActivated(action, parameter) {
let [accountPath, channelName, message, time_] = parameter.deep_unpack();
let account = this._accountsMonitor.lookupAccount(accountPath);
if (!account || !account.enabled)
return;
- this._requestChannel(account, Tp.HandleType.CONTACT, channelName, c => {
- this._sendMessage(c, message);
- });
+ try {
+ let channel = await this._requestChannel(
+ account, Tp.HandleType.CONTACT, channelName);
+
+ if (!message)
+ return;
+
+ let type = Tp.ChannelTextMessageType.NORMAL;
+ let tpMessage = Tp.ClientMessage.new_text(type, message);
+ await channel.send_message_async(tpMessage, 0);
+ } catch (e) {
+ if (message)
+ log(`Failed to send message: ${e.message}`);
+ }
}
- _onLeaveActivated(action, parameter) {
+ async _onLeaveActivated(action, parameter) {
let [id, message] = parameter.deep_unpack();
let request = this._pendingRequests.get(id);
@@ -475,16 +491,14 @@ class TelepathyClient extends Tp.BaseClient {
let reason = Tp.ChannelGroupChangeReason.NONE;
message = message || _('Good Bye');
- room.channel.leave_async(reason, message, (c, res) => {
- try {
- c.leave_finish(res);
- } catch (e) {
- log(`Failed to leave channel: ${e.message}`);
- }
- });
+ try {
+ await room.channel.leave_async(reason, message);
+ } catch (e) {
+ log(`Failed to leave channel: ${e.message}`);
+ }
}
- _onSaveIdentifyPasswordActivated(action, parameter) {
+ async _onSaveIdentifyPasswordActivated(action, parameter) {
let accountPath = parameter.deep_unpack();
let account = this._accountsMonitor.lookupAccount(accountPath);
if (!account)
@@ -494,12 +508,12 @@ class TelepathyClient extends Tp.BaseClient {
if (!data)
return;
- Utils.storeIdentifyPassword(account, data.password, res => {
- if (res)
- this._saveIdentifySettings(account, data);
-
+ try {
+ await Utils.storeIdentifyPassword(account, data.password);
+ this._saveIdentifySettings(account, data);
+ } finally {
this._pendingBotPasswords.delete(account.object_path);
- });
+ }
}
_saveIdentifySettings(account, data) {
diff --git a/src/utils.js b/src/utils.js
index 90c7880c..1d197134 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -31,6 +31,10 @@ const { Gdk, Gio, GLib, Gtk, Secret, Soup, TelepathyGLib: Tp } = imports.gi;
const AppNotifications = imports.appNotifications;
+Gio._promisify(Secret, 'password_store', 'password_store_finish');
+Gio._promisify(Secret, 'password_lookup', 'password_lookup_finish');
+Gio._promisify(Secret, 'password_clear', 'password_clear_finish');
+
const SECRET_SCHEMA_ACCOUNT = new Secret.Schema(
'org.gnome.Polari.Account',
Secret.SchemaFlags.NONE,
@@ -133,51 +137,48 @@ function getTpEventTime() {
return Tp.user_action_time_from_x11(time);
}
-function storeAccountPassword(account, password, callback) {
+function storeAccountPassword(account, password) {
let label = _('Polari server password for %s').format(account.display_name);
- _storePassword(SECRET_SCHEMA_ACCOUNT, label, account, password, callback);
+ _storePassword(SECRET_SCHEMA_ACCOUNT, label, account, password);
}
-function storeIdentifyPassword(account, password, callback) {
+function storeIdentifyPassword(account, password) {
let label = _('Polari NickServ password for %s').format(account.display_name);
- _storePassword(SECRET_SCHEMA_IDENTIFY, label, account, password, callback);
+ _storePassword(SECRET_SCHEMA_IDENTIFY, label, account, password);
}
-function _storePassword(schema, label, account, password, callback) {
+async function _storePassword(schema, label, account, password) {
let attr = { 'account-id': account.get_path_suffix() };
let coll = Secret.COLLECTION_DEFAULT;
- Secret.password_store(schema, attr, coll, label, password, null, (o, res) => {
- try {
- let success = Secret.password_store_finish(res);
- callback(success);
- } catch (e) {
- let name = account.display_name;
- log(`Failed to store password for account ${name}: ${e.message}`);
- callback(false);
- }
- });
+ try {
+ await Secret.password_store(schema, attr, coll, label, password, null);
+ } catch (e) {
+ const name = account.display_name;
+ log(`Failed to store password for account ${name}: ${e.message}`);
+ throw e;
+ }
}
-function lookupAccountPassword(account, callback) {
- _lookupPassword(SECRET_SCHEMA_ACCOUNT, account, callback);
+function lookupAccountPassword(account) {
+ return _lookupPassword(SECRET_SCHEMA_ACCOUNT, account);
}
-function lookupIdentifyPassword(account, callback) {
- _lookupPassword(SECRET_SCHEMA_IDENTIFY, account, callback);
+function lookupIdentifyPassword(account) {
+ return _lookupPassword(SECRET_SCHEMA_IDENTIFY, account);
}
-function _lookupPassword(schema, account, callback) {
+async function _lookupPassword(schema, account) {
let attr = { 'account-id': account.get_path_suffix() };
- Secret.password_lookup(schema, attr, null, (o, res) => {
- try {
- let password = Secret.password_lookup_finish(res);
- callback(password);
- } catch (e) {
- let name = account.display_name;
- log(`Failed to lookup password for account "${name}": ${e.message}`);
- callback(null);
- }
- });
+ let password = null;
+ try {
+ password = await Secret.password_lookup(schema, attr, null);
+ } catch (e) {
+ const name = account.display_name;
+ log(`Failed to lookup password for account "${name}": ${e.message}`);
+ throw e;
+ }
+
+ return password;
}
function clearAccountPassword(account) {
@@ -188,16 +189,15 @@ function clearIdentifyPassword(account) {
_clearPassword(SECRET_SCHEMA_IDENTIFY, account);
}
-function _clearPassword(schema, account) {
+async function _clearPassword(schema, account) {
let attr = { 'account-id': account.get_path_suffix() };
- Secret.password_clear(schema, attr, null, (o, res) => {
- try {
- Secret.password_clear_finish(res);
- } catch (e) {
- const name = account.display_name;
- log(`Failed to clear password for account "${name}": ${e.message}`);
- }
- });
+ try {
+ await Secret.password_clear(schema, attr, null);
+ } catch (e) {
+ const name = account.display_name;
+ log(`Failed to clear password for account "${name}": ${e.message}`);
+ throw e;
+ }
}
// findUrls:
@@ -258,45 +258,39 @@ function updateTerms(terms, str) {
return changed;
}
-function _getGpasteExpire(callback) {
+function _queueSoupMessage(session, message) {
+ return new Promise((resolve, reject) => {
+ session.queue_message(message, () => {
+ const { statusCode } = message;
+ if (statusCode === Soup.KnownStatusCode.OK)
+ resolve(message.responseBody.data);
+ else
+ reject(new Error(`Got unexpected response ${statusCode}`));
+ });
+ });
+}
+
+async function _getGpasteExpire() {
let session = new Soup.Session();
let paramUrl = `${GPASTE_BASEURL}api/json/parameter/expire`;
let message = Soup.form_request_new_from_hash('GET', paramUrl, {});
- session.queue_message(message, () => {
- if (message.status_code !== Soup.KnownStatusCode.OK) {
- callback(false);
- return;
- }
-
- let info = {};
- try {
- info = JSON.parse(message.response_body.data);
- } catch (e) {
- log(e.message);
- }
-
- let values = info.result ? info.result.values : undefined;
- if (!values)
- callback(false);
-
- let day = 24 * 60 * 60;
- _gpasteExpire = values.reduce((acc, val) => {
- return Math.abs(day - acc) < Math.abs(day - val) ? acc : val;
- }, 0).toString();
- callback(true);
- });
+
+ const json = await _queueSoupMessage(session, message);
+ const info = JSON.parse(json);
+
+ const values = info.result?.values;
+ if (!values)
+ throw new Error('Returned data is missing expected fields');
+
+ const day = 24 * 60 * 60;
+ return values.reduce((acc, val) => {
+ return Math.abs(day - acc) < Math.abs(day - val) ? acc : val;
+ }, 0).toString();
}
-function gpaste(text, title, callback) {
- if (_gpasteExpire === undefined) {
- _getGpasteExpire(success => {
- if (success)
- gpaste(text, title, callback);
- else
- callback(null);
- });
- return;
- }
+async function gpaste(text, title) {
+ if (_gpasteExpire === undefined)
+ _gpasteExpire = await _getGpasteExpire();
if (title.length > MAX_PASTE_TITLE_LENGTH)
title = `${title.substr(0, MAX_PASTE_TITLE_LENGTH - 1)}…`;
@@ -311,31 +305,19 @@ function gpaste(text, title, callback) {
let session = new Soup.Session();
let createUrl = `${GPASTE_BASEURL}api/json/create`;
let message = Soup.form_request_new_from_hash('POST', createUrl, params);
- session.queue_message(message, () => {
- if (message.status_code !== Soup.KnownStatusCode.OK) {
- callback(null);
- return;
- }
-
- let info = {};
- try {
- info = JSON.parse(message.response_body.data);
- } catch (e) {
- log(e.message);
- }
- if (info.result && info.result.id)
- callback(`${GPASTE_BASEURL}${info.result.id}`);
- else
- callback(null);
- });
+
+ const json = await _queueSoupMessage(session, message);
+ const info = JSON.parse(json);
+
+ if (!info.result?.id)
+ throw new Error('Paste server did not return a URL');
+ return `${GPASTE_BASEURL}${info.result.id}`;
}
-function imgurPaste(pixbuf, title, callback) {
+async function imgurPaste(pixbuf, title) {
let [success, buffer] = pixbuf.save_to_bufferv('png', [], []);
- if (!success) {
- callback(null);
- return;
- }
+ if (!success)
+ throw new Error('Failed to create image buffer');
let params = {
title,
@@ -348,23 +330,14 @@ function imgurPaste(pixbuf, title, callback) {
let requestHeaders = message.request_headers;
requestHeaders.append('Authorization', `Client-ID ${IMGUR_CLIENT_ID}`);
- session.queue_message(message, () => {
- if (message.status_code !== Soup.KnownStatusCode.OK) {
- callback(null);
- return;
- }
-
- let info = {};
- try {
- info = JSON.parse(message.response_body.data);
- } catch (e) {
- log(e.message);
- }
- if (info.success)
- callback(info.data.link);
- else
- callback(null);
- });
+
+ const json = await _queueSoupMessage(session, message);
+ const info = JSON.parse(json);
+
+ if (!info.success)
+ throw new Error('Failed to upload image to paste service');
+
+ return info.data.link;
}
function formatTimePassed(seconds) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]