Merge pull request #3612 from matrix-org/t3chguy/remove_bluebird_1

Remove Bluebird: phase 1
This commit is contained in:
Michael Telatynski 2019-11-14 14:52:30 +00:00 committed by GitHub
commit 58d15acf19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 196 additions and 161 deletions

View File

@ -59,40 +59,38 @@ export class UploadCanceledError extends Error {}
* and a thumbnail key.
*/
function createThumbnail(element, inputWidth, inputHeight, mimeType) {
const deferred = Promise.defer();
return new Promise((resolve) => {
let targetWidth = inputWidth;
let targetHeight = inputHeight;
if (targetHeight > MAX_HEIGHT) {
targetWidth = Math.floor(targetWidth * (MAX_HEIGHT / targetHeight));
targetHeight = MAX_HEIGHT;
}
if (targetWidth > MAX_WIDTH) {
targetHeight = Math.floor(targetHeight * (MAX_WIDTH / targetWidth));
targetWidth = MAX_WIDTH;
}
let targetWidth = inputWidth;
let targetHeight = inputHeight;
if (targetHeight > MAX_HEIGHT) {
targetWidth = Math.floor(targetWidth * (MAX_HEIGHT / targetHeight));
targetHeight = MAX_HEIGHT;
}
if (targetWidth > MAX_WIDTH) {
targetHeight = Math.floor(targetHeight * (MAX_WIDTH / targetWidth));
targetWidth = MAX_WIDTH;
}
const canvas = document.createElement("canvas");
canvas.width = targetWidth;
canvas.height = targetHeight;
canvas.getContext("2d").drawImage(element, 0, 0, targetWidth, targetHeight);
canvas.toBlob(function(thumbnail) {
deferred.resolve({
info: {
thumbnail_info: {
w: targetWidth,
h: targetHeight,
mimetype: thumbnail.type,
size: thumbnail.size,
const canvas = document.createElement("canvas");
canvas.width = targetWidth;
canvas.height = targetHeight;
canvas.getContext("2d").drawImage(element, 0, 0, targetWidth, targetHeight);
canvas.toBlob(function(thumbnail) {
resolve({
info: {
thumbnail_info: {
w: targetWidth,
h: targetHeight,
mimetype: thumbnail.type,
size: thumbnail.size,
},
w: inputWidth,
h: inputHeight,
},
w: inputWidth,
h: inputHeight,
},
thumbnail: thumbnail,
});
}, mimeType);
return deferred.promise;
thumbnail: thumbnail,
});
}, mimeType);
});
}
/**
@ -179,30 +177,29 @@ function infoForImageFile(matrixClient, roomId, imageFile) {
* @return {Promise} A promise that resolves with the video image element.
*/
function loadVideoElement(videoFile) {
const deferred = Promise.defer();
return new Promise((resolve, reject) => {
// Load the file into an html element
const video = document.createElement("video");
// Load the file into an html element
const video = document.createElement("video");
const reader = new FileReader();
const reader = new FileReader();
reader.onload = function(e) {
video.src = e.target.result;
reader.onload = function(e) {
video.src = e.target.result;
// Once ready, returns its size
// Wait until we have enough data to thumbnail the first frame.
video.onloadeddata = function() {
deferred.resolve(video);
// Once ready, returns its size
// Wait until we have enough data to thumbnail the first frame.
video.onloadeddata = function() {
resolve(video);
};
video.onerror = function(e) {
reject(e);
};
};
video.onerror = function(e) {
deferred.reject(e);
reader.onerror = function(e) {
reject(e);
};
};
reader.onerror = function(e) {
deferred.reject(e);
};
reader.readAsDataURL(videoFile);
return deferred.promise;
reader.readAsDataURL(videoFile);
});
}
/**
@ -236,16 +233,16 @@ function infoForVideoFile(matrixClient, roomId, videoFile) {
* is read.
*/
function readFileAsArrayBuffer(file) {
const deferred = Promise.defer();
const reader = new FileReader();
reader.onload = function(e) {
deferred.resolve(e.target.result);
};
reader.onerror = function(e) {
deferred.reject(e);
};
reader.readAsArrayBuffer(file);
return deferred.promise;
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function(e) {
resolve(e.target.result);
};
reader.onerror = function(e) {
reject(e);
};
reader.readAsArrayBuffer(file);
});
}
/**
@ -461,33 +458,34 @@ export default class ContentMessages {
content.info.mimetype = file.type;
}
const def = Promise.defer();
if (file.type.indexOf('image/') == 0) {
content.msgtype = 'm.image';
infoForImageFile(matrixClient, roomId, file).then((imageInfo)=>{
extend(content.info, imageInfo);
def.resolve();
}, (error)=>{
console.error(error);
const prom = new Promise((resolve) => {
if (file.type.indexOf('image/') == 0) {
content.msgtype = 'm.image';
infoForImageFile(matrixClient, roomId, file).then((imageInfo)=>{
extend(content.info, imageInfo);
resolve();
}, (error)=>{
console.error(error);
content.msgtype = 'm.file';
resolve();
});
} else if (file.type.indexOf('audio/') == 0) {
content.msgtype = 'm.audio';
resolve();
} else if (file.type.indexOf('video/') == 0) {
content.msgtype = 'm.video';
infoForVideoFile(matrixClient, roomId, file).then((videoInfo)=>{
extend(content.info, videoInfo);
resolve();
}, (error)=>{
content.msgtype = 'm.file';
resolve();
});
} else {
content.msgtype = 'm.file';
def.resolve();
});
} else if (file.type.indexOf('audio/') == 0) {
content.msgtype = 'm.audio';
def.resolve();
} else if (file.type.indexOf('video/') == 0) {
content.msgtype = 'm.video';
infoForVideoFile(matrixClient, roomId, file).then((videoInfo)=>{
extend(content.info, videoInfo);
def.resolve();
}, (error)=>{
content.msgtype = 'm.file';
def.resolve();
});
} else {
content.msgtype = 'm.file';
def.resolve();
}
resolve();
}
});
const upload = {
fileName: file.name || 'Attachment',
@ -509,7 +507,7 @@ export default class ContentMessages {
dis.dispatch({action: 'upload_progress', upload: upload});
}
return def.promise.then(function() {
return prom.then(function() {
// XXX: upload.promise must be the promise that
// is returned by uploadFile as it has an abort()
// method hacked onto it.

View File

@ -313,18 +313,14 @@ async function _restoreFromLocalStorage(opts) {
function _handleLoadSessionFailure(e) {
console.error("Unable to load session", e);
const def = Promise.defer();
const SessionRestoreErrorDialog =
sdk.getComponent('views.dialogs.SessionRestoreErrorDialog');
Modal.createTrackedDialog('Session Restore Error', '', SessionRestoreErrorDialog, {
const modal = Modal.createTrackedDialog('Session Restore Error', '', SessionRestoreErrorDialog, {
error: e.message,
onFinished: (success) => {
def.resolve(success);
},
});
return def.promise.then((success) => {
return modal.finished.then(([success]) => {
if (success) {
// user clicked continue.
_clearStorage();

View File

@ -24,6 +24,7 @@ import sdk from './index';
import dis from './dispatcher';
import { _t } from './languageHandler';
import Promise from "bluebird";
import {defer} from "./utils/promise";
const DIALOG_CONTAINER_ID = "mx_Dialog_Container";
const STATIC_DIALOG_CONTAINER_ID = "mx_Dialog_StaticContainer";
@ -202,7 +203,7 @@ class ModalManager {
}
_getCloseFn(modal, props) {
const deferred = Promise.defer();
const deferred = defer();
return [(...args) => {
deferred.resolve(args);
if (props && props.onFinished) props.onFinished.apply(null, args);

View File

@ -38,6 +38,7 @@ import FlairStore from '../../stores/FlairStore';
import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
import {makeGroupPermalink, makeUserPermalink} from "../../utils/permalinks/Permalinks";
import {Group} from "matrix-js-sdk";
import {sleep} from "../../utils/promise";
const LONG_DESC_PLACEHOLDER = _td(
`<h1>HTML for your community's page</h1>
@ -692,7 +693,7 @@ export default createReactClass({
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
await sleep(500);
GroupStore.acceptGroupInvite(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
@ -711,7 +712,7 @@ export default createReactClass({
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
await sleep(500);
GroupStore.leaveGroup(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
@ -735,7 +736,7 @@ export default createReactClass({
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
await sleep(500);
GroupStore.joinGroup(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
@ -787,7 +788,7 @@ export default createReactClass({
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
await sleep(500);
GroupStore.leaveGroup(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync

View File

@ -61,6 +61,7 @@ import DMRoomMap from '../../utils/DMRoomMap';
import { countRoomsWithNotif } from '../../RoomNotifs';
import { setTheme } from "../../theme";
import { storeRoomAliasInCache } from '../../RoomAliasCache';
import { defer } from "../../utils/promise";
// Disable warnings for now: we use deprecated bluebird functions
// and need to migrate, but they spam the console with warnings.
@ -237,7 +238,7 @@ export default createReactClass({
// Used by _viewRoom before getting state from sync
this.firstSyncComplete = false;
this.firstSyncPromise = Promise.defer();
this.firstSyncPromise = defer();
if (this.props.config.sync_timeline_limit) {
MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit;
@ -1267,7 +1268,7 @@ export default createReactClass({
// since we're about to start the client and therefore about
// to do the first sync
this.firstSyncComplete = false;
this.firstSyncPromise = Promise.defer();
this.firstSyncPromise = defer();
const cli = MatrixClientPeg.get();
const IncomingSasDialog = sdk.getComponent('views.dialogs.IncomingSasDialog');

View File

@ -17,7 +17,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import Promise from 'bluebird';
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
@ -32,6 +31,7 @@ import * as RoomNotifs from '../../../RoomNotifs';
import Modal from '../../../Modal';
import RoomListActions from '../../../actions/RoomListActions';
import RoomViewStore from '../../../stores/RoomViewStore';
import {sleep} from "../../../utils/promise";
module.exports = createReactClass({
displayName: 'RoomTileContextMenu',
@ -62,7 +62,7 @@ module.exports = createReactClass({
_toggleTag: function(tagNameOn, tagNameOff) {
if (!MatrixClientPeg.get().isGuest()) {
Promise.delay(500).then(() => {
sleep(500).then(() => {
dis.dispatch(RoomListActions.tagRoom(
MatrixClientPeg.get(),
this.props.room,
@ -119,7 +119,7 @@ module.exports = createReactClass({
Rooms.guessAndSetDMRoom(
this.props.room, newIsDirectMessage,
).delay(500).finally(() => {
).then(sleep(500)).finally(() => {
// Close the context menu
if (this.props.onFinished) {
this.props.onFinished();
@ -193,7 +193,7 @@ module.exports = createReactClass({
RoomNotifs.setRoomNotifsState(roomId, newState).done(() => {
// delay slightly so that the user can see their state change
// before closing the menu
return Promise.delay(500).then(() => {
return sleep(500).then(() => {
if (this._unmounted) return;
// Close the context menu
if (this.props.onFinished) {

View File

@ -25,13 +25,13 @@ import { _t, _td } from '../../../languageHandler';
import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
import dis from '../../../dispatcher';
import Promise from 'bluebird';
import { addressTypes, getAddressType } from '../../../UserAddress.js';
import GroupStore from '../../../stores/GroupStore';
import * as Email from '../../../email';
import IdentityAuthClient from '../../../IdentityAuthClient';
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from '../../../utils/IdentityServerUtils';
import { abbreviateUrl } from '../../../utils/UrlUtils';
import {sleep} from "../../../utils/promise";
const TRUNCATE_QUERY_LIST = 40;
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
@ -533,7 +533,7 @@ module.exports = createReactClass({
};
// wait a bit to let the user finish typing
await Promise.delay(500);
await sleep(500);
if (cancelled) return null;
try {

View File

@ -26,6 +26,7 @@ import { Room } from 'matrix-js-sdk';
import SettingsStore from "../../../settings/SettingsStore";
import Autocompleter from '../../../autocomplete/Autocompleter';
import {sleep} from "../../../utils/promise";
const COMPOSER_SELECTED = 0;
@ -105,13 +106,11 @@ export default class Autocomplete extends React.Component {
autocompleteDelay = 0;
}
const deferred = Promise.defer();
this.debounceCompletionsRequest = setTimeout(() => {
this.processQuery(query, selection).then(() => {
deferred.resolve();
});
}, autocompleteDelay);
return deferred.promise;
return new Promise((resolve) => {
this.debounceCompletionsRequest = setTimeout(() => {
resolve(this.processQuery(query, selection));
}, autocompleteDelay);
});
}
processQuery(query, selection) {
@ -197,16 +196,16 @@ export default class Autocomplete extends React.Component {
}
forceComplete() {
const done = Promise.defer();
this.setState({
forceComplete: true,
hide: false,
}, () => {
this.complete(this.props.query, this.props.selection).then(() => {
done.resolve(this.countCompletions());
return new Promise((resolve) => {
this.setState({
forceComplete: true,
hide: false,
}, () => {
this.complete(this.props.query, this.props.selection).then(() => {
resolve(this.countCompletions());
});
});
});
return done.promise;
}
onCompletionClicked(selectionOffset: number): boolean {

View File

@ -178,17 +178,12 @@ module.exports = createReactClass({
},
_optionallySetEmail: function() {
const deferred = Promise.defer();
// Ask for an email otherwise the user has no way to reset their password
const SetEmailDialog = sdk.getComponent("dialogs.SetEmailDialog");
Modal.createTrackedDialog('Do you want to set an email address?', '', SetEmailDialog, {
const modal = Modal.createTrackedDialog('Do you want to set an email address?', '', SetEmailDialog, {
title: _t('Do you want to set an email address?'),
onFinished: (confirmed) => {
// ignore confirmed, setting an email is optional
deferred.resolve(confirmed);
},
});
return deferred.promise;
return modal.finished.then(([confirmed]) => confirmed);
},
_onExportE2eKeysClicked: function() {

View File

@ -26,6 +26,7 @@ import { getThreepidsWithBindStatus } from '../../../boundThreepids';
import IdentityAuthClient from "../../../IdentityAuthClient";
import {abbreviateUrl, unabbreviateUrl} from "../../../utils/UrlUtils";
import { getDefaultIdentityServerUrl, doesIdentityServerHaveTerms } from '../../../utils/IdentityServerUtils';
import {timeout} from "../../../utils/promise";
// We'll wait up to this long when checking for 3PID bindings on the IS.
const REACHABILITY_TIMEOUT = 10000; // ms
@ -245,14 +246,11 @@ export default class SetIdServer extends React.Component {
let threepids = [];
let currentServerReachable = true;
try {
threepids = await Promise.race([
threepids = await timeout(
getThreepidsWithBindStatus(MatrixClientPeg.get()),
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("Timeout attempting to reach identity server"));
}, REACHABILITY_TIMEOUT);
}),
]);
Promise.reject(new Error("Timeout attempting to reach identity server")),
REACHABILITY_TIMEOUT,
);
} catch (e) {
currentServerReachable = false;
console.warn(

View File

@ -22,9 +22,9 @@ import MatrixClientPeg from "../../../../../MatrixClientPeg";
import * as FormattingUtils from "../../../../../utils/FormattingUtils";
import AccessibleButton from "../../../elements/AccessibleButton";
import Analytics from "../../../../../Analytics";
import Promise from "bluebird";
import Modal from "../../../../../Modal";
import sdk from "../../../../..";
import {sleep} from "../../../../../utils/promise";
export class IgnoredUser extends React.Component {
static propTypes = {
@ -129,7 +129,7 @@ export default class SecurityUserSettingsTab extends React.Component {
if (e.errcode === "M_LIMIT_EXCEEDED") {
// Add a delay between each invite change in order to avoid rate
// limiting by the server.
await Promise.delay(e.retry_after_ms || 2500);
await sleep(e.retry_after_ms || 2500);
// Redo last action
i--;

View File

@ -105,26 +105,22 @@ export default async function sendBugReport(bugReportEndpoint, opts) {
}
function _submitReport(endpoint, body, progressCallback) {
const deferred = Promise.defer();
const req = new XMLHttpRequest();
req.open("POST", endpoint);
req.timeout = 5 * 60 * 1000;
req.onreadystatechange = function() {
if (req.readyState === XMLHttpRequest.LOADING) {
progressCallback(_t("Waiting for response from server"));
} else if (req.readyState === XMLHttpRequest.DONE) {
on_done();
}
};
req.send(body);
return deferred.promise;
function on_done() {
if (req.status < 200 || req.status >= 400) {
deferred.reject(new Error(`HTTP ${req.status}`));
return;
}
deferred.resolve();
}
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open("POST", endpoint);
req.timeout = 5 * 60 * 1000;
req.onreadystatechange = function() {
if (req.readyState === XMLHttpRequest.LOADING) {
progressCallback(_t("Waiting for response from server"));
} else if (req.readyState === XMLHttpRequest.DONE) {
// on done
if (req.status < 200 || req.status >= 400) {
reject(new Error(`HTTP ${req.status}`));
return;
}
resolve();
}
};
req.send(body);
});
}

View File

@ -24,6 +24,7 @@ import {_t} from "../languageHandler";
import sdk from "../index";
import Modal from "../Modal";
import SettingsStore from "../settings/SettingsStore";
import {defer} from "./promise";
/**
* Invites multiple addresses to a room or group, handling rate limiting from the server
@ -71,7 +72,7 @@ export default class MultiInviter {
};
}
}
this.deferred = Promise.defer();
this.deferred = defer();
this._inviteMore(0);
return this.deferred.promise;

49
src/utils/promise.js Normal file
View File

@ -0,0 +1,49 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This is only here to allow access to methods like done for the time being
import Promise from "bluebird";
// @flow
// Returns a promise which resolves with a given value after the given number of ms
export const sleep = (ms: number, value: any): Promise => new Promise((resolve => { setTimeout(resolve, ms, value); }));
// Returns a promise which resolves when the input promise resolves with its value
// or when the timeout of ms is reached with the value of given timeoutValue
export async function timeout(promise: Promise, timeoutValue: any, ms: number): Promise {
const timeoutPromise = new Promise((resolve) => {
const timeoutId = setTimeout(resolve, ms, timeoutValue);
promise.then(() => {
clearTimeout(timeoutId);
});
});
return Promise.race([promise, timeoutPromise]);
}
// Returns a Deferred
export function defer(): {resolve: () => {}, reject: () => {}, promise: Promise} {
let resolve;
let reject;
const promise = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
return {resolve, reject, promise};
}