From 10decf95f633a76d60898589bb4fa4a16eea8497 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Thu, 13 Jul 2017 00:48:31 +0100
Subject: [PATCH 1/7] replace imports of `q` with bluebird

update `package.json`

```
find src test -name '*.js' |
    xargs perl -i -pe 'if (/require\(.[qQ].\)/) { $_ = "import Promise from '\''bluebird'\'';\n"; }'

find src test -name '*.js' |
    xargs perl -i -pe 'if (/import [qQ] /) { $_ = "import Promise from '\''bluebird'\'';\n"; }'
```
---
 package.json                                              | 2 +-
 src/VectorConferenceHandler.js                            | 2 +-
 src/components/structures/RoomDirectory.js                | 2 +-
 src/components/views/context_menus/RoomTileContextMenu.js | 2 +-
 src/components/views/settings/Notifications.js            | 2 +-
 src/vector/index.js                                       | 2 +-
 src/vector/platform/ElectronPlatform.js                   | 2 +-
 src/vector/platform/WebPlatform.js                        | 2 +-
 src/vector/rageshake.js                                   | 2 +-
 src/vector/submit-rageshake.js                            | 2 +-
 test/app-tests/joining.js                                 | 2 +-
 test/app-tests/loading.js                                 | 2 +-
 test/test-utils.js                                        | 2 +-
 13 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/package.json b/package.json
index 83d9bd16..06cd786b 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,7 @@
   "dependencies": {
     "babel-polyfill": "^6.5.0",
     "babel-runtime": "^6.11.6",
+    "bluebird": "^3.5.0",
     "browser-request": "^0.3.3",
     "classnames": "^2.1.2",
     "draft-js": "^0.8.1",
@@ -69,7 +70,6 @@
     "matrix-react-sdk": "0.9.7",
     "modernizr": "^3.1.0",
     "pako": "^1.0.5",
-    "q": "^1.4.1",
     "react": "^15.4.0",
     "react-dnd": "^2.1.4",
     "react-dnd-html5-backend": "^2.1.2",
diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js
index f34a7b73..807fbba0 100644
--- a/src/VectorConferenceHandler.js
+++ b/src/VectorConferenceHandler.js
@@ -16,7 +16,7 @@ limitations under the License.
 
 "use strict";
 
-var q = require("q");
+import Promise from 'bluebird';
 var Matrix = require("matrix-js-sdk");
 var Room = Matrix.Room;
 var CallHandler = require('matrix-react-sdk/lib/CallHandler');
diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js
index e7d68c39..f487bbd1 100644
--- a/src/components/structures/RoomDirectory.js
+++ b/src/components/structures/RoomDirectory.js
@@ -28,7 +28,7 @@ var linkify = require('linkifyjs');
 var linkifyString = require('linkifyjs/string');
 var linkifyMatrix = require('matrix-react-sdk/lib/linkify-matrix');
 var sanitizeHtml = require('sanitize-html');
-var q = require('q');
+import Promise from 'bluebird';
 
 import { _t } from 'matrix-react-sdk/lib/languageHandler';
 
diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js
index a7b19689..f4c369e0 100644
--- a/src/components/views/context_menus/RoomTileContextMenu.js
+++ b/src/components/views/context_menus/RoomTileContextMenu.js
@@ -17,7 +17,7 @@ limitations under the License.
 
 'use strict';
 
-import q from 'q';
+import Promise from 'bluebird';
 import React from 'react';
 import classNames from 'classnames';
 import sdk from 'matrix-react-sdk';
diff --git a/src/components/views/settings/Notifications.js b/src/components/views/settings/Notifications.js
index 1b8de52d..12f77ba7 100644
--- a/src/components/views/settings/Notifications.js
+++ b/src/components/views/settings/Notifications.js
@@ -17,7 +17,7 @@ limitations under the License.
 'use strict';
 var React = require('react');
 import { _t, _tJsx } from 'matrix-react-sdk/lib/languageHandler';
-var q = require("q");
+import Promise from 'bluebird';
 var sdk = require('matrix-react-sdk');
 var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
 var UserSettingsStore = require('matrix-react-sdk/lib/UserSettingsStore');
diff --git a/src/vector/index.js b/src/vector/index.js
index 81d329c0..fa640c59 100644
--- a/src/vector/index.js
+++ b/src/vector/index.js
@@ -65,7 +65,7 @@ var sdk = require("matrix-react-sdk");
 const PlatformPeg = require("matrix-react-sdk/lib/PlatformPeg");
 sdk.loadSkin(require('../component-index'));
 var VectorConferenceHandler = require('../VectorConferenceHandler');
-var q = require('q');
+import Promise from 'bluebird';
 var request = require('browser-request');
 import * as UserSettingsStore from 'matrix-react-sdk/lib/UserSettingsStore';
 import * as languageHandler from 'matrix-react-sdk/lib/languageHandler';
diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js
index 4f0ffae7..809abccd 100644
--- a/src/vector/platform/ElectronPlatform.js
+++ b/src/vector/platform/ElectronPlatform.js
@@ -20,7 +20,7 @@ limitations under the License.
 import VectorBasePlatform, {updateCheckStatusEnum} from './VectorBasePlatform';
 import dis from 'matrix-react-sdk/lib/dispatcher';
 import { _t } from 'matrix-react-sdk/lib/languageHandler';
-import q from 'q';
+import Promise from 'bluebird';
 import {remote, ipcRenderer} from 'electron';
 import rageshake from '../rageshake';
 
diff --git a/src/vector/platform/WebPlatform.js b/src/vector/platform/WebPlatform.js
index ae1e54b3..5946a57a 100644
--- a/src/vector/platform/WebPlatform.js
+++ b/src/vector/platform/WebPlatform.js
@@ -21,7 +21,7 @@ import VectorBasePlatform, {updateCheckStatusEnum} from './VectorBasePlatform';
 import request from 'browser-request';
 import dis from 'matrix-react-sdk/lib/dispatcher.js';
 import { _t } from 'matrix-react-sdk/lib/languageHandler';
-import q from 'q';
+import Promise from 'bluebird';
 
 import url from 'url';
 import UAParser from 'ua-parser-js';
diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js
index 07726f68..05f00d65 100644
--- a/src/vector/rageshake.js
+++ b/src/vector/rageshake.js
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-import q from "q";
+import Promise from 'bluebird';
 
 // This module contains all the code needed to log the console, persist it to
 // disk and submit bug reports. Rationale is as follows:
diff --git a/src/vector/submit-rageshake.js b/src/vector/submit-rageshake.js
index c6c551c6..04fe69f6 100644
--- a/src/vector/submit-rageshake.js
+++ b/src/vector/submit-rageshake.js
@@ -15,7 +15,7 @@ limitations under the License.
 */
 
 import pako from 'pako';
-import q from "q";
+import Promise from 'bluebird';
 
 import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
 import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
diff --git a/test/app-tests/joining.js b/test/app-tests/joining.js
index 11fd3d48..2f7c6070 100644
--- a/test/app-tests/joining.js
+++ b/test/app-tests/joining.js
@@ -33,7 +33,7 @@ var React = require('react');
 var ReactDOM = require('react-dom');
 var ReactTestUtils = require('react-addons-test-utils');
 var expect = require('expect');
-var q = require('q');
+import Promise from 'bluebird';
 
 var test_utils = require('../test-utils');
 var MockHttpBackend = require('matrix-mock-request');
diff --git a/test/app-tests/loading.js b/test/app-tests/loading.js
index d01836a3..d517c05f 100644
--- a/test/app-tests/loading.js
+++ b/test/app-tests/loading.js
@@ -22,7 +22,7 @@ import React from 'react';
 import ReactDOM from 'react-dom';
 import ReactTestUtils from 'react-addons-test-utils';
 import expect from 'expect';
-import q from 'q';
+import Promise from 'bluebird';
 
 import jssdk from 'matrix-js-sdk';
 
diff --git a/test/test-utils.js b/test/test-utils.js
index cda9a017..88ac9d99 100644
--- a/test/test-utils.js
+++ b/test/test-utils.js
@@ -1,6 +1,6 @@
 "use strict";
 
-var q = require('q');
+import Promise from 'bluebird';
 
 /**
  * Perform common actions before each test case, e.g. printing the test case

From b29b4a959b8ea7f793e0d6494fb011a340ac7463 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Thu, 13 Jul 2017 00:32:07 +0100
Subject: [PATCH 2/7] q(...) -> Promise.resolve

```
find src test -name '*.js' | xargs perl -i -pe 's/\b[qQ]\(/Promise.resolve(/'
```
---
 src/VectorConferenceHandler.js             | 4 ++--
 src/components/structures/RoomDirectory.js | 4 ++--
 src/vector/platform/ElectronPlatform.js    | 4 ++--
 src/vector/platform/WebPlatform.js         | 2 +-
 test/app-tests/joining.js                  | 2 +-
 test/app-tests/loading.js                  | 4 ++--
 6 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js
index 807fbba0..de1aa233 100644
--- a/src/VectorConferenceHandler.js
+++ b/src/VectorConferenceHandler.js
@@ -57,7 +57,7 @@ ConferenceCall.prototype._joinConferenceUser = function() {
     }
     var member = groupRoom.getMember(this.confUserId);
     if (member && member.membership === "join") {
-        return q();
+        return Promise.resolve();
     }
     return this.client.invite(this.groupRoomId, this.confUserId);
 };
@@ -75,7 +75,7 @@ ConferenceCall.prototype._getConferenceUserRoom = function() {
         }
     }
     if (confRoom) {
-        return q(confRoom);
+        return Promise.resolve(confRoom);
     }
     return this.client.createRoom({
         preset: "private_chat",
diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js
index f487bbd1..ea3aa5a2 100644
--- a/src/components/structures/RoomDirectory.js
+++ b/src/components/structures/RoomDirectory.js
@@ -117,7 +117,7 @@ module.exports = React.createClass({
     },
 
     getMoreRooms: function() {
-        if (!MatrixClientPeg.get()) return q();
+        if (!MatrixClientPeg.get()) return Promise.resolve();
 
         const my_filter_string = this.state.filterString;
         const my_server = this.state.roomServer;
@@ -266,7 +266,7 @@ module.exports = React.createClass({
     },
 
     onFillRequest: function(backwards) {
-        if (backwards || !this.nextBatch) return q(false);
+        if (backwards || !this.nextBatch) return Promise.resolve(false);
 
         return this.getMoreRooms();
     },
diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js
index 809abccd..8e97f238 100644
--- a/src/vector/platform/ElectronPlatform.js
+++ b/src/vector/platform/ElectronPlatform.js
@@ -173,7 +173,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
     }
 
     getAppVersion(): Promise<string> {
-        return q(remote.app.getVersion());
+        return Promise.resolve(remote.app.getVersion());
     }
 
     startUpdateCheck() {
@@ -201,7 +201,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
     isElectron(): boolean { return true; }
 
     requestNotificationPermission(): Promise<string> {
-        return q('granted');
+        return Promise.resolve('granted');
     }
 
     reload() {
diff --git a/src/vector/platform/WebPlatform.js b/src/vector/platform/WebPlatform.js
index 5946a57a..19187632 100644
--- a/src/vector/platform/WebPlatform.js
+++ b/src/vector/platform/WebPlatform.js
@@ -132,7 +132,7 @@ export default class WebPlatform extends VectorBasePlatform {
 
     getAppVersion(): Promise<string> {
         if (this.runningVersion !== null) {
-            return q(this.runningVersion);
+            return Promise.resolve(this.runningVersion);
         }
         return this._getVersion();
     }
diff --git a/test/app-tests/joining.js b/test/app-tests/joining.js
index 2f7c6070..6788b534 100644
--- a/test/app-tests/joining.js
+++ b/test/app-tests/joining.js
@@ -106,7 +106,7 @@ describe('joining a room', function () {
                 .respond(200, {});
             function awaitSync(attempts) {
                 if (syncDone) {
-                    return q();
+                    return Promise.resolve();
                 }
                 if (!attempts) {
                     throw new Error("Gave up waiting for /sync")
diff --git a/test/app-tests/loading.js b/test/app-tests/loading.js
index d517c05f..e3291b0b 100644
--- a/test/app-tests/loading.js
+++ b/test/app-tests/loading.js
@@ -683,7 +683,7 @@ function awaitSyncingSpinner(matrixChat, retryLimit, retryCount) {
 
     // state looks good, check the rendered output
     assertAtSyncingSpinner(matrixChat);
-    return q();
+    return Promise.resolve();
 }
 
 function assertAtSyncingSpinner(matrixChat) {
@@ -721,5 +721,5 @@ function awaitRoomView(matrixChat, retryLimit, retryCount) {
     // state looks good, check the rendered output
     ReactTestUtils.findRenderedComponentWithType(
         matrixChat, sdk.getComponent('structures.RoomView'));
-    return q();
+    return Promise.resolve();
 }

From d5b550f89acab6c7016400174f19ac7cadc17208 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Thu, 13 Jul 2017 00:33:28 +0100
Subject: [PATCH 3/7] replace q method calls with bluebird ones

```
find src test -name '*.js' |
    xargs perl -i -pe 's/q\.(all|defer|reject|delay|try|isFulfilled)\(/Promise.$1(/'
```
---
 src/VectorConferenceHandler.js                |  2 +-
 .../context_menus/RoomTileContextMenu.js      |  4 +--
 .../views/settings/Notifications.js           | 10 +++---
 src/vector/index.js                           |  2 +-
 src/vector/platform/WebPlatform.js            |  4 +--
 src/vector/submit-rageshake.js                |  2 +-
 test/app-tests/joining.js                     | 12 +++----
 test/app-tests/loading.js                     | 34 +++++++++----------
 8 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js
index de1aa233..933f5993 100644
--- a/src/VectorConferenceHandler.js
+++ b/src/VectorConferenceHandler.js
@@ -53,7 +53,7 @@ ConferenceCall.prototype._joinConferenceUser = function() {
     // Make sure the conference user is in the group chat room
     var groupRoom = this.client.getRoom(this.groupRoomId);
     if (!groupRoom) {
-        return q.reject("Bad group room ID");
+        return Promise.reject("Bad group room ID");
     }
     var member = groupRoom.getMember(this.confUserId);
     if (member && member.membership === "join") {
diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js
index f4c369e0..4d08e833 100644
--- a/src/components/views/context_menus/RoomTileContextMenu.js
+++ b/src/components/views/context_menus/RoomTileContextMenu.js
@@ -61,7 +61,7 @@ module.exports = React.createClass({
         const roomId = this.props.room.roomId;
         var cli = MatrixClientPeg.get();
         if (!cli.isGuest()) {
-            q.delay(500).then(function() {
+            Promise.delay(500).then(function() {
                 if (tagNameOff !== null && tagNameOff !== undefined) {
                     cli.deleteRoomTag(roomId, tagNameOff).finally(function() {
                         // Close the context menu
@@ -212,7 +212,7 @@ module.exports = React.createClass({
         RoomNotifs.setRoomNotifsState(this.props.room.roomId, newState).done(() => {
             // delay slightly so that the user can see their state change
             // before closing the menu
-            return q.delay(500).then(() => {
+            return Promise.delay(500).then(() => {
                 if (this._unmounted) return;
                 // Close the context menu
                 if (this.props.onFinished) {
diff --git a/src/components/views/settings/Notifications.js b/src/components/views/settings/Notifications.js
index 12f77ba7..e8ee5434 100644
--- a/src/components/views/settings/Notifications.js
+++ b/src/components/views/settings/Notifications.js
@@ -236,7 +236,7 @@ module.exports = React.createClass({
                 }
             }
 
-            q.all(deferreds).done(function() {
+            Promise.all(deferreds).done(function() {
                 self._refreshFromServer();
             }, function(error) {
                 var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@@ -306,7 +306,7 @@ module.exports = React.createClass({
             }
         }
 
-        q.all(deferreds).done(function(resps) {
+        Promise.all(deferreds).done(function(resps) {
             self._refreshFromServer();
         }, function(error) {
             var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@@ -361,7 +361,7 @@ module.exports = React.createClass({
         }
 
         // Then, add the new ones
-        q.all(removeDeferreds).done(function(resps) {
+        Promise.all(removeDeferreds).done(function(resps) {
             var deferreds = [];
 
             var pushRuleVectorStateKind = self.state.vectorContentRules.vectorState;
@@ -399,7 +399,7 @@ module.exports = React.createClass({
                 }
             }
 
-            q.all(deferreds).done(function(resps) {
+            Promise.all(deferreds).done(function(resps) {
                 self._refreshFromServer();
             }, onError);
         }, onError);
@@ -594,7 +594,7 @@ module.exports = React.createClass({
             self.setState({pushers: resp.pushers});
         });
 
-        q.all([pushRulesPromise, pushersPromise]).then(function() {
+        Promise.all([pushRulesPromise, pushersPromise]).then(function() {
             self.setState({
                 phase: self.phases.DISPLAY
             });
diff --git a/src/vector/index.js b/src/vector/index.js
index fa640c59..0cf8563f 100644
--- a/src/vector/index.js
+++ b/src/vector/index.js
@@ -188,7 +188,7 @@ var makeRegistrationUrl = function(params) {
 window.addEventListener('hashchange', onHashChange);
 
 function getConfig() {
-    let deferred = q.defer();
+    let deferred = Promise.defer();
 
     request(
         { method: "GET", url: "config.json" },
diff --git a/src/vector/platform/WebPlatform.js b/src/vector/platform/WebPlatform.js
index 19187632..b88ee93f 100644
--- a/src/vector/platform/WebPlatform.js
+++ b/src/vector/platform/WebPlatform.js
@@ -68,7 +68,7 @@ export default class WebPlatform extends VectorBasePlatform {
         // annoyingly, the latest spec says this returns a
         // promise, but this is only supported in Chrome 46
         // and Firefox 47, so adapt the callback API.
-        const defer = q.defer();
+        const defer = Promise.defer();
         global.Notification.requestPermission((result) => {
             defer.resolve(result);
         });
@@ -103,7 +103,7 @@ export default class WebPlatform extends VectorBasePlatform {
     }
 
     _getVersion(): Promise<string> {
-        const deferred = q.defer();
+        const deferred = Promise.defer();
 
         // We add a cachebuster to the request to make sure that we know about
         // the most recent version on the origin server. That might not
diff --git a/src/vector/submit-rageshake.js b/src/vector/submit-rageshake.js
index 04fe69f6..b66ec9ab 100644
--- a/src/vector/submit-rageshake.js
+++ b/src/vector/submit-rageshake.js
@@ -100,7 +100,7 @@ export default async function sendBugReport(bugReportEndpoint, opts) {
 }
 
 function _submitReport(endpoint, body, progressCallback) {
-    const deferred = q.defer();
+    const deferred = Promise.defer();
 
     const req = new XMLHttpRequest();
     req.open("POST", endpoint);
diff --git a/test/app-tests/joining.js b/test/app-tests/joining.js
index 6788b534..34acd26a 100644
--- a/test/app-tests/joining.js
+++ b/test/app-tests/joining.js
@@ -118,7 +118,7 @@ describe('joining a room', function () {
                 // wait for the directory requests
                 httpBackend.when('POST', '/publicRooms').respond(200, {chunk: []});
                 httpBackend.when('GET', '/thirdparty/protocols').respond(200, {});
-                return q.all([
+                return Promise.all([
                     httpBackend.flush('/thirdparty/protocols'),
                     httpBackend.flush('/publicRooms'),
                 ]);
@@ -139,14 +139,14 @@ describe('joining a room', function () {
                 httpBackend.when('GET', '/rooms/'+encodeURIComponent(ROOM_ID)+"/initialSync")
                     .respond(401, {errcode: 'M_GUEST_ACCESS_FORBIDDEN'});
 
-                return q.all([
+                return Promise.all([
                     httpBackend.flush('/directory/room/'+encodeURIComponent(ROOM_ALIAS), 1, 200),
                     httpBackend.flush('/rooms/'+encodeURIComponent(ROOM_ID)+"/initialSync", 1, 200),
                 ]);
             }).then(() => {
                 httpBackend.verifyNoOutstandingExpectation();
 
-                return q.delay(1);
+                return Promise.delay(1);
             }).then(() => {
                 // we should now have a roomview, with a preview bar
                 roomView = ReactTestUtils.findRenderedComponentWithType(
@@ -164,14 +164,14 @@ describe('joining a room', function () {
                     .respond(200, {room_id: ROOM_ID});
             }).then(() => {
                 // wait for the join request to be made
-                return q.delay(1);
+                return Promise.delay(1);
             }).then(() => {
                 // and again, because the state update has to go to the store and
                 // then one dispatch within the store, then to the view
                 // XXX: This is *super flaky*: a better way would be to declare
                 // that we expect a certain state transition to happen, then wait
                 // for that transition to occur.
-                return q.delay(1);
+                return Promise.delay(1);
             }).then(() => {
                 // the roomview should now be loading
                 expect(roomView.state.room).toBe(null);
@@ -186,7 +186,7 @@ describe('joining a room', function () {
             }).then(() => {
                 httpBackend.verifyNoOutstandingExpectation();
 
-                return q.delay(1);
+                return Promise.delay(1);
             }).then(() => {
                 // We've joined, expect this to false
                 expect(roomView.state.joining).toBe(false);
diff --git a/test/app-tests/loading.js b/test/app-tests/loading.js
index e3291b0b..558cbc5e 100644
--- a/test/app-tests/loading.js
+++ b/test/app-tests/loading.js
@@ -103,7 +103,7 @@ describe('loading:', function () {
             toString: function() { return this.search + this.hash; },
         };
 
-        let tokenLoginCompleteDefer = q.defer();
+        let tokenLoginCompleteDefer = Promise.defer();
         tokenLoginCompletePromise = tokenLoginCompleteDefer.promise;
 
         function onNewScreen(screen) {
@@ -171,7 +171,7 @@ describe('loading:', function () {
         it('gives a login panel by default', function (done) {
             loadApp();
 
-            q.delay(1).then(() => {
+            Promise.delay(1).then(() => {
                 // at this point, we're trying to do a guest registration;
                 // we expect a spinner
                 assertAtLoadingSpinner(matrixChat);
@@ -183,7 +183,7 @@ describe('loading:', function () {
                 return httpBackend.flush();
             }).then(() => {
                 // Wait for another trip around the event loop for the UI to update
-                return q.delay(10);
+                return Promise.delay(10);
             }).then(() => {
                 // we expect a single <Login> component following session load
                 ReactTestUtils.findRenderedComponentWithType(
@@ -197,7 +197,7 @@ describe('loading:', function () {
                 uriFragment: "#/room/!room:id",
             });
 
-            q.delay(1).then(() => {
+            Promise.delay(1).then(() => {
                 // at this point, we're trying to do a guest registration;
                 // we expect a spinner
                 assertAtLoadingSpinner(matrixChat);
@@ -209,7 +209,7 @@ describe('loading:', function () {
                 return httpBackend.flush();
             }).then(() => {
                 // Wait for another trip around the event loop for the UI to update
-                return q.delay(10);
+                return Promise.delay(10);
             }).then(() => {
                 return completeLogin(matrixChat);
             }).then(() => {
@@ -232,7 +232,7 @@ describe('loading:', function () {
                 uriFragment: "#/login",
             });
 
-            return q.delay(100).then(() => {
+            return Promise.delay(100).then(() => {
                 // we expect a single <Login> component
                 ReactTestUtils.findRenderedComponentWithType(
                     matrixChat, sdk.getComponent('structures.login.Login'));
@@ -339,7 +339,7 @@ describe('loading:', function () {
                 },
             });
 
-            return q.delay(1).then(() => {
+            return Promise.delay(1).then(() => {
                 // we expect a loading spinner while we log into the RTS
                 assertAtLoadingSpinner(matrixChat);
 
@@ -366,7 +366,7 @@ describe('loading:', function () {
                 });
 
                 // give the UI a chance to display
-                return q.delay(50);
+                return Promise.delay(50);
             });
 
             it('shows a login view', function() {
@@ -403,7 +403,7 @@ describe('loading:', function () {
         it('shows a home page by default', function (done) {
             loadApp();
 
-            q.delay(1).then(() => {
+            Promise.delay(1).then(() => {
                 // at this point, we're trying to do a guest registration;
                 // we expect a spinner
                 assertAtLoadingSpinner(matrixChat);
@@ -436,7 +436,7 @@ describe('loading:', function () {
 
             loadApp();
 
-            q.delay(1).then(() => {
+            Promise.delay(1).then(() => {
                 // at this point, we're trying to do a guest registration;
                 // we expect a spinner
                 assertAtLoadingSpinner(matrixChat);
@@ -471,7 +471,7 @@ describe('loading:', function () {
             loadApp({
                 uriFragment: "#/room/!room:id"
             });
-            q.delay(1).then(() => {
+            Promise.delay(1).then(() => {
                 // at this point, we're trying to do a guest registration;
                 // we expect a spinner
                 assertAtLoadingSpinner(matrixChat);
@@ -530,7 +530,7 @@ describe('loading:', function () {
 
                     dis.dispatch({ action: 'start_login' });
 
-                    return q.delay(1);
+                    return Promise.delay(1);
                 });
             });
 
@@ -559,7 +559,7 @@ describe('loading:', function () {
 
                 ReactTestUtils.Simulate.click(returnToApp);
 
-                return q.delay(1).then(() => {
+                return Promise.delay(1).then(() => {
                     // we should be straight back into the home page
                     ReactTestUtils.findRenderedComponentWithType(
                         matrixChat, sdk.getComponent('structures.HomePage'));
@@ -574,7 +574,7 @@ describe('loading:', function () {
                 queryString: "?loginToken=secretToken&homeserver=https%3A%2F%2Fhomeserver&identityServer=https%3A%2F%2Fidserver",
             });
 
-            q.delay(1).then(() => {
+            Promise.delay(1).then(() => {
                 // we expect a spinner while we're logging in
                 assertAtLoadingSpinner(matrixChat);
 
@@ -629,7 +629,7 @@ describe('loading:', function () {
 
         return httpBackend.flush().then(() => {
             // Wait for another trip around the event loop for the UI to update
-            return q.delay(1);
+            return Promise.delay(1);
         }).then(() => {
             // we expect a spinner
             ReactTestUtils.findRenderedComponentWithType(
@@ -674,7 +674,7 @@ function awaitSyncingSpinner(matrixChat, retryLimit, retryCount) {
         }
         // loading can take quite a long time, because we delete the
         // indexedDB store.
-        return q.delay(5).then(() => {
+        return Promise.delay(5).then(() => {
             return awaitSyncingSpinner(matrixChat, retryLimit, retryCount + 1);
         });
     }
@@ -711,7 +711,7 @@ function awaitRoomView(matrixChat, retryLimit, retryCount) {
             throw new Error("MatrixChat still not ready after " +
                             retryCount + " tries");
         }
-        return q.delay(0).then(() => {
+        return Promise.delay(0).then(() => {
             return awaitRoomView(matrixChat, retryLimit, retryCount + 1);
         });
     }

From 4c5b5ca0ba8cd55316229b4d0bc6b697dfc24022 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Thu, 13 Jul 2017 00:37:04 +0100
Subject: [PATCH 4/7] replace `q.Promise` with `new Promise`

---
 src/vector/rageshake.js | 8 ++++----
 test/test-utils.js      | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js
index 05f00d65..d0977414 100644
--- a/src/vector/rageshake.js
+++ b/src/vector/rageshake.js
@@ -116,7 +116,7 @@ class IndexedDBLogStore {
      */
     connect() {
         let req = this.indexedDB.open("logs");
-        return q.Promise((resolve, reject) => {
+        return new Promise((resolve, reject) => {
             req.onsuccess = (event) => {
                 this.db = event.target.result;
                 // Periodically flush logs to local storage / indexeddb
@@ -193,7 +193,7 @@ class IndexedDBLogStore {
         }
         // there is no flush promise or there was but it has finished, so do
         // a brand new one, destroying the chain which may have been built up.
-        this.flushPromise = q.Promise((resolve, reject) => {
+        this.flushPromise = new Promise((resolve, reject) => {
             if (!this.db) {
                 // not connected yet or user rejected access for us to r/w to
                 // the db.
@@ -277,7 +277,7 @@ class IndexedDBLogStore {
         }
 
         function deleteLogs(id) {
-            return q.Promise((resolve, reject) => {
+            return new Promise((resolve, reject) => {
                 const txn = db.transaction(
                     ["logs", "logslastmod"], "readwrite"
                 );
@@ -375,7 +375,7 @@ class IndexedDBLogStore {
  */
 function selectQuery(store, keyRange, resultMapper) {
     const query = store.openCursor(keyRange);
-    return q.Promise((resolve, reject) => {
+    return new Promise((resolve, reject) => {
         let results = [];
         query.onerror = (event) => {
             reject(new Error("Query failed: " + event.target.errorCode));
diff --git a/test/test-utils.js b/test/test-utils.js
index 88ac9d99..007883df 100644
--- a/test/test-utils.js
+++ b/test/test-utils.js
@@ -28,7 +28,7 @@ export function browserSupportsWebRTC() {
 }
 
 export function deleteIndexedDB(dbName) {
-    return new q.Promise((resolve, reject) => {
+    return new Promise((resolve, reject) => {
         if (!window.indexedDB) {
             resolve();
             return;

From 1d2d086a58f6887f91c166f98e4314ebd2fa8c95 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Thu, 13 Jul 2017 00:41:18 +0100
Subject: [PATCH 5/7] Replace q.allSettled()

Bluebird doesn't have an `allSettled` method, so instead catch the exceptions and
use `all`.
---
 src/components/views/settings/Notifications.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/components/views/settings/Notifications.js b/src/components/views/settings/Notifications.js
index e8ee5434..11ca2e39 100644
--- a/src/components/views/settings/Notifications.js
+++ b/src/components/views/settings/Notifications.js
@@ -431,7 +431,9 @@ module.exports = React.createClass({
                             'global', kind, LEGACY_RULES[rule.rule_id], portLegacyActions(rule.actions)
                         ).then( function() {
                             return cli.deletePushRule('global', kind, rule.rule_id);
-                        })
+                        }).catch( (e) => {
+                            console.warn(`Error when porting legacy rule: ${e}`);
+                        });
                     }(kind, rule));
                 }
             }
@@ -440,7 +442,7 @@ module.exports = React.createClass({
         if (needsUpdate.length > 0) {
             // If some of the rules need to be ported then wait for the porting
             // to happen and then fetch the rules again.
-            return q.allSettled(needsUpdate).then( function() {
+            return q.all(needsUpdate).then( function() {
                 return cli.getPushRules();
             });
         } else {

From 69eb1a49d45e28381df59a3f5c1018763807f611 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Thu, 13 Jul 2017 00:56:13 +0100
Subject: [PATCH 6/7] Fix a broken test

turns out that you could call defer.resolve on q defers as an unbound function,
whereas that doesn't work with bluebird promises.
---
 test/app-tests/loading.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/app-tests/loading.js b/test/app-tests/loading.js
index 558cbc5e..0161a826 100644
--- a/test/app-tests/loading.js
+++ b/test/app-tests/loading.js
@@ -139,7 +139,7 @@ describe('loading:', function () {
                 realQueryParams={params}
                 startingFragmentQueryParams={fragParts.params}
                 enableGuest={true}
-                onTokenLoginCompleted={tokenLoginCompleteDefer.resolve}
+                onTokenLoginCompleted={() => tokenLoginCompleteDefer.resolve()}
                 initialScreenAfterLogin={getScreenFromLocation(windowLocation)}
                 makeRegistrationUrl={() => {throw new Error('Not implemented');}}
             />, parentDiv

From 7238c4dfacd2fcb07cdc2a1454a6d5a378721c85 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Thu, 13 Jul 2017 11:33:25 +0100
Subject: [PATCH 7/7] Fix overlooked `q`

---
 src/components/views/settings/Notifications.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/views/settings/Notifications.js b/src/components/views/settings/Notifications.js
index 11ca2e39..451a28b8 100644
--- a/src/components/views/settings/Notifications.js
+++ b/src/components/views/settings/Notifications.js
@@ -442,7 +442,7 @@ module.exports = React.createClass({
         if (needsUpdate.length > 0) {
             // If some of the rules need to be ported then wait for the porting
             // to happen and then fetch the rules again.
-            return q.all(needsUpdate).then( function() {
+            return Promise.all(needsUpdate).then( function() {
                 return cli.getPushRules();
             });
         } else {