From a06bd8421329edebe3327afdbb3946fb7471737d Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 12 Jul 2017 13:58:14 +0100 Subject: [PATCH 01/21] 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/ContentMessages.js | 2 +- src/Lifecycle.js | 2 +- src/Login.js | 2 +- src/RoomNotifs.js | 2 +- src/Rooms.js | 2 +- src/ScalarAuthClient.js | 2 +- src/UserSettingsStore.js | 2 +- src/autocomplete/Autocompleter.js | 2 +- src/components/structures/MatrixChat.js | 2 +- src/components/structures/RoomView.js | 2 +- src/components/structures/ScrollPanel.js | 2 +- src/components/structures/TimelinePanel.js | 2 +- src/components/structures/UserSettings.js | 2 +- src/components/structures/login/Registration.js | 2 +- src/components/views/dialogs/ChatInviteDialog.js | 2 +- src/components/views/dialogs/SetMxIdDialog.js | 2 +- src/components/views/elements/EditableTextContainer.js | 2 +- src/components/views/messages/MImageBody.js | 2 +- src/components/views/messages/MVideoBody.js | 2 +- src/components/views/room_settings/AliasSettings.js | 2 +- src/components/views/room_settings/ColorSettings.js | 2 +- src/components/views/room_settings/UrlPreviewSettings.js | 2 +- src/components/views/rooms/Autocomplete.js | 2 +- src/components/views/rooms/MemberList.js | 2 +- src/components/views/rooms/MessageComposerInput.js | 2 +- src/components/views/rooms/RoomSettings.js | 2 +- src/components/views/settings/ChangePassword.js | 2 +- src/createRoom.js | 2 +- src/languageHandler.js | 2 +- src/utils/DecryptFile.js | 2 +- src/utils/MultiInviter.js | 2 +- test/components/structures/ScrollPanel-test.js | 2 +- test/components/structures/TimelinePanel-test.js | 2 +- test/components/views/dialogs/InteractiveAuthDialog-test.js | 2 +- test/components/views/rooms/MessageComposerInput-test.js | 2 +- test/stores/RoomViewStore-test.js | 2 +- test/test-utils.js | 2 +- 38 files changed, 38 insertions(+), 38 deletions(-) diff --git a/package.json b/package.json index 2b637f68c6..831644083a 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ }, "dependencies": { "babel-runtime": "^6.11.6", + "bluebird": "^3.5.0", "blueimp-canvas-to-blob": "^3.5.0", "browser-encrypt-attachment": "^0.3.0", "browser-request": "^0.3.3", @@ -68,7 +69,6 @@ "matrix-js-sdk": "matrix-org/matrix-js-sdk#develop", "optimist": "^0.6.1", "prop-types": "^15.5.8", - "q": "^1.4.1", "react": "^15.4.0", "react-addons-css-transition-group": "15.3.2", "react-dom": "^15.4.0", diff --git a/src/ContentMessages.js b/src/ContentMessages.js index 315c312b9f..a9d02b1825 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -16,7 +16,7 @@ limitations under the License. 'use strict'; -var q = require('q'); +import Promise from 'bluebird'; var extend = require('./extend'); var dis = require('./dispatcher'); var MatrixClientPeg = require('./MatrixClientPeg'); diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 00a82bad21..4d44e9839c 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import q from 'q'; +import Promise from 'bluebird'; import Matrix from 'matrix-js-sdk'; import MatrixClientPeg from './MatrixClientPeg'; diff --git a/src/Login.js b/src/Login.js index 8225509919..038813cd62 100644 --- a/src/Login.js +++ b/src/Login.js @@ -18,7 +18,7 @@ limitations under the License. import Matrix from "matrix-js-sdk"; import { _t } from "./languageHandler"; -import q from 'q'; +import Promise from 'bluebird'; import url from 'url'; export default class Login { diff --git a/src/RoomNotifs.js b/src/RoomNotifs.js index 88b6e56c7f..eddf516b20 100644 --- a/src/RoomNotifs.js +++ b/src/RoomNotifs.js @@ -16,7 +16,7 @@ limitations under the License. import MatrixClientPeg from './MatrixClientPeg'; import PushProcessor from 'matrix-js-sdk/lib/pushprocessor'; -import q from 'q'; +import Promise from 'bluebird'; export const ALL_MESSAGES_LOUD = 'all_messages_loud'; export const ALL_MESSAGES = 'all_messages'; diff --git a/src/Rooms.js b/src/Rooms.js index 3ac7c68533..44d7bedcce 100644 --- a/src/Rooms.js +++ b/src/Rooms.js @@ -15,7 +15,7 @@ limitations under the License. */ import MatrixClientPeg from './MatrixClientPeg'; -import q from 'q'; +import Promise from 'bluebird'; /** * Given a room object, return the alias we should use for it, diff --git a/src/ScalarAuthClient.js b/src/ScalarAuthClient.js index 6908a7f67d..9702d21add 100644 --- a/src/ScalarAuthClient.js +++ b/src/ScalarAuthClient.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -var q = require("q"); +import Promise from 'bluebird'; var request = require('browser-request'); var SdkConfig = require('./SdkConfig'); diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index fef84468ec..8c04f35d02 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.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'; import MatrixClientPeg from './MatrixClientPeg'; import Notifier from './Notifier'; import { _t } from './languageHandler'; diff --git a/src/autocomplete/Autocompleter.js b/src/autocomplete/Autocompleter.js index 62b5a870f3..236c0aa0f2 100644 --- a/src/autocomplete/Autocompleter.js +++ b/src/autocomplete/Autocompleter.js @@ -22,7 +22,7 @@ import DuckDuckGoProvider from './DuckDuckGoProvider'; import RoomProvider from './RoomProvider'; import UserProvider from './UserProvider'; import EmojiProvider from './EmojiProvider'; -import Q from 'q'; +import Promise from 'bluebird'; export type SelectionRange = { start: number, diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 75a15d71ee..8bc91fdc18 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import q from 'q'; +import Promise from 'bluebird'; import React from 'react'; import Matrix from "matrix-js-sdk"; diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 75ab4a0db2..306cbe922d 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -22,7 +22,7 @@ limitations under the License. var React = require("react"); var ReactDOM = require("react-dom"); -var q = require("q"); +import Promise from 'bluebird'; var classNames = require("classnames"); var Matrix = require("matrix-js-sdk"); import { _t } from '../../languageHandler'; diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index f035efee92..0a593c29ac 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -17,7 +17,7 @@ limitations under the License. var React = require("react"); var ReactDOM = require("react-dom"); var GeminiScrollbar = require('react-gemini-scrollbar'); -var q = require("q"); +import Promise from 'bluebird'; var KeyCode = require('../../KeyCode'); var DEBUG_SCROLL = false; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index cdee03ab63..4dcb9c5ef8 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -17,7 +17,7 @@ limitations under the License. var React = require('react'); var ReactDOM = require("react-dom"); -var q = require("q"); +import Promise from 'bluebird'; var Matrix = require("matrix-js-sdk"); var EventTimeline = Matrix.EventTimeline; diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index bfbb9831b0..0907c3e261 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -22,7 +22,7 @@ const PlatformPeg = require("../../PlatformPeg"); const Modal = require('../../Modal'); const dis = require("../../dispatcher"); import sessionStore from '../../stores/SessionStore'; -const q = require('q'); +import Promise from 'bluebird'; const packageJson = require('../../../package.json'); const UserSettingsStore = require('../../UserSettingsStore'); const CallMediaHandler = require('../../CallMediaHandler'); diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js index 388198bb02..a2e2d02bb0 100644 --- a/src/components/structures/login/Registration.js +++ b/src/components/structures/login/Registration.js @@ -17,7 +17,7 @@ limitations under the License. import Matrix from 'matrix-js-sdk'; -import q from 'q'; +import Promise from 'bluebird'; import React from 'react'; import sdk from '../../../index'; diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js index a85efadef7..f8ebaefec8 100644 --- a/src/components/views/dialogs/ChatInviteDialog.js +++ b/src/components/views/dialogs/ChatInviteDialog.js @@ -23,7 +23,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg'; import DMRoomMap from '../../../utils/DMRoomMap'; import Modal from '../../../Modal'; import AccessibleButton from '../elements/AccessibleButton'; -import q from 'q'; +import Promise from 'bluebird'; import dis from '../../../dispatcher'; const TRUNCATE_QUERY_LIST = 40; diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index d428223ad6..4d4f672f2b 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import q from 'q'; +import Promise from 'bluebird'; import React from 'react'; import sdk from '../../../index'; import MatrixClientPeg from '../../../MatrixClientPeg'; diff --git a/src/components/views/elements/EditableTextContainer.js b/src/components/views/elements/EditableTextContainer.js index d6f9c555c6..f986587966 100644 --- a/src/components/views/elements/EditableTextContainer.js +++ b/src/components/views/elements/EditableTextContainer.js @@ -16,7 +16,7 @@ limitations under the License. import React from 'react'; import sdk from '../../../index'; -import q from 'q'; +import Promise from 'bluebird'; /** * A component which wraps an EditableText, with a spinner while updates take diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index da6447c7e5..cd80785749 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -24,7 +24,7 @@ import Modal from '../../../Modal'; import sdk from '../../../index'; import dis from '../../../dispatcher'; import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile'; -import q from 'q'; +import Promise from 'bluebird'; import UserSettingsStore from '../../../UserSettingsStore'; import { _t } from '../../../languageHandler'; diff --git a/src/components/views/messages/MVideoBody.js b/src/components/views/messages/MVideoBody.js index f31b94df80..10008a8e46 100644 --- a/src/components/views/messages/MVideoBody.js +++ b/src/components/views/messages/MVideoBody.js @@ -20,7 +20,7 @@ import React from 'react'; import MFileBody from './MFileBody'; import MatrixClientPeg from '../../../MatrixClientPeg'; import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile'; -import q from 'q'; +import Promise from 'bluebird'; import UserSettingsStore from '../../../UserSettingsStore'; import { _t } from '../../../languageHandler'; diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.js index b9c9c7d989..0fb099422c 100644 --- a/src/components/views/room_settings/AliasSettings.js +++ b/src/components/views/room_settings/AliasSettings.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -var q = require("q"); +import Promise from 'bluebird'; var React = require('react'); var ObjectUtils = require("../../../ObjectUtils"); var MatrixClientPeg = require('../../../MatrixClientPeg'); diff --git a/src/components/views/room_settings/ColorSettings.js b/src/components/views/room_settings/ColorSettings.js index 5fc845a541..763c402d6f 100644 --- a/src/components/views/room_settings/ColorSettings.js +++ b/src/components/views/room_settings/ColorSettings.js @@ -13,7 +13,7 @@ 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. */ -var q = require("q"); +import Promise from 'bluebird'; var React = require('react'); var sdk = require('../../../index'); diff --git a/src/components/views/room_settings/UrlPreviewSettings.js b/src/components/views/room_settings/UrlPreviewSettings.js index e5236b90cc..5ee18badaa 100644 --- a/src/components/views/room_settings/UrlPreviewSettings.js +++ b/src/components/views/room_settings/UrlPreviewSettings.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -var q = require("q"); +import Promise from 'bluebird'; var React = require('react'); var MatrixClientPeg = require('../../../MatrixClientPeg'); var sdk = require("../../../index"); diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index 026be0da62..a3315f61aa 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -5,7 +5,7 @@ import flatMap from 'lodash/flatMap'; import isEqual from 'lodash/isEqual'; import sdk from '../../../index'; import type {Completion} from '../../../autocomplete/Autocompleter'; -import Q from 'q'; +import Promise from 'bluebird'; import UserSettingsStore from '../../../UserSettingsStore'; import {getCompletions} from '../../../autocomplete/Autocompleter'; diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index 63737d5fad..37a9bcd0cd 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -17,7 +17,7 @@ var React = require('react'); import { _t } from '../../../languageHandler'; var classNames = require('classnames'); var Matrix = require("matrix-js-sdk"); -var q = require('q'); +import Promise from 'bluebird'; var MatrixClientPeg = require("../../../MatrixClientPeg"); var Modal = require("../../../Modal"); var Entities = require("../../../Entities"); diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index cf6dfbb6b7..bec74c15dd 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -22,7 +22,7 @@ import {Editor, EditorState, RichUtils, CompositeDecorator, import classNames from 'classnames'; import escape from 'lodash/escape'; -import Q from 'q'; +import Promise from 'bluebird'; import MatrixClientPeg from '../../../MatrixClientPeg'; import type {MatrixClient} from 'matrix-js-sdk/lib/matrix'; diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index d255670a52..caa9f1bdaf 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import q from 'q'; +import Promise from 'bluebird'; import React from 'react'; import { _t, _tJsx } from '../../../languageHandler'; import MatrixClientPeg from '../../../MatrixClientPeg'; diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js index 099ae2deae..9d91c007b1 100644 --- a/src/components/views/settings/ChangePassword.js +++ b/src/components/views/settings/ChangePassword.js @@ -21,7 +21,7 @@ var MatrixClientPeg = require("../../../MatrixClientPeg"); var Modal = require("../../../Modal"); var sdk = require("../../../index"); -import q from 'q'; +import Promise from 'bluebird'; import AccessibleButton from '../elements/AccessibleButton'; import { _t } from '../../../languageHandler'; diff --git a/src/createRoom.js b/src/createRoom.js index 916405776d..3b95c29b4c 100644 --- a/src/createRoom.js +++ b/src/createRoom.js @@ -21,7 +21,7 @@ import { _t } from './languageHandler'; import dis from "./dispatcher"; import * as Rooms from "./Rooms"; -import q from 'q'; +import Promise from 'bluebird'; /** * Create a new room, and switch to it. diff --git a/src/languageHandler.js b/src/languageHandler.js index 1f0e6326e0..7af4f7b200 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -17,7 +17,7 @@ limitations under the License. import request from 'browser-request'; import counterpart from 'counterpart'; -import q from 'q'; +import Promise from 'bluebird'; import React from 'react'; import UserSettingsStore from './UserSettingsStore'; diff --git a/src/utils/DecryptFile.js b/src/utils/DecryptFile.js index 8b5f014f2c..b9ef33ea6c 100644 --- a/src/utils/DecryptFile.js +++ b/src/utils/DecryptFile.js @@ -20,7 +20,7 @@ import encrypt from 'browser-encrypt-attachment'; import 'isomorphic-fetch'; // Grab the client so that we can turn mxc:// URLs into https:// URLS. import MatrixClientPeg from '../MatrixClientPeg'; -import q from 'q'; +import Promise from 'bluebird'; /** diff --git a/src/utils/MultiInviter.js b/src/utils/MultiInviter.js index 68a0800ed7..ecb5bd67f2 100644 --- a/src/utils/MultiInviter.js +++ b/src/utils/MultiInviter.js @@ -15,7 +15,7 @@ limitations under the License. */ import {getAddressType, inviteToRoom} from '../Invite'; -import q from 'q'; +import Promise from 'bluebird'; /** * Invites multiple addresses to a room, handling rate limiting from the server diff --git a/test/components/structures/ScrollPanel-test.js b/test/components/structures/ScrollPanel-test.js index a783f424e7..0f8b2416cd 100644 --- a/test/components/structures/ScrollPanel-test.js +++ b/test/components/structures/ScrollPanel-test.js @@ -18,7 +18,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 sdk = require('matrix-react-sdk'); diff --git a/test/components/structures/TimelinePanel-test.js b/test/components/structures/TimelinePanel-test.js index 748e37bee3..7822e14e2c 100644 --- a/test/components/structures/TimelinePanel-test.js +++ b/test/components/structures/TimelinePanel-test.js @@ -18,7 +18,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 sinon = require('sinon'); var jssdk = require('matrix-js-sdk'); diff --git a/test/components/views/dialogs/InteractiveAuthDialog-test.js b/test/components/views/dialogs/InteractiveAuthDialog-test.js index ecfaba4e2c..2816b987aa 100644 --- a/test/components/views/dialogs/InteractiveAuthDialog-test.js +++ b/test/components/views/dialogs/InteractiveAuthDialog-test.js @@ -15,7 +15,7 @@ limitations under the License. */ import expect from 'expect'; -import q from 'q'; +import Promise from 'bluebird'; import React from 'react'; import ReactDOM from 'react-dom'; import ReactTestUtils from 'react-addons-test-utils'; diff --git a/test/components/views/rooms/MessageComposerInput-test.js b/test/components/views/rooms/MessageComposerInput-test.js index 6d4b4e69cc..2edc1c9432 100644 --- a/test/components/views/rooms/MessageComposerInput-test.js +++ b/test/components/views/rooms/MessageComposerInput-test.js @@ -3,7 +3,7 @@ import ReactTestUtils from 'react-addons-test-utils'; import ReactDOM from 'react-dom'; import expect, {createSpy} from 'expect'; import sinon from 'sinon'; -import Q from 'q'; +import Promise from 'bluebird'; import * as testUtils from '../../../test-utils'; import sdk from 'matrix-react-sdk'; import UserSettingsStore from '../../../../src/UserSettingsStore'; diff --git a/test/stores/RoomViewStore-test.js b/test/stores/RoomViewStore-test.js index adcae90e9b..a0f804da18 100644 --- a/test/stores/RoomViewStore-test.js +++ b/test/stores/RoomViewStore-test.js @@ -7,7 +7,7 @@ import RoomViewStore from '../../src/stores/RoomViewStore'; import peg from '../../src/MatrixClientPeg'; import * as testUtils from '../test-utils'; -import q from 'q'; +import Promise from 'bluebird'; const dispatch = testUtils.getDispatchForStore(RoomViewStore); diff --git a/test/test-utils.js b/test/test-utils.js index bc6b484bb5..8f6b5c15e7 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -1,7 +1,7 @@ "use strict"; import sinon from 'sinon'; -import q from 'q'; +import Promise from 'bluebird'; import peg from '../src/MatrixClientPeg'; import dis from '../src/dispatcher'; From 0254d2b3a2eef7be5a0b9a55108581e59de9ab7f Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 12 Jul 2017 14:02:00 +0100 Subject: [PATCH 02/21] q(...) -> Promise.resolve ``` find src test -name '*.js' | xargs perl -i -pe 's/\b[qQ]\(/Promise.resolve(/' ``` --- src/Lifecycle.js | 8 +++---- src/Login.js | 4 ++-- src/Rooms.js | 2 +- src/ScalarAuthClient.js | 2 +- src/UserSettingsStore.js | 2 +- src/autocomplete/Autocompleter.js | 2 +- src/components/structures/MatrixChat.js | 6 ++--- src/components/structures/RoomView.js | 8 +++---- src/components/structures/ScrollPanel.js | 2 +- src/components/structures/TimelinePanel.js | 4 ++-- src/components/structures/UserSettings.js | 4 ++-- .../structures/login/Registration.js | 4 ++-- .../views/elements/EditableTextContainer.js | 2 +- src/components/views/messages/MImageBody.js | 2 +- src/components/views/messages/MVideoBody.js | 2 +- .../views/room_settings/ColorSettings.js | 4 ++-- src/components/views/rooms/Autocomplete.js | 2 +- src/components/views/rooms/RoomSettings.js | 10 ++++----- src/createRoom.js | 4 ++-- src/utils/DecryptFile.js | 2 +- .../components/structures/ScrollPanel-test.js | 8 +++---- .../structures/TimelinePanel-test.js | 4 ++-- .../dialogs/InteractiveAuthDialog-test.js | 2 +- test/stores/RoomViewStore-test.js | 2 +- test/test-utils.js | 22 +++++++++---------- 25 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 4d44e9839c..3c7da9496e 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -116,12 +116,12 @@ export function loadSession(opts) { */ export function attemptTokenLogin(queryParams, defaultDeviceDisplayName) { if (!queryParams.loginToken) { - return q(false); + return Promise.resolve(false); } if (!queryParams.homeserver) { console.warn("Cannot log in with token: can't determine HS URL to use"); - return q(false); + return Promise.resolve(false); } // create a temporary MatrixClient to do the login @@ -197,7 +197,7 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) { // localStorage (e.g. teamToken, isGuest etc.) function _restoreFromLocalStorage() { if (!localStorage) { - return q(false); + return Promise.resolve(false); } const hsUrl = localStorage.getItem("mx_hs_url"); const isUrl = localStorage.getItem("mx_is_url") || 'https://matrix.org'; @@ -229,7 +229,7 @@ function _restoreFromLocalStorage() { } } else { console.log("No previous session found."); - return q(false); + return Promise.resolve(false); } } diff --git a/src/Login.js b/src/Login.js index 038813cd62..049b79c2f4 100644 --- a/src/Login.js +++ b/src/Login.js @@ -144,7 +144,7 @@ export default class Login { const client = this._createTemporaryClient(); return client.login('m.login.password', loginParams).then(function(data) { - return q({ + return Promise.resolve({ homeserverUrl: self._hsUrl, identityServerUrl: self._isUrl, userId: data.user_id, @@ -160,7 +160,7 @@ export default class Login { }); return fbClient.login('m.login.password', loginParams).then(function(data) { - return q({ + return Promise.resolve({ homeserverUrl: self._fallbackHsUrl, identityServerUrl: self._isUrl, userId: data.user_id, diff --git a/src/Rooms.js b/src/Rooms.js index 44d7bedcce..2e3f4457f0 100644 --- a/src/Rooms.js +++ b/src/Rooms.js @@ -102,7 +102,7 @@ export function guessAndSetDMRoom(room, isDirect) { */ export function setDMRoom(roomId, userId) { if (MatrixClientPeg.get().isGuest()) { - return q(); + return Promise.resolve(); } const mDirectEvent = MatrixClientPeg.get().getAccountData('m.direct'); diff --git a/src/ScalarAuthClient.js b/src/ScalarAuthClient.js index 9702d21add..0275dcd1f7 100644 --- a/src/ScalarAuthClient.js +++ b/src/ScalarAuthClient.js @@ -39,7 +39,7 @@ class ScalarAuthClient { // Returns a scalar_token string getScalarToken() { var tok = window.localStorage.getItem("mx_scalar_token"); - if (tok) return q(tok); + if (tok) return Promise.resolve(tok); // No saved token, so do the dance to get one. First, we // need an openid bearer token from the HS. diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index 8c04f35d02..4c66c90598 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -48,7 +48,7 @@ export default { loadThreePids: function() { if (MatrixClientPeg.get().isGuest()) { - return q({ + return Promise.resolve({ threepids: [], }); // guests can't poke 3pid endpoint } diff --git a/src/autocomplete/Autocompleter.js b/src/autocomplete/Autocompleter.js index 236c0aa0f2..6b8cd66ae2 100644 --- a/src/autocomplete/Autocompleter.js +++ b/src/autocomplete/Autocompleter.js @@ -57,7 +57,7 @@ export async function getCompletions(query: string, selection: SelectionRange, f state (== "fulfilled" || "rejected") and value. */ const completionsList = await Q.allSettled( PROVIDERS.map(provider => { - return Q(provider.getCompletions(query, selection, force)) + return Promise.resolve(provider.getCompletions(query, selection, force)) .timeout(PROVIDER_COMPLETION_TIMEOUT); }), ); diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 8bc91fdc18..b4fd469441 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -323,9 +323,9 @@ module.exports = React.createClass({ return; } - // the extra q() ensures that synchronous exceptions hit the same codepath as + // the extra Promise.resolve() ensures that synchronous exceptions hit the same codepath as // asynchronous ones. - return q().then(() => { + return Promise.resolve().then(() => { return Lifecycle.loadSession({ fragmentQueryParams: this.props.startingFragmentQueryParams, enableGuest: this.props.enableGuest, @@ -694,7 +694,7 @@ module.exports = React.createClass({ // Wait for the first sync to complete so that if a room does have an alias, // it would have been retrieved. - let waitFor = q(null); + let waitFor = Promise.resolve(null); if (!this.firstSyncComplete) { if (!this.firstSyncPromise) { console.warn('Cannot view a room before first sync. room_id:', roomInfo.room_id); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 306cbe922d..451f01a633 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -788,7 +788,7 @@ module.exports = React.createClass({ onSearchResultsFillRequest: function(backwards) { if (!backwards) { - return q(false); + return Promise.resolve(false); } if (this.state.searchResults.next_batch) { @@ -798,7 +798,7 @@ module.exports = React.createClass({ return this._handleSearchResult(searchPromise); } else { debuglog("no more search results"); - return q(false); + return Promise.resolve(false); } }, @@ -859,7 +859,7 @@ module.exports = React.createClass({ return; } - q().then(() => { + Promise.resolve().then(() => { const signUrl = this.props.thirdPartyInvite ? this.props.thirdPartyInvite.inviteSignUrl : undefined; dis.dispatch({ @@ -878,7 +878,7 @@ module.exports = React.createClass({ } } } - return q(); + return Promise.resolve(); }); }, diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 0a593c29ac..95ab586e03 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -145,7 +145,7 @@ module.exports = React.createClass({ return { stickyBottom: true, startAtBottom: true, - onFillRequest: function(backwards) { return q(false); }, + onFillRequest: function(backwards) { return Promise.resolve(false); }, onUnfillRequest: function(backwards, scrollToken) {}, onScroll: function() {}, }; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 4dcb9c5ef8..dc9c0fc9ba 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -311,13 +311,13 @@ var TimelinePanel = React.createClass({ if (!this.state[canPaginateKey]) { debuglog("TimelinePanel: have given up", dir, "paginating this timeline"); - return q(false); + return Promise.resolve(false); } if(!this._timelineWindow.canPaginate(dir)) { debuglog("TimelinePanel: can't", dir, "paginate any further"); this.setState({[canPaginateKey]: false}); - return q(false); + return Promise.resolve(false); } debuglog("TimelinePanel: Initiating paginate; backwards:"+backwards); diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 0907c3e261..3558e0c278 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -203,7 +203,7 @@ module.exports = React.createClass({ this._addThreepid = null; if (PlatformPeg.get()) { - q().then(() => { + Promise.resolve().then(() => { return PlatformPeg.get().getAppVersion(); }).done((appVersion) => { if (this._unmounted) return; @@ -301,7 +301,7 @@ module.exports = React.createClass({ }, _refreshMediaDevices: function() { - q().then(() => { + Promise.resolve().then(() => { return CallMediaHandler.getDevices(); }).then((mediaDevices) => { // console.log("got mediaDevices", mediaDevices, this._unmounted); diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js index a2e2d02bb0..fe05ba4cfd 100644 --- a/src/components/structures/login/Registration.js +++ b/src/components/structures/login/Registration.js @@ -180,7 +180,7 @@ module.exports = React.createClass({ // will just nop. The point of this being we might not have the email address // that the user registered with at this stage (depending on whether this // is the client they initiated registration). - let trackPromise = q(null); + let trackPromise = Promise.resolve(null); if (this._rtsClient && extra.emailSid) { // Track referral if this.props.referrer set, get team_token in order to // retrieve team config and see welcome page etc. @@ -232,7 +232,7 @@ module.exports = React.createClass({ _setupPushers: function(matrixClient) { if (!this.props.brand) { - return q(); + return Promise.resolve(); } return matrixClient.getPushers().then((resp)=>{ const pushers = resp.pushers; diff --git a/src/components/views/elements/EditableTextContainer.js b/src/components/views/elements/EditableTextContainer.js index f986587966..aeb32c07ec 100644 --- a/src/components/views/elements/EditableTextContainer.js +++ b/src/components/views/elements/EditableTextContainer.js @@ -148,5 +148,5 @@ EditableTextContainer.defaultProps = { initialValue: "", placeholder: "", blurToSubmit: false, - onSubmit: function(v) {return q(); }, + onSubmit: function(v) {return Promise.resolve(); }, }; diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index cd80785749..dcc073c80e 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -123,7 +123,7 @@ module.exports = React.createClass({ this.fixupHeight(); const content = this.props.mxEvent.getContent(); if (content.file !== undefined && this.state.decryptedUrl === null) { - let thumbnailPromise = q(null); + let thumbnailPromise = Promise.resolve(null); if (content.info.thumbnail_file) { thumbnailPromise = decryptFile( content.info.thumbnail_file, diff --git a/src/components/views/messages/MVideoBody.js b/src/components/views/messages/MVideoBody.js index 10008a8e46..06c233e012 100644 --- a/src/components/views/messages/MVideoBody.js +++ b/src/components/views/messages/MVideoBody.js @@ -89,7 +89,7 @@ module.exports = React.createClass({ componentDidMount: function() { const content = this.props.mxEvent.getContent(); if (content.file !== undefined && this.state.decryptedUrl === null) { - var thumbnailPromise = q(null); + var thumbnailPromise = Promise.resolve(null); if (content.info.thumbnail_file) { thumbnailPromise = decryptFile( content.info.thumbnail_file diff --git a/src/components/views/room_settings/ColorSettings.js b/src/components/views/room_settings/ColorSettings.js index 763c402d6f..b37802d304 100644 --- a/src/components/views/room_settings/ColorSettings.js +++ b/src/components/views/room_settings/ColorSettings.js @@ -72,7 +72,7 @@ module.exports = React.createClass({ saveSettings: function() { // : Promise if (!this.state.hasChanged) { - return q(); // They didn't explicitly give a color to save. + return Promise.resolve(); // They didn't explicitly give a color to save. } var originalState = this.getInitialState(); if (originalState.primary_color !== this.state.primary_color || @@ -92,7 +92,7 @@ module.exports = React.createClass({ } }); } - return q(); // no color diff + return Promise.resolve(); // no color diff }, _getColorIndex: function(scheme) { diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index a3315f61aa..10c9d405ac 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -64,7 +64,7 @@ export default class Autocomplete extends React.Component { // Hide the autocomplete box hide: true, }); - return Q(null); + return Promise.resolve(null); } let autocompleteDelay = UserSettingsStore.getLocalSetting('autocompleteDelay', 200); diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index caa9f1bdaf..270b2a54d5 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -305,25 +305,25 @@ module.exports = React.createClass({ }, saveAliases: function() { - if (!this.refs.alias_settings) { return [q()]; } + if (!this.refs.alias_settings) { return [Promise.resolve()]; } return this.refs.alias_settings.saveSettings(); }, saveColor: function() { - if (!this.refs.color_settings) { return q(); } + if (!this.refs.color_settings) { return Promise.resolve(); } return this.refs.color_settings.saveSettings(); }, saveUrlPreviewSettings: function() { - if (!this.refs.url_preview_settings) { return q(); } + if (!this.refs.url_preview_settings) { return Promise.resolve(); } return this.refs.url_preview_settings.saveSettings(); }, saveEnableEncryption: function() { - if (!this.refs.encrypt) { return q(); } + if (!this.refs.encrypt) { return Promise.resolve(); } var encrypt = this.refs.encrypt.checked; - if (!encrypt) { return q(); } + if (!encrypt) { return Promise.resolve(); } var roomId = this.props.room.roomId; return MatrixClientPeg.get().sendStateEvent( diff --git a/src/createRoom.js b/src/createRoom.js index 3b95c29b4c..74e4b3c2fc 100644 --- a/src/createRoom.js +++ b/src/createRoom.js @@ -42,7 +42,7 @@ function createRoom(opts) { const client = MatrixClientPeg.get(); if (client.isGuest()) { dis.dispatch({action: 'view_set_mxid'}); - return q(null); + return Promise.resolve(null); } const defaultPreset = opts.dmUserId ? 'trusted_private_chat' : 'private_chat'; @@ -92,7 +92,7 @@ function createRoom(opts) { if (opts.dmUserId) { return Rooms.setDMRoom(roomId, opts.dmUserId); } else { - return q(); + return Promise.resolve(); } }).then(function() { // NB createRoom doesn't block on the client seeing the echo that the diff --git a/src/utils/DecryptFile.js b/src/utils/DecryptFile.js index b9ef33ea6c..8054805409 100644 --- a/src/utils/DecryptFile.js +++ b/src/utils/DecryptFile.js @@ -53,7 +53,7 @@ export function readBlobAsDataUri(file) { export function decryptFile(file) { const url = MatrixClientPeg.get().mxcUrlToHttp(file.url); // Download the encrypted file as an array buffer. - return q(fetch(url)).then(function(response) { + return Promise.resolve(fetch(url)).then(function(response) { return response.arrayBuffer(); }).then(function(responseData) { // Decrypt the array buffer using the information taken from diff --git a/test/components/structures/ScrollPanel-test.js b/test/components/structures/ScrollPanel-test.js index 0f8b2416cd..670633c6fd 100644 --- a/test/components/structures/ScrollPanel-test.js +++ b/test/components/structures/ScrollPanel-test.js @@ -58,7 +58,7 @@ var Tester = React.createClass({ if (handler) { res = handler(); } else { - res = q(false); + res = Promise.resolve(false); } if (defer) { @@ -168,7 +168,7 @@ describe('ScrollPanel', function() { const sp = tester.scrollPanel(); let retriesRemaining = 1; const awaitReady = function() { - return q().then(() => { + return Promise.resolve().then(() => { if (sp._pendingFillRequests.b === false && sp._pendingFillRequests.f === false ) { @@ -195,7 +195,7 @@ describe('ScrollPanel', function() { it('should handle scrollEvent strangeness', function() { const events = []; - return q().then(() => { + return Promise.resolve().then(() => { // initialise with a load of events for (let i = 0; i < 20; i++) { events.push(i+80); @@ -227,7 +227,7 @@ describe('ScrollPanel', function() { it('should not get stuck in #528 workaround', function(done) { var events = []; - q().then(() => { + Promise.resolve().then(() => { // initialise with a bunch of events for (var i = 0; i < 40; i++) { events.push(i); diff --git a/test/components/structures/TimelinePanel-test.js b/test/components/structures/TimelinePanel-test.js index 7822e14e2c..56bde195ee 100644 --- a/test/components/structures/TimelinePanel-test.js +++ b/test/components/structures/TimelinePanel-test.js @@ -145,7 +145,7 @@ describe('TimelinePanel', function() { // panel isn't paginating var awaitPaginationCompletion = function() { if(!panel.state.forwardPaginating) - return q(); + return Promise.resolve(); else return q.delay(0).then(awaitPaginationCompletion); }; @@ -214,7 +214,7 @@ describe('TimelinePanel', function() { client.paginateEventTimeline = sinon.spy((tl, opts) => { console.log("paginate:", opts); expect(opts.backwards).toBe(true); - return q(true); + return Promise.resolve(true); }); var rendered = ReactDOM.render( diff --git a/test/components/views/dialogs/InteractiveAuthDialog-test.js b/test/components/views/dialogs/InteractiveAuthDialog-test.js index 2816b987aa..969396865e 100644 --- a/test/components/views/dialogs/InteractiveAuthDialog-test.js +++ b/test/components/views/dialogs/InteractiveAuthDialog-test.js @@ -50,7 +50,7 @@ describe('InteractiveAuthDialog', function () { it('Should successfully complete a password flow', function() { const onFinished = sinon.spy(); - const doRequest = sinon.stub().returns(q({a:1})); + const doRequest = sinon.stub().returns(Promise.resolve({a:1})); // tell the stub matrixclient to return a real userid var client = MatrixClientPeg.get(); diff --git a/test/stores/RoomViewStore-test.js b/test/stores/RoomViewStore-test.js index a0f804da18..be598de8da 100644 --- a/test/stores/RoomViewStore-test.js +++ b/test/stores/RoomViewStore-test.js @@ -39,7 +39,7 @@ describe('RoomViewStore', function() { }); it('can be used to view a room by alias and join', function(done) { - peg.get().getRoomIdForAlias.returns(q({room_id: "!randomcharacters:aser.ver"})); + peg.get().getRoomIdForAlias.returns(Promise.resolve({room_id: "!randomcharacters:aser.ver"})); peg.get().joinRoom = (roomAddress) => { expect(roomAddress).toBe("#somealias2:aser.ver"); done(); diff --git a/test/test-utils.js b/test/test-utils.js index 8f6b5c15e7..23f16a2e4c 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -75,12 +75,12 @@ export function createTestClient() { on: sinon.stub(), removeListener: sinon.stub(), isRoomEncrypted: sinon.stub().returns(false), - peekInRoom: sinon.stub().returns(q(mkStubRoom())), + peekInRoom: sinon.stub().returns(Promise.resolve(mkStubRoom())), - paginateEventTimeline: sinon.stub().returns(q()), - sendReadReceipt: sinon.stub().returns(q()), - getRoomIdForAlias: sinon.stub().returns(q()), - getProfileInfo: sinon.stub().returns(q({})), + paginateEventTimeline: sinon.stub().returns(Promise.resolve()), + sendReadReceipt: sinon.stub().returns(Promise.resolve()), + getRoomIdForAlias: sinon.stub().returns(Promise.resolve()), + getProfileInfo: sinon.stub().returns(Promise.resolve({})), getAccountData: (type) => { return mkEvent({ type, @@ -89,9 +89,9 @@ export function createTestClient() { }); }, setAccountData: sinon.stub(), - sendTyping: sinon.stub().returns(q({})), - sendTextMessage: () => q({}), - sendHtmlMessage: () => q({}), + sendTyping: sinon.stub().returns(Promise.resolve({})), + sendTextMessage: () => Promise.resolve({}), + sendHtmlMessage: () => Promise.resolve({}), getSyncState: () => "SYNCING", generateClientSecret: () => "t35tcl1Ent5ECr3T", isGuest: () => false, @@ -101,13 +101,13 @@ export function createTestClient() { export function createTestRtsClient(teamMap, sidMap) { return { getTeamsConfig() { - return q(Object.keys(teamMap).map((token) => teamMap[token])); + return Promise.resolve(Object.keys(teamMap).map((token) => teamMap[token])); }, trackReferral(referrer, emailSid, clientSecret) { - return q({team_token: sidMap[emailSid]}); + return Promise.resolve({team_token: sidMap[emailSid]}); }, getTeam(teamToken) { - return q(teamMap[teamToken]); + return Promise.resolve(teamMap[teamToken]); }, }; } From 0d7cc59d999a7866e5f369d7263d15d79c698a1c Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 12 Jul 2017 14:04:20 +0100 Subject: [PATCH 03/21] 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/ContentMessages.js | 10 +++++----- src/Lifecycle.js | 2 +- src/RoomNotifs.js | 4 ++-- src/ScalarAuthClient.js | 2 +- src/components/structures/MatrixChat.js | 4 ++-- src/components/structures/UserSettings.js | 2 +- src/components/views/dialogs/ChatInviteDialog.js | 2 +- src/components/views/room_settings/AliasSettings.js | 2 +- src/components/views/rooms/Autocomplete.js | 4 ++-- src/components/views/rooms/RoomSettings.js | 6 +++--- src/components/views/settings/ChangePassword.js | 2 +- src/languageHandler.js | 4 ++-- src/utils/DecryptFile.js | 2 +- src/utils/MultiInviter.js | 2 +- test/components/structures/ScrollPanel-test.js | 4 ++-- test/components/structures/TimelinePanel-test.js | 8 ++++---- .../views/dialogs/InteractiveAuthDialog-test.js | 2 +- .../views/rooms/MessageComposerInput-test.js | 2 +- 18 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/ContentMessages.js b/src/ContentMessages.js index a9d02b1825..93013cfd6c 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -52,7 +52,7 @@ const MAX_HEIGHT = 600; * and a thumbnail key. */ function createThumbnail(element, inputWidth, inputHeight, mimeType) { - const deferred = q.defer(); + const deferred = Promise.defer(); var targetWidth = inputWidth; var targetHeight = inputHeight; @@ -95,7 +95,7 @@ function createThumbnail(element, inputWidth, inputHeight, mimeType) { * @return {Promise} A promise that resolves with the html image element. */ function loadImageElement(imageFile) { - const deferred = q.defer(); + const deferred = Promise.defer(); // Load the file into an html element const img = document.createElement("img"); @@ -154,7 +154,7 @@ function infoForImageFile(matrixClient, roomId, imageFile) { * @return {Promise} A promise that resolves with the video image element. */ function loadVideoElement(videoFile) { - const deferred = q.defer(); + const deferred = Promise.defer(); // Load the file into an html element const video = document.createElement("video"); @@ -210,7 +210,7 @@ function infoForVideoFile(matrixClient, roomId, videoFile) { * is read. */ function readFileAsArrayBuffer(file) { - const deferred = q.defer(); + const deferred = Promise.defer(); const reader = new FileReader(); reader.onload = function(e) { deferred.resolve(e.target.result); @@ -288,7 +288,7 @@ class ContentMessages { content.info.mimetype = file.type; } - const def = q.defer(); + const def = Promise.defer(); if (file.type.indexOf('image/') == 0) { content.msgtype = 'm.image'; infoForImageFile(matrixClient, roomId, file).then(imageInfo=>{ diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 3c7da9496e..eb2156e780 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -236,7 +236,7 @@ function _restoreFromLocalStorage() { function _handleRestoreFailure(e) { console.log("Unable to restore session", e); - const def = q.defer(); + const def = Promise.defer(); const SessionRestoreErrorDialog = sdk.getComponent('views.dialogs.SessionRestoreErrorDialog'); diff --git a/src/RoomNotifs.js b/src/RoomNotifs.js index eddf516b20..5cc078dc59 100644 --- a/src/RoomNotifs.js +++ b/src/RoomNotifs.js @@ -87,7 +87,7 @@ function setRoomNotifsStateMuted(roomId) { ], })); - return q.all(promises); + return Promise.all(promises); } function setRoomNotifsStateUnmuted(roomId, newState) { @@ -126,7 +126,7 @@ function setRoomNotifsStateUnmuted(roomId, newState) { promises.push(cli.setPushRuleEnabled('global', 'room', roomId, true)); } - return q.all(promises); + return Promise.all(promises); } function findOverrideMuteRule(roomId) { diff --git a/src/ScalarAuthClient.js b/src/ScalarAuthClient.js index 0275dcd1f7..b1d17b93a9 100644 --- a/src/ScalarAuthClient.js +++ b/src/ScalarAuthClient.js @@ -53,7 +53,7 @@ class ScalarAuthClient { } exchangeForScalarToken(openid_token_object) { - var defer = q.defer(); + var defer = Promise.defer(); var scalar_rest_url = SdkConfig.get().integrations_rest_url; request({ diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index b4fd469441..b90cb53435 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -224,7 +224,7 @@ module.exports = React.createClass({ // Used by _viewRoom before getting state from sync this.firstSyncComplete = false; - this.firstSyncPromise = q.defer(); + this.firstSyncPromise = Promise.defer(); if (this.props.config.sync_timeline_limit) { MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit; @@ -1039,7 +1039,7 @@ module.exports = React.createClass({ // since we're about to start the client and therefore about // to do the first sync this.firstSyncComplete = false; - this.firstSyncPromise = q.defer(); + this.firstSyncPromise = Promise.defer(); const cli = MatrixClientPeg.get(); // Allow the JS SDK to reap timeline events. This reduces the amount of diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 3558e0c278..3cc0b40686 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -316,7 +316,7 @@ module.exports = React.createClass({ _refreshFromServer: function() { const self = this; - q.all([ + Promise.all([ UserSettingsStore.loadProfileInfo(), UserSettingsStore.loadThreePids(), ]).done(function(resps) { self.setState({ diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js index f8ebaefec8..d3a208a785 100644 --- a/src/components/views/dialogs/ChatInviteDialog.js +++ b/src/components/views/dialogs/ChatInviteDialog.js @@ -498,7 +498,7 @@ module.exports = React.createClass({ } // wait a bit to let the user finish typing - return q.delay(500).then(() => { + return Promise.delay(500).then(() => { if (cancelled) return null; return MatrixClientPeg.get().lookupThreePid(medium, address); }).then((res) => { diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.js index 0fb099422c..ba0663153e 100644 --- a/src/components/views/room_settings/AliasSettings.js +++ b/src/components/views/room_settings/AliasSettings.js @@ -104,7 +104,7 @@ module.exports = React.createClass({ } if (oldCanonicalAlias !== this.state.canonicalAlias) { console.log("AliasSettings: Updating canonical alias"); - promises = [q.all(promises).then( + promises = [Promise.all(promises).then( MatrixClientPeg.get().sendStateEvent( this.props.roomId, "m.room.canonical_alias", { alias: this.state.canonicalAlias diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index 10c9d405ac..1ea2eada7c 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -73,7 +73,7 @@ export default class Autocomplete extends React.Component { autocompleteDelay = 0; } - const deferred = Q.defer(); + const deferred = Promise.defer(); this.debounceCompletionsRequest = setTimeout(() => { this.processQuery(query, selection).then(() => { deferred.resolve(); @@ -176,7 +176,7 @@ export default class Autocomplete extends React.Component { } forceComplete() { - const done = Q.defer(); + const done = Promise.defer(); this.setState({ forceComplete: true, hide: false, diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 270b2a54d5..addd1f2e7f 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -184,7 +184,7 @@ module.exports = React.createClass({ }, save: function() { - var stateWasSetDefer = q.defer(); + var stateWasSetDefer = Promise.defer(); // the caller may have JUST called setState on stuff, so we need to re-render before saving // else we won't use the latest values of things. // We can be a bit cheeky here and set a loading flag, and listen for the callback on that @@ -282,7 +282,7 @@ module.exports = React.createClass({ // color scheme var p; p = this.saveColor(); - if (!q.isFulfilled(p)) { + if (!Promise.isFulfilled(p)) { promises.push(p); } @@ -294,7 +294,7 @@ module.exports = React.createClass({ // encryption p = this.saveEnableEncryption(); - if (!q.isFulfilled(p)) { + if (!Promise.isFulfilled(p)) { promises.push(p); } diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js index 9d91c007b1..14ec9806b4 100644 --- a/src/components/views/settings/ChangePassword.js +++ b/src/components/views/settings/ChangePassword.js @@ -161,7 +161,7 @@ module.exports = React.createClass({ }, _optionallySetEmail: function() { - const deferred = q.defer(); + 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.createDialog(SetEmailDialog, { diff --git a/src/languageHandler.js b/src/languageHandler.js index 7af4f7b200..e956d4f8bd 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -231,7 +231,7 @@ export function getCurrentLanguage() { } function getLangsJson() { - const deferred = q.defer(); + const deferred = Promise.defer(); request( { method: "GET", url: i18nFolder + 'languages.json' }, @@ -247,7 +247,7 @@ function getLangsJson() { } function getLanguage(langPath) { - const deferred = q.defer(); + const deferred = Promise.defer(); let response_return = {}; request( diff --git a/src/utils/DecryptFile.js b/src/utils/DecryptFile.js index 8054805409..04496e4204 100644 --- a/src/utils/DecryptFile.js +++ b/src/utils/DecryptFile.js @@ -28,7 +28,7 @@ import Promise from 'bluebird'; * @return {Promise} A promise that resolves with the data:// URI. */ export function readBlobAsDataUri(file) { - var deferred = q.defer(); + var deferred = Promise.defer(); var reader = new FileReader(); reader.onload = function(e) { deferred.resolve(e.target.result); diff --git a/src/utils/MultiInviter.js b/src/utils/MultiInviter.js index ecb5bd67f2..c26cc6b52e 100644 --- a/src/utils/MultiInviter.js +++ b/src/utils/MultiInviter.js @@ -55,7 +55,7 @@ export default class MultiInviter { this.errorTexts[addr] = 'Unrecognised address'; } } - this.deferred = q.defer(); + this.deferred = Promise.defer(); this._inviteMore(0); return this.deferred.promise; diff --git a/test/components/structures/ScrollPanel-test.js b/test/components/structures/ScrollPanel-test.js index 670633c6fd..e7d3d7b0e6 100644 --- a/test/components/structures/ScrollPanel-test.js +++ b/test/components/structures/ScrollPanel-test.js @@ -74,7 +74,7 @@ var Tester = React.createClass({ /* returns a promise which will resolve when the fill happens */ awaitFill: function(dir) { console.log("ScrollPanel Tester: awaiting " + dir + " fill"); - var defer = q.defer(); + var defer = Promise.defer(); this._fillDefers[dir] = defer; return defer.promise; }, @@ -94,7 +94,7 @@ var Tester = React.createClass({ /* returns a promise which will resolve when a scroll event happens */ awaitScroll: function() { console.log("Awaiting scroll"); - this._scrollDefer = q.defer(); + this._scrollDefer = Promise.defer(); return this._scrollDefer.promise; }, diff --git a/test/components/structures/TimelinePanel-test.js b/test/components/structures/TimelinePanel-test.js index 56bde195ee..98ec65b8e8 100644 --- a/test/components/structures/TimelinePanel-test.js +++ b/test/components/structures/TimelinePanel-test.js @@ -147,18 +147,18 @@ describe('TimelinePanel', function() { if(!panel.state.forwardPaginating) return Promise.resolve(); else - return q.delay(0).then(awaitPaginationCompletion); + return Promise.delay(0).then(awaitPaginationCompletion); }; // helper function which will return a promise which resolves when // the TimelinePanel fires a scroll event var awaitScroll = function() { - scrollDefer = q.defer(); + scrollDefer = Promise.defer(); return scrollDefer.promise; }; // let the first round of pagination finish off - q.delay(5).then(() => { + Promise.delay(5).then(() => { expect(panel.state.canBackPaginate).toBe(false); expect(scryEventTiles(panel).length).toEqual(N_EVENTS); @@ -279,7 +279,7 @@ describe('TimelinePanel', function() { // helper function which will return a promise which resolves when // the TimelinePanel fires a scroll event var awaitScroll = function() { - scrollDefer = q.defer(); + scrollDefer = Promise.defer(); return scrollDefer.promise.then(() => { console.log("got scroll event; scrollTop now " + diff --git a/test/components/views/dialogs/InteractiveAuthDialog-test.js b/test/components/views/dialogs/InteractiveAuthDialog-test.js index 969396865e..f606793132 100644 --- a/test/components/views/dialogs/InteractiveAuthDialog-test.js +++ b/test/components/views/dialogs/InteractiveAuthDialog-test.js @@ -110,7 +110,7 @@ describe('InteractiveAuthDialog', function () { ); // let the request complete - return q.delay(1); + return Promise.delay(1); }).then(() => { expect(onFinished.callCount).toEqual(1); expect(onFinished.calledWithExactly(true, {a:1})).toBe(true); diff --git a/test/components/views/rooms/MessageComposerInput-test.js b/test/components/views/rooms/MessageComposerInput-test.js index 2edc1c9432..fe379afcff 100644 --- a/test/components/views/rooms/MessageComposerInput-test.js +++ b/test/components/views/rooms/MessageComposerInput-test.js @@ -47,7 +47,7 @@ describe('MessageComposerInput', () => { // warnings // (please can we make the components not setState() after // they are unmounted?) - Q.delay(10).done(() => { + Promise.delay(10).done(() => { if (parentDiv) { ReactDOM.unmountComponentAtNode(parentDiv); parentDiv.remove(); From 33f330d43485f46424fea0700abd221999d4884e Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 12 Jul 2017 15:15:59 +0100 Subject: [PATCH 04/21] Manual fixup for remaining q incantaions * don't try to use `finally` as a static method * work around absence of `allSettled` --- src/components/structures/ScrollPanel.js | 15 ++++----------- src/components/structures/UserSettings.js | 11 ++++++----- src/components/views/rooms/RoomSettings.js | 18 +++++++++++++++++- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 95ab586e03..a8a2ec181b 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -386,19 +386,12 @@ module.exports = React.createClass({ debuglog("ScrollPanel: starting "+dir+" fill"); // onFillRequest can end up calling us recursively (via onScroll - // events) so make sure we set this before firing off the call. That - // does present the risk that we might not ever actually fire off the - // fill request, so wrap it in a try/catch. + // events) so make sure we set this before firing off the call. this._pendingFillRequests[dir] = true; - var fillPromise; - try { - fillPromise = this.props.onFillRequest(backwards); - } catch (e) { - this._pendingFillRequests[dir] = false; - throw e; - } - q.finally(fillPromise, () => { + Promise.try(() => { + return this.props.onFillRequest(backwards); + }).finally(() => { this._pendingFillRequests[dir] = false; }).then((hasMoreResults) => { if (this.unmounted) { diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 3cc0b40686..c5791aa933 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -568,15 +568,16 @@ module.exports = React.createClass({ }); // reject the invites const promises = rooms.map((room) => { - return MatrixClientPeg.get().leave(room.roomId); + return MatrixClientPeg.get().leave(room.roomId).catch((e) => { + // purposefully drop errors to the floor: we'll just have a non-zero number on the UI + // after trying to reject all the invites. + }); }); - // purposefully drop errors to the floor: we'll just have a non-zero number on the UI - // after trying to reject all the invites. - q.allSettled(promises).then(() => { + Promise.all(promises).then(() => { this.setState({ rejectingInvites: false, }); - }).done(); + }); }, _onExportE2eKeysClicked: function() { diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index addd1f2e7f..387aca2fa5 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -183,6 +183,12 @@ module.exports = React.createClass({ }); }, + /** + * Returns a promise which resolves once all of the save operations have completed or failed. + * + * The result is a list of promise state snapshots, each with the form + * `{ state: "fulfilled", value: v }` or `{ state: "rejected", reason: r }`. + */ save: function() { var stateWasSetDefer = Promise.defer(); // the caller may have JUST called setState on stuff, so we need to re-render before saving @@ -194,8 +200,18 @@ module.exports = React.createClass({ this.setState({ _loading: false}); }); + function mapPromiseToSnapshot(p) { + return p.then((r) => { + return { state: "fulfilled", value: r }; + }, (e) => { + return { state: "rejected", reason: e }; + }); + } + return stateWasSetDefer.promise.then(() => { - return q.allSettled(this._calcSavePromises()); + return Promise.all( + this._calcSavePromises().map(mapPromiseToSnapshot), + ); }); }, From 1e80edb43c442d5d2f3ff0e86d4afc806ad8f972 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 13 Jul 2017 11:30:25 +0100 Subject: [PATCH 05/21] Prevent exception on page unload Null check when saving sent history --- src/components/views/rooms/MessageComposer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 6993fd8f7d..d9bd8d2892 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -74,7 +74,7 @@ export default class MessageComposer extends React.Component { } onPageUnload(event) { - if (this.messageComposerInput) { + if (this.messageComposerInput && this.messageComposerInput.sentHistory) { this.messageComposerInput.sentHistory.saveLastTextEntry(); } } From 917133219f40a53821ef9dde5bf26061bd406c6d Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 13 Jul 2017 11:53:22 +0100 Subject: [PATCH 06/21] Remove sentHistory save altogether --- src/components/views/rooms/MessageComposer.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index d9bd8d2892..5c3008dcc6 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -41,7 +41,6 @@ export default class MessageComposer extends React.Component { this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this); this.onInputStateChanged = this.onInputStateChanged.bind(this); this.onEvent = this.onEvent.bind(this); - this.onPageUnload = this.onPageUnload.bind(this); this.state = { autocompleteQuery: '', @@ -62,21 +61,12 @@ export default class MessageComposer extends React.Component { // marked as encrypted. // XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something. MatrixClientPeg.get().on("event", this.onEvent); - - window.addEventListener('beforeunload', this.onPageUnload); } componentWillUnmount() { if (MatrixClientPeg.get()) { MatrixClientPeg.get().removeListener("event", this.onEvent); } - window.removeEventListener('beforeunload', this.onPageUnload); - } - - onPageUnload(event) { - if (this.messageComposerInput && this.messageComposerInput.sentHistory) { - this.messageComposerInput.sentHistory.saveLastTextEntry(); - } } onEvent(event) { From 55e1202c0951f41be97e38992140456a43e4e634 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 13 Jul 2017 13:26:13 +0100 Subject: [PATCH 07/21] Decorate pasted links so that they look like links By default, draftjs will represent pasted `` tags as `LINK` entities, but it doesn't do any default decoration of these links. Add a decorator to do so. Most of this was taken from https://github.com/facebook/draft-js/blob/v0.8.1/examples/link/link.html (note the version, v0.8.1). --- .../views/rooms/MessageComposerInput.js | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index cf6dfbb6b7..8ba5289eb4 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -16,9 +16,9 @@ limitations under the License. import React from 'react'; import type SyntheticKeyboardEvent from 'react/lib/SyntheticKeyboardEvent'; -import {Editor, EditorState, RichUtils, CompositeDecorator, - convertFromRaw, convertToRaw, Modifier, EditorChangeType, - getDefaultKeyBinding, KeyBindingUtil, ContentState, ContentBlock, SelectionState} from 'draft-js'; +import {Editor, EditorState, RichUtils, CompositeDecorator, Modifier, + getDefaultKeyBinding, KeyBindingUtil, ContentState, ContentBlock, SelectionState, + Entity} from 'draft-js'; import classNames from 'classnames'; import escape from 'lodash/escape'; @@ -144,15 +144,37 @@ export default class MessageComposerInput extends React.Component { this.client = MatrixClientPeg.get(); } + findLinkEntities(contentBlock, callback) { + contentBlock.findEntityRanges( + (character) => { + const entityKey = character.getEntity(); + return ( + entityKey !== null && + Entity.get(entityKey).getType() === 'LINK' + ); + }, callback, + ); + } /* * "Does the right thing" to create an EditorState, based on: * - whether we've got rich text mode enabled * - contentState was passed in */ createEditorState(richText: boolean, contentState: ?ContentState): EditorState { - let decorators = richText ? RichText.getScopedRTDecorators(this.props) : - RichText.getScopedMDDecorators(this.props), - compositeDecorator = new CompositeDecorator(decorators); + const decorators = richText ? RichText.getScopedRTDecorators(this.props) : + RichText.getScopedMDDecorators(this.props); + decorators.push({ + strategy: this.findLinkEntities.bind(this), + component: (props) => { + const {url} = Entity.get(props.entityKey).getData(); + return ( + + {props.children} + + ); + }, + }); + const compositeDecorator = new CompositeDecorator(decorators); let editorState = null; if (contentState) { From 4b969634083bb370da4f9457099fe6a03b8eb541 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 13 Jul 2017 13:27:49 +0100 Subject: [PATCH 08/21] Send HTML if there are any entities present in the composer This is so that pasted HTML links that are represented as entities are sent as HTML. --- src/components/views/rooms/MessageComposerInput.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 8ba5289eb4..5149d5a790 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -591,6 +591,14 @@ export default class MessageComposerInput extends React.Component { } }); } + if (!shouldSendHTML) { + const hasAnEntity = blocks.some((block) => { + return block.getCharacterList().filter((c) => { + return c.getEntity(); + }).size > 0; + }); + shouldSendHTML = hasAnEntity; + } if (shouldSendHTML) { contentHTML = HtmlUtils.processHtmlForSending( RichText.contentStateToHTML(contentState), From be045a6dc04d72cb01b3e3ec19d5d28cb6f15cbc Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 13 Jul 2017 13:28:51 +0100 Subject: [PATCH 09/21] Interpret whitespace after entity as the end of the entity The easiest way to stop the user from inserting whitespace onto the end of an entity is to toggle the entity state of the whitespace that was just entered. This allows the user to continue drafting a message without editing the link content. This is for pasted `` tags that have been copied from a website. We probably also want to be storing entities for substrings of content that are determined to be URLs. --- .../views/rooms/MessageComposerInput.js | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 5149d5a790..9886f8623f 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -329,6 +329,34 @@ export default class MessageComposerInput extends React.Component { onEditorContentChanged = (editorState: EditorState) => { editorState = RichText.attachImmutableEntitiesToEmoji(editorState); + const currentBlock = editorState.getSelection().getStartKey(); + const currentSelection = editorState.getSelection(); + const currentStartOffset = editorState.getSelection().getStartOffset(); + + const block = editorState.getCurrentContent().getBlockForKey(currentBlock); + const text = block.getText(); + + const entityBeforeCurrentOffset = block.getEntityAt(currentStartOffset - 1); + const entityAtCurrentOffset = block.getEntityAt(currentStartOffset); + + // If the cursor is on the boundary between an entity and a non-entity and the + // text before the cursor has whitespace at the end, set the entity state of the + // character before the cursor (the whitespace) to null. This allows the user to + // stop editing the link. + if (entityBeforeCurrentOffset && !entityAtCurrentOffset && + /\s$/.test(text.slice(0, currentStartOffset))) { + editorState = RichUtils.toggleLink( + editorState, + currentSelection.merge({ + anchorOffset: currentStartOffset - 1, + focusOffset: currentStartOffset, + }), + null, + ); + // Reset selection + editorState = EditorState.forceSelection(editorState, currentSelection); + } + /* Since a modification was made, set originalEditorState to null, since newState is now our original */ this.setState({ editorState, From 5826b6f22a3816bc7c1ed06794384266f411aaa2 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 13 Jul 2017 13:41:17 +0100 Subject: [PATCH 10/21] Instead of sending HTML for any Entity, only send HTML for Links Otherwise emoji messages are sent as HTML, needlessly --- src/components/views/rooms/MessageComposerInput.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 9886f8623f..c0cc937e24 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -620,12 +620,13 @@ export default class MessageComposerInput extends React.Component { }); } if (!shouldSendHTML) { - const hasAnEntity = blocks.some((block) => { + const hasLink = blocks.some((block) => { return block.getCharacterList().filter((c) => { - return c.getEntity(); + const entityKey = c.getEntity(); + return entityKey && Entity.get(entityKey).getType() === 'LINK'; }).size > 0; }); - shouldSendHTML = hasAnEntity; + shouldSendHTML = hasLink; } if (shouldSendHTML) { contentHTML = HtmlUtils.processHtmlForSending( From f1a4209d6be16012bf4044ed0f9d13e36f782ab1 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 13 Jul 2017 13:47:08 +0100 Subject: [PATCH 11/21] Fix indentation --- src/components/views/rooms/MessageComposerInput.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index c0cc937e24..452f25fd2e 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -164,13 +164,13 @@ export default class MessageComposerInput extends React.Component { const decorators = richText ? RichText.getScopedRTDecorators(this.props) : RichText.getScopedMDDecorators(this.props); decorators.push({ - strategy: this.findLinkEntities.bind(this), - component: (props) => { + strategy: this.findLinkEntities.bind(this), + component: (props) => { const {url} = Entity.get(props.entityKey).getData(); return ( - - {props.children} - + + {props.children} + ); }, }); From c9f3a12693345a5e591f6518fdabbe453096e5df Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 13 Jul 2017 16:51:14 +0100 Subject: [PATCH 12/21] Fix Autocompleter promises Use bluebird instead of the now removed "q" library. Also, make sure we timeout and then `reflect` to effectively do an `allSettled` that waits for all promises to either be resolved or rejected. Then we filter for those that are fulfilled and return the completions. --- src/autocomplete/Autocompleter.js | 44 ++++++++++++++++--------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/autocomplete/Autocompleter.js b/src/autocomplete/Autocompleter.js index 6b8cd66ae2..ec5d84f9a2 100644 --- a/src/autocomplete/Autocompleter.js +++ b/src/autocomplete/Autocompleter.js @@ -52,28 +52,30 @@ export async function getCompletions(query: string, selection: SelectionRange, f otherwise, we run into a condition where new completions are displayed while the user is interacting with the list, which makes it difficult to predict whether an action will actually do what is intended - - It ends up containing a list of Q promise states, which are objects with - state (== "fulfilled" || "rejected") and value. */ - const completionsList = await Q.allSettled( - PROVIDERS.map(provider => { - return Promise.resolve(provider.getCompletions(query, selection, force)) - .timeout(PROVIDER_COMPLETION_TIMEOUT); - }), + */ + const completionsList = await Promise.all( + // Array of inspections of promises that might timeout. Instead of allowing a + // single timeout to reject the Promise.all, reflect each one and once they've all + // settled, filter for the fulfilled ones + PROVIDERS.map((provider) => { + // Convert to bluebird promise so that we can do a timeout + const p = Promise.resolve(provider.getCompletions(query, selection, force)); + return p.timeout(PROVIDER_COMPLETION_TIMEOUT); + }).map((p) => p.reflect()), ); - return completionsList - .filter(completion => completion.state === "fulfilled") - .map((completionsState, i) => { - return { - completions: completionsState.value, - provider: PROVIDERS[i], + return completionsList.filter( + (inspection) => inspection.isFulfilled(), + ).map((completionsState, i) => { + return { + completions: completionsState.value(), + provider: PROVIDERS[i], - /* the currently matched "command" the completer tried to complete - * we pass this through so that Autocomplete can figure out when to - * re-show itself once hidden. - */ - command: PROVIDERS[i].getCurrentCommand(query, selection, force), - }; - }); + /* the currently matched "command" the completer tried to complete + * we pass this through so that Autocomplete can figure out when to + * re-show itself once hidden. + */ + command: PROVIDERS[i].getCurrentCommand(query, selection, force), + }; + }); } From 4d8315085594a447a6aa7596abc142e671e514dd Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 13 Jul 2017 17:10:08 +0100 Subject: [PATCH 13/21] Transform `async` functions to bluebird promises It turns out that the assertion made in https://github.com/matrix-org/matrix-react-sdk/pull/1213 about `async` functions returning bluebird promises was only correct when babel used an inline version of the `asyncToGenerator` helper; in react-sdk we are using `babel-transform-runtime` which means that we use a separate `babel-runtime/helpers/asyncToGenerator`, which returns a native (or core-js) Promise. This meant that we were still in the situation where some methods returned native Promises, and some bluebird ones, which is exactly the situation I wanted to resolve by switching to bluebird in the first place: in short, unless/until we get rid of all code which assumes Promises have a `done` method etc, we need to make sure that everything returns a bluebird promise. (Aside: there was debate over whether in the long term we should be trying to wean ourselves off bluebird promises by assuming all promises are native. The conclusion was that the complexity hit involved in doing so outweighed any benefit of a potential future migration away from bluebird). --- .babelrc | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.babelrc b/.babelrc index 8c7b66269d..6ba0e0dae0 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,4 @@ { "presets": ["react", "es2015", "es2016"], - "plugins": ["transform-class-properties", "transform-object-rest-spread", "transform-async-to-generator", "transform-runtime", "add-module-exports"] + "plugins": ["transform-class-properties", "transform-object-rest-spread", "transform-async-to-bluebird", "transform-runtime", "add-module-exports"] } diff --git a/package.json b/package.json index 831644083a..9daaa38da7 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "babel-eslint": "^6.1.2", "babel-loader": "^6.2.5", "babel-plugin-add-module-exports": "^0.2.1", - "babel-plugin-transform-async-to-generator": "^6.16.0", + "babel-plugin-transform-async-to-bluebird": "^1.1.1", "babel-plugin-transform-class-properties": "^6.16.0", "babel-plugin-transform-object-rest-spread": "^6.16.0", "babel-plugin-transform-runtime": "^6.15.0", From f78a49b3a9489f7c21889af72c077f7b12f17472 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 13 Jul 2017 17:20:17 +0100 Subject: [PATCH 14/21] Do `reflect` in the same `map` --- src/autocomplete/Autocompleter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autocomplete/Autocompleter.js b/src/autocomplete/Autocompleter.js index ec5d84f9a2..8d78aa448a 100644 --- a/src/autocomplete/Autocompleter.js +++ b/src/autocomplete/Autocompleter.js @@ -60,8 +60,8 @@ export async function getCompletions(query: string, selection: SelectionRange, f PROVIDERS.map((provider) => { // Convert to bluebird promise so that we can do a timeout const p = Promise.resolve(provider.getCompletions(query, selection, force)); - return p.timeout(PROVIDER_COMPLETION_TIMEOUT); - }).map((p) => p.reflect()), + return p.timeout(PROVIDER_COMPLETION_TIMEOUT).reflect(); + }), ); return completionsList.filter( From d97de4d57667a2cf4dff4bfe7803080e2ca54098 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 13 Jul 2017 17:37:43 +0100 Subject: [PATCH 15/21] Implement disabled-by-default setting for auto-replacement of plaintext emojis FTR a list of plaintexts and their unicode equivalents can be found here - https://github.com/vector-im/riot-web/issues/4554#issuecomment-314374303 Pressing space after a matching emoji will replace the plaintext emoji with the equivalent unicode emoji. --- src/components/structures/UserSettings.js | 4 +++ .../views/rooms/MessageComposerInput.js | 31 +++++++++++++++++++ src/i18n/strings/en_EN.json | 3 +- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index a172ca4dbf..45c52c7f1d 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -93,6 +93,10 @@ const SETTINGS_LABELS = [ id: 'enableSyntaxHighlightLanguageDetection', label: 'Enable automatic language detection for syntax highlighting', }, + { + id: 'MessageComposerInput.autoReplaceEmoji', + label: 'Automatically replace plain text Emoji', + }, /* { id: 'useFixedWidthFont', diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index abe63ad491..acd49f6fb4 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -43,6 +43,11 @@ import Markdown from '../../../Markdown'; import ComposerHistoryManager from '../../../ComposerHistoryManager'; import MessageComposerStore from '../../../stores/MessageComposerStore'; +import {asciiRegexp, shortnameToUnicode, emojioneList, asciiList, mapUnicodeToShort} from 'emojione'; +const EMOJI_SHORTNAMES = Object.keys(emojioneList); +const EMOJI_UNICODE_TO_SHORTNAME = mapUnicodeToShort(); +const REGEX_EMOJI_WHITESPACE = new RegExp('(' + asciiRegexp + ')\\s$'); + const TYPING_USER_TIMEOUT = 10000, TYPING_SERVER_TIMEOUT = 30000; const ZWS_CODE = 8203; @@ -369,6 +374,32 @@ export default class MessageComposerInput extends React.Component { editorState = EditorState.forceSelection(editorState, currentSelection); } + // Automatic replacement of plaintext emoji to Unicode emoji + if (UserSettingsStore.getSyncedSetting('MessageComposerInput.autoReplaceEmoji', false)) { + // The first matched group includes just the matched plaintext emoji + const emojiMatch = REGEX_EMOJI_WHITESPACE.exec(text.slice(0, currentStartOffset)); + if(emojiMatch) { + // plaintext -> hex unicode + const emojiUc = asciiList[emojiMatch[1]]; + // hex unicode -> shortname -> actual unicode + const unicodeEmoji = shortnameToUnicode(EMOJI_UNICODE_TO_SHORTNAME[emojiUc]); + const newContentState = Modifier.replaceText( + editorState.getCurrentContent(), + currentSelection.merge({ + anchorOffset: currentStartOffset - emojiMatch[0].length, + focusOffset: currentStartOffset, + }), + unicodeEmoji, + ); + editorState = EditorState.push( + editorState, + newContentState, + 'insert-characters', + ); + editorState = EditorState.forceSelection(editorState, newContentState.getSelectionAfter()); + } + } + /* Since a modification was made, set originalEditorState to null, since newState is now our original */ this.setState({ editorState, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e041649906..4841f2f0de 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -955,5 +955,6 @@ "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org.": "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org.", "Featured Rooms:": "Featured Rooms:", "Error whilst fetching joined groups": "Error whilst fetching joined groups", - "Featured Users:": "Featured Users:" + "Featured Users:": "Featured Users:", + "Automatically replace plain text Emoji": "Automatically replace plain text Emoji" } From dcc4db53f9feade28736d6334c6f91fa955ad9eb Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 13 Jul 2017 17:51:14 +0100 Subject: [PATCH 16/21] Async functions now return `bluebird` promises, remove redundant Promise.resolve --- src/autocomplete/Autocompleter.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/autocomplete/Autocompleter.js b/src/autocomplete/Autocompleter.js index 8d78aa448a..e47c5dd59c 100644 --- a/src/autocomplete/Autocompleter.js +++ b/src/autocomplete/Autocompleter.js @@ -58,9 +58,10 @@ export async function getCompletions(query: string, selection: SelectionRange, f // single timeout to reject the Promise.all, reflect each one and once they've all // settled, filter for the fulfilled ones PROVIDERS.map((provider) => { - // Convert to bluebird promise so that we can do a timeout - const p = Promise.resolve(provider.getCompletions(query, selection, force)); - return p.timeout(PROVIDER_COMPLETION_TIMEOUT).reflect(); + return provider + .getCompletions(query, selection, force) + .timeout(PROVIDER_COMPLETION_TIMEOUT) + .reflect(); }), ); From 219c00bbc315d7abde24a5c1a3846869ad5a19e1 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 13 Jul 2017 18:42:37 +0100 Subject: [PATCH 17/21] Add code-block to the list of blocks where hitting return will split-block Hitting return in a code-block will now split the block into two code blocks. (Holding shift will still insert a soft newline into the current block). We still need to make it a bit more obvious that consecutive code-blocks are not contiguous - https://github.com/vector-im/riot-web/issues/4535 --- src/components/views/rooms/MessageComposerInput.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index acd49f6fb4..3c422b7c8a 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -598,10 +598,13 @@ export default class MessageComposerInput extends React.Component { } const currentBlockType = RichUtils.getCurrentBlockType(this.state.editorState); - // If we're in any of these three types of blocks, shift enter should insert soft newlines - // And just enter should end the block - // XXX: Empirically enter does not end these blocks - if(['blockquote', 'unordered-list-item', 'ordered-list-item'].includes(currentBlockType)) { + if( + ['code-block', 'blockquote', 'unordered-list-item', 'ordered-list-item'] + .includes(currentBlockType) + ) { + // By returning false, we allow the default draft-js key binding to occur, + // which in this case invokes "split-block". This creates a new block of the + // same type, allowing the user to delete it with backspace. return false; } From 5274cf59a0ed8fb8c03bb728b7c8045b9dce85c6 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Fri, 14 Jul 2017 11:17:59 +0100 Subject: [PATCH 18/21] Hook up the edit widget button (#1218) * Hook up the edit widget button * Review comments --- src/ScalarMessaging.js | 2 +- src/components/views/elements/AppTile.js | 24 ++++++++++++++++++------ src/components/views/rooms/AppsDrawer.js | 1 + 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 88a78595d6..d14d439d66 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -491,7 +491,7 @@ function canSendEvent(event, roomId) { } if (!canSend) { - sendError(event, _t('You do not have permission in this room.')); + sendError(event, _t('You do not have permission to do that in this room.')); return; } diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 3c3f01c1cd..effe50643c 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -21,7 +21,9 @@ import React from 'react'; import MatrixClientPeg from '../../../MatrixClientPeg'; import ScalarAuthClient from '../../../ScalarAuthClient'; import SdkConfig from '../../../SdkConfig'; +import Modal from '../../../Modal'; import { _t } from '../../../languageHandler'; +import sdk from '../../../index'; const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:']; @@ -33,6 +35,7 @@ export default React.createClass({ url: React.PropTypes.string.isRequired, name: React.PropTypes.string.isRequired, room: React.PropTypes.object.isRequired, + type: React.PropTypes.string.isRequired, }, getDefaultProps: function() { @@ -86,8 +89,13 @@ export default React.createClass({ }); }, - _onEditClick: function() { - console.log("Edit widget %s", this.props.id); + _onEditClick: function(e) { + console.log("Edit widget ID ", this.props.id); + const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager"); + const src = this._scalarClient.getScalarInterfaceUrlForRoom(this.props.room.roomId, 'type_' + this.props.type); + Modal.createDialog(IntegrationsManager, { + src: src, + }, "mx_IntegrationsManager"); }, _onDeleteClick: function() { @@ -120,10 +128,10 @@ export default React.createClass({
Loading...
); } else { - // Note that there is advice saying allow-scripts shouldn;t be used with allow-same-origin + // Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin // because that would allow the iframe to prgramatically remove the sandbox attribute, but // this would only be for content hosted on the same origin as the riot client: anything - // hosted on the same origin as the client will get the same access access as if you clicked + // hosted on the same origin as the client will get the same access as if you clicked // a link to it. const sandboxFlags = "allow-forms allow-popups allow-popups-to-escape-sandbox "+ "allow-same-origin allow-scripts"; @@ -140,18 +148,22 @@ export default React.createClass({ ); } + + // editing is done in scalar + const showEditButton = Boolean(this._scalarClient); + return (
{this.formatAppTileName()} {/* Edit widget */} - {/* Edit */} + />} {/* Delete widget */} Date: Fri, 14 Jul 2017 14:24:35 +0100 Subject: [PATCH 19/21] Fix RoomSettings save Looks like saving RoomSettings had been broken since 0d7cc59. `isFulfilled` cannot be used as a static function. --- src/components/views/rooms/RoomSettings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 387aca2fa5..d6a973f648 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -298,7 +298,7 @@ module.exports = React.createClass({ // color scheme var p; p = this.saveColor(); - if (!Promise.isFulfilled(p)) { + if (!p.isFulfilled()) { promises.push(p); } @@ -310,7 +310,7 @@ module.exports = React.createClass({ // encryption p = this.saveEnableEncryption(); - if (!Promise.isFulfilled(p)) { + if (!p.isFulfilled()) { promises.push(p); } From ad085a62735d285e79c052b441fba5f6fe7dfdd4 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 14 Jul 2017 17:01:03 +0100 Subject: [PATCH 20/21] Fix file uploading File uploading no longer supports the .progress API; we now need to pass a callback to get updates on the upload. --- src/ContentMessages.js | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/ContentMessages.js b/src/ContentMessages.js index 93013cfd6c..9239de9d8f 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -229,11 +229,13 @@ function readFileAsArrayBuffer(file) { * @param {MatrixClient} matrixClient The matrix client to upload the file with. * @param {String} roomId The ID of the room being uploaded to. * @param {File} file The file to upload. + * @param {Function?} progressHandler optional callback to be called when a chunk of + * data is uploaded. * @return {Promise} A promise that resolves with an object. * If the file is unencrypted then the object will have a "url" key. * If the file is encrypted then the object will have a "file" key. */ -function uploadFile(matrixClient, roomId, file) { +function uploadFile(matrixClient, roomId, file, progressHandler) { if (matrixClient.isRoomEncrypted(roomId)) { // If the room is encrypted then encrypt the file before uploading it. // First read the file into memory. @@ -245,7 +247,9 @@ function uploadFile(matrixClient, roomId, file) { const encryptInfo = encryptResult.info; // Pass the encrypted data as a Blob to the uploader. const blob = new Blob([encryptResult.data]); - return matrixClient.uploadContent(blob).then(function(url) { + return matrixClient.uploadContent(blob, { + progressHandler: progressHandler, + }).then(function(url) { // If the attachment is encrypted then bundle the URL along // with the information needed to decrypt the attachment and // add it under a file key. @@ -257,7 +261,9 @@ function uploadFile(matrixClient, roomId, file) { }); }); } else { - const basePromise = matrixClient.uploadContent(file); + const basePromise = matrixClient.uploadContent(file, { + progressHandler: progressHandler, + }); const promise1 = basePromise.then(function(url) { // If the attachment isn't encrypted then include the URL directly. return {"url": url}; @@ -326,23 +332,24 @@ class ContentMessages { dis.dispatch({action: 'upload_started'}); var error; + + function onProgress(ev) { + upload.total = ev.total; + upload.loaded = ev.loaded; + dis.dispatch({action: 'upload_progress', upload: upload}); + } + return def.promise.then(function() { // XXX: upload.promise must be the promise that // is returned by uploadFile as it has an abort() // method hacked onto it. upload.promise = uploadFile( - matrixClient, roomId, file + matrixClient, roomId, file, onProgress, ); return upload.promise.then(function(result) { content.file = result.file; content.url = result.url; }); - }).progress(function(ev) { - if (ev) { - upload.total = ev.total; - upload.loaded = ev.loaded; - dis.dispatch({action: 'upload_progress', upload: upload}); - } }).then(function(url) { return matrixClient.sendMessage(roomId, content); }, function(err) { From 434660166cd59f469dc39c1ccf62e3ae124e8fdd Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 14 Jul 2017 17:04:28 +0100 Subject: [PATCH 21/21] Decorate http://matrix.to links in the composer as pills Any links in the composer that are recognised as matrix.to links will be decorated as ``s with CSS classes (one of mx_UserPill or mx_RoomPill). This implementation has the nice bonus that switching to markdown (and back) will Just Work. This will have some CSS changes coming to better match the design. --- .../views/rooms/MessageComposerInput.js | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 3c422b7c8a..d4ae55f03a 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -26,6 +26,7 @@ import Promise from 'bluebird'; import MatrixClientPeg from '../../../MatrixClientPeg'; import type {MatrixClient} from 'matrix-js-sdk/lib/matrix'; +import {RoomMember} from 'matrix-js-sdk'; import SlashCommands from '../../../SlashCommands'; import KeyCode from '../../../KeyCode'; import Modal from '../../../Modal'; @@ -43,6 +44,9 @@ import Markdown from '../../../Markdown'; import ComposerHistoryManager from '../../../ComposerHistoryManager'; import MessageComposerStore from '../../../stores/MessageComposerStore'; +import {MATRIXTO_URL_PATTERN} from '../../../linkify-matrix'; +const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN); + import {asciiRegexp, shortnameToUnicode, emojioneList, asciiList, mapUnicodeToShort} from 'emojione'; const EMOJI_SHORTNAMES = Object.keys(emojioneList); const EMOJI_UNICODE_TO_SHORTNAME = mapUnicodeToShort(); @@ -183,7 +187,42 @@ export default class MessageComposerInput extends React.Component { decorators.push({ strategy: this.findLinkEntities.bind(this), component: (props) => { + const MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); + const RoomAvatar = sdk.getComponent('avatars.RoomAvatar'); const {url} = Entity.get(props.entityKey).getData(); + const matrixToMatch = REGEX_MATRIXTO.exec(url); + const isUserPill = matrixToMatch[2] === '@'; + const isRoomPill = matrixToMatch[2] === '#' || matrixToMatch[2] === '!'; + + const classes = classNames({ + "mx_UserPill": isUserPill, + "mx_RoomPill": isRoomPill, + }); + + let avatar = null; + if (isUserPill) { + // If this user is not a member of this room, default to the empty + // member. This could be improved by doing an async profile lookup. + const member = this.props.room.getMember(matrixToMatch[1]) || + new RoomMember(null, matrixToMatch[1]); + avatar = member ? : null; + } else if (isRoomPill) { + const room = matrixToMatch[2] === '#' ? + MatrixClientPeg.get().getRooms().find((r) => { + return r.getCanonicalAlias() === matrixToMatch[1]; + }) : MatrixClientPeg.get().getRoom(matrixToMatch[1]); + avatar = room ? : null; + } + + if (isUserPill || isRoomPill) { + return ( + + {avatar} + {props.children} + + ); + } + return ( {props.children}