Merge remote-tracking branch 'origin/develop' into dbkr/groupview_edit

This commit is contained in:
David Baker 2017-07-17 17:23:02 +01:00
commit 7041106bf2
44 changed files with 363 additions and 203 deletions

View file

@ -1,4 +1,4 @@
{ {
"presets": ["react", "es2015", "es2016"], "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"]
} }

View file

@ -46,6 +46,7 @@
}, },
"dependencies": { "dependencies": {
"babel-runtime": "^6.11.6", "babel-runtime": "^6.11.6",
"bluebird": "^3.5.0",
"blueimp-canvas-to-blob": "^3.5.0", "blueimp-canvas-to-blob": "^3.5.0",
"browser-encrypt-attachment": "^0.3.0", "browser-encrypt-attachment": "^0.3.0",
"browser-request": "^0.3.3", "browser-request": "^0.3.3",
@ -68,7 +69,6 @@
"matrix-js-sdk": "matrix-org/matrix-js-sdk#develop", "matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
"optimist": "^0.6.1", "optimist": "^0.6.1",
"prop-types": "^15.5.8", "prop-types": "^15.5.8",
"q": "^1.4.1",
"react": "^15.4.0", "react": "^15.4.0",
"react-addons-css-transition-group": "15.3.2", "react-addons-css-transition-group": "15.3.2",
"react-dom": "^15.4.0", "react-dom": "^15.4.0",
@ -85,7 +85,7 @@
"babel-eslint": "^6.1.2", "babel-eslint": "^6.1.2",
"babel-loader": "^6.2.5", "babel-loader": "^6.2.5",
"babel-plugin-add-module-exports": "^0.2.1", "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-class-properties": "^6.16.0",
"babel-plugin-transform-object-rest-spread": "^6.16.0", "babel-plugin-transform-object-rest-spread": "^6.16.0",
"babel-plugin-transform-runtime": "^6.15.0", "babel-plugin-transform-runtime": "^6.15.0",

View file

@ -16,7 +16,7 @@ limitations under the License.
'use strict'; 'use strict';
var q = require('q'); import Promise from 'bluebird';
var extend = require('./extend'); var extend = require('./extend');
var dis = require('./dispatcher'); var dis = require('./dispatcher');
var MatrixClientPeg = require('./MatrixClientPeg'); var MatrixClientPeg = require('./MatrixClientPeg');
@ -52,7 +52,7 @@ const MAX_HEIGHT = 600;
* and a thumbnail key. * and a thumbnail key.
*/ */
function createThumbnail(element, inputWidth, inputHeight, mimeType) { function createThumbnail(element, inputWidth, inputHeight, mimeType) {
const deferred = q.defer(); const deferred = Promise.defer();
var targetWidth = inputWidth; var targetWidth = inputWidth;
var targetHeight = inputHeight; var targetHeight = inputHeight;
@ -95,7 +95,7 @@ function createThumbnail(element, inputWidth, inputHeight, mimeType) {
* @return {Promise} A promise that resolves with the html image element. * @return {Promise} A promise that resolves with the html image element.
*/ */
function loadImageElement(imageFile) { function loadImageElement(imageFile) {
const deferred = q.defer(); const deferred = Promise.defer();
// Load the file into an html element // Load the file into an html element
const img = document.createElement("img"); 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. * @return {Promise} A promise that resolves with the video image element.
*/ */
function loadVideoElement(videoFile) { function loadVideoElement(videoFile) {
const deferred = q.defer(); const deferred = Promise.defer();
// Load the file into an html element // Load the file into an html element
const video = document.createElement("video"); const video = document.createElement("video");
@ -210,7 +210,7 @@ function infoForVideoFile(matrixClient, roomId, videoFile) {
* is read. * is read.
*/ */
function readFileAsArrayBuffer(file) { function readFileAsArrayBuffer(file) {
const deferred = q.defer(); const deferred = Promise.defer();
const reader = new FileReader(); const reader = new FileReader();
reader.onload = function(e) { reader.onload = function(e) {
deferred.resolve(e.target.result); deferred.resolve(e.target.result);
@ -229,11 +229,13 @@ function readFileAsArrayBuffer(file) {
* @param {MatrixClient} matrixClient The matrix client to upload the file with. * @param {MatrixClient} matrixClient The matrix client to upload the file with.
* @param {String} roomId The ID of the room being uploaded to. * @param {String} roomId The ID of the room being uploaded to.
* @param {File} file The file to upload. * @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. * @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 unencrypted then the object will have a "url" key.
* If the file is encrypted then the object will have a "file" 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 (matrixClient.isRoomEncrypted(roomId)) {
// If the room is encrypted then encrypt the file before uploading it. // If the room is encrypted then encrypt the file before uploading it.
// First read the file into memory. // First read the file into memory.
@ -245,7 +247,9 @@ function uploadFile(matrixClient, roomId, file) {
const encryptInfo = encryptResult.info; const encryptInfo = encryptResult.info;
// Pass the encrypted data as a Blob to the uploader. // Pass the encrypted data as a Blob to the uploader.
const blob = new Blob([encryptResult.data]); 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 // If the attachment is encrypted then bundle the URL along
// with the information needed to decrypt the attachment and // with the information needed to decrypt the attachment and
// add it under a file key. // add it under a file key.
@ -257,7 +261,9 @@ function uploadFile(matrixClient, roomId, file) {
}); });
}); });
} else { } else {
const basePromise = matrixClient.uploadContent(file); const basePromise = matrixClient.uploadContent(file, {
progressHandler: progressHandler,
});
const promise1 = basePromise.then(function(url) { const promise1 = basePromise.then(function(url) {
// If the attachment isn't encrypted then include the URL directly. // If the attachment isn't encrypted then include the URL directly.
return {"url": url}; return {"url": url};
@ -288,7 +294,7 @@ class ContentMessages {
content.info.mimetype = file.type; content.info.mimetype = file.type;
} }
const def = q.defer(); const def = Promise.defer();
if (file.type.indexOf('image/') == 0) { if (file.type.indexOf('image/') == 0) {
content.msgtype = 'm.image'; content.msgtype = 'm.image';
infoForImageFile(matrixClient, roomId, file).then(imageInfo=>{ infoForImageFile(matrixClient, roomId, file).then(imageInfo=>{
@ -326,23 +332,24 @@ class ContentMessages {
dis.dispatch({action: 'upload_started'}); dis.dispatch({action: 'upload_started'});
var error; 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() { return def.promise.then(function() {
// XXX: upload.promise must be the promise that // XXX: upload.promise must be the promise that
// is returned by uploadFile as it has an abort() // is returned by uploadFile as it has an abort()
// method hacked onto it. // method hacked onto it.
upload.promise = uploadFile( upload.promise = uploadFile(
matrixClient, roomId, file matrixClient, roomId, file, onProgress,
); );
return upload.promise.then(function(result) { return upload.promise.then(function(result) {
content.file = result.file; content.file = result.file;
content.url = result.url; 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) { }).then(function(url) {
return matrixClient.sendMessage(roomId, content); return matrixClient.sendMessage(roomId, content);
}, function(err) { }, function(err) {

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import q from 'q'; import Promise from 'bluebird';
import Matrix from 'matrix-js-sdk'; import Matrix from 'matrix-js-sdk';
import MatrixClientPeg from './MatrixClientPeg'; import MatrixClientPeg from './MatrixClientPeg';
@ -116,12 +116,12 @@ export function loadSession(opts) {
*/ */
export function attemptTokenLogin(queryParams, defaultDeviceDisplayName) { export function attemptTokenLogin(queryParams, defaultDeviceDisplayName) {
if (!queryParams.loginToken) { if (!queryParams.loginToken) {
return q(false); return Promise.resolve(false);
} }
if (!queryParams.homeserver) { if (!queryParams.homeserver) {
console.warn("Cannot log in with token: can't determine HS URL to use"); 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 // create a temporary MatrixClient to do the login
@ -197,7 +197,7 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) {
// localStorage (e.g. teamToken, isGuest etc.) // localStorage (e.g. teamToken, isGuest etc.)
function _restoreFromLocalStorage() { function _restoreFromLocalStorage() {
if (!localStorage) { if (!localStorage) {
return q(false); return Promise.resolve(false);
} }
const hsUrl = localStorage.getItem("mx_hs_url"); const hsUrl = localStorage.getItem("mx_hs_url");
const isUrl = localStorage.getItem("mx_is_url") || 'https://matrix.org'; const isUrl = localStorage.getItem("mx_is_url") || 'https://matrix.org';
@ -229,14 +229,14 @@ function _restoreFromLocalStorage() {
} }
} else { } else {
console.log("No previous session found."); console.log("No previous session found.");
return q(false); return Promise.resolve(false);
} }
} }
function _handleRestoreFailure(e) { function _handleRestoreFailure(e) {
console.log("Unable to restore session", e); console.log("Unable to restore session", e);
const def = q.defer(); const def = Promise.defer();
const SessionRestoreErrorDialog = const SessionRestoreErrorDialog =
sdk.getComponent('views.dialogs.SessionRestoreErrorDialog'); sdk.getComponent('views.dialogs.SessionRestoreErrorDialog');

View file

@ -18,7 +18,7 @@ limitations under the License.
import Matrix from "matrix-js-sdk"; import Matrix from "matrix-js-sdk";
import { _t } from "./languageHandler"; import { _t } from "./languageHandler";
import q from 'q'; import Promise from 'bluebird';
import url from 'url'; import url from 'url';
export default class Login { export default class Login {
@ -144,7 +144,7 @@ export default class Login {
const client = this._createTemporaryClient(); const client = this._createTemporaryClient();
return client.login('m.login.password', loginParams).then(function(data) { return client.login('m.login.password', loginParams).then(function(data) {
return q({ return Promise.resolve({
homeserverUrl: self._hsUrl, homeserverUrl: self._hsUrl,
identityServerUrl: self._isUrl, identityServerUrl: self._isUrl,
userId: data.user_id, userId: data.user_id,
@ -160,7 +160,7 @@ export default class Login {
}); });
return fbClient.login('m.login.password', loginParams).then(function(data) { return fbClient.login('m.login.password', loginParams).then(function(data) {
return q({ return Promise.resolve({
homeserverUrl: self._fallbackHsUrl, homeserverUrl: self._fallbackHsUrl,
identityServerUrl: self._isUrl, identityServerUrl: self._isUrl,
userId: data.user_id, userId: data.user_id,

View file

@ -16,7 +16,7 @@ limitations under the License.
import MatrixClientPeg from './MatrixClientPeg'; import MatrixClientPeg from './MatrixClientPeg';
import PushProcessor from 'matrix-js-sdk/lib/pushprocessor'; 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_LOUD = 'all_messages_loud';
export const ALL_MESSAGES = 'all_messages'; export const ALL_MESSAGES = 'all_messages';
@ -87,7 +87,7 @@ function setRoomNotifsStateMuted(roomId) {
], ],
})); }));
return q.all(promises); return Promise.all(promises);
} }
function setRoomNotifsStateUnmuted(roomId, newState) { function setRoomNotifsStateUnmuted(roomId, newState) {
@ -126,7 +126,7 @@ function setRoomNotifsStateUnmuted(roomId, newState) {
promises.push(cli.setPushRuleEnabled('global', 'room', roomId, true)); promises.push(cli.setPushRuleEnabled('global', 'room', roomId, true));
} }
return q.all(promises); return Promise.all(promises);
} }
function findOverrideMuteRule(roomId) { function findOverrideMuteRule(roomId) {

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import MatrixClientPeg from './MatrixClientPeg'; import MatrixClientPeg from './MatrixClientPeg';
import q from 'q'; import Promise from 'bluebird';
/** /**
* Given a room object, return the alias we should use for it, * Given a room object, return the alias we should use for it,
@ -102,7 +102,7 @@ export function guessAndSetDMRoom(room, isDirect) {
*/ */
export function setDMRoom(roomId, userId) { export function setDMRoom(roomId, userId) {
if (MatrixClientPeg.get().isGuest()) { if (MatrixClientPeg.get().isGuest()) {
return q(); return Promise.resolve();
} }
const mDirectEvent = MatrixClientPeg.get().getAccountData('m.direct'); const mDirectEvent = MatrixClientPeg.get().getAccountData('m.direct');

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
var q = require("q"); import Promise from 'bluebird';
var request = require('browser-request'); var request = require('browser-request');
var SdkConfig = require('./SdkConfig'); var SdkConfig = require('./SdkConfig');
@ -39,7 +39,7 @@ class ScalarAuthClient {
// Returns a scalar_token string // Returns a scalar_token string
getScalarToken() { getScalarToken() {
var tok = window.localStorage.getItem("mx_scalar_token"); 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 // No saved token, so do the dance to get one. First, we
// need an openid bearer token from the HS. // need an openid bearer token from the HS.
@ -53,7 +53,7 @@ class ScalarAuthClient {
} }
exchangeForScalarToken(openid_token_object) { exchangeForScalarToken(openid_token_object) {
var defer = q.defer(); var defer = Promise.defer();
var scalar_rest_url = SdkConfig.get().integrations_rest_url; var scalar_rest_url = SdkConfig.get().integrations_rest_url;
request({ request({

View file

@ -491,7 +491,7 @@ function canSendEvent(event, roomId) {
} }
if (!canSend) { 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; return;
} }

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import q from 'q'; import Promise from 'bluebird';
import MatrixClientPeg from './MatrixClientPeg'; import MatrixClientPeg from './MatrixClientPeg';
import Notifier from './Notifier'; import Notifier from './Notifier';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
@ -48,7 +48,7 @@ export default {
loadThreePids: function() { loadThreePids: function() {
if (MatrixClientPeg.get().isGuest()) { if (MatrixClientPeg.get().isGuest()) {
return q({ return Promise.resolve({
threepids: [], threepids: [],
}); // guests can't poke 3pid endpoint }); // guests can't poke 3pid endpoint
} }

View file

@ -22,7 +22,7 @@ import DuckDuckGoProvider from './DuckDuckGoProvider';
import RoomProvider from './RoomProvider'; import RoomProvider from './RoomProvider';
import UserProvider from './UserProvider'; import UserProvider from './UserProvider';
import EmojiProvider from './EmojiProvider'; import EmojiProvider from './EmojiProvider';
import Q from 'q'; import Promise from 'bluebird';
export type SelectionRange = { export type SelectionRange = {
start: number, start: number,
@ -52,28 +52,31 @@ export async function getCompletions(query: string, selection: SelectionRange, f
otherwise, we run into a condition where new completions are displayed otherwise, we run into a condition where new completions are displayed
while the user is interacting with the list, which makes it difficult while the user is interacting with the list, which makes it difficult
to predict whether an action will actually do what is intended 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 const completionsList = await Promise.all(
state (== "fulfilled" || "rejected") and value. */ // Array of inspections of promises that might timeout. Instead of allowing a
const completionsList = await Q.allSettled( // single timeout to reject the Promise.all, reflect each one and once they've all
PROVIDERS.map(provider => { // settled, filter for the fulfilled ones
return Q(provider.getCompletions(query, selection, force)) PROVIDERS.map((provider) => {
.timeout(PROVIDER_COMPLETION_TIMEOUT); return provider
.getCompletions(query, selection, force)
.timeout(PROVIDER_COMPLETION_TIMEOUT)
.reflect();
}), }),
); );
return completionsList return completionsList.filter(
.filter(completion => completion.state === "fulfilled") (inspection) => inspection.isFulfilled(),
.map((completionsState, i) => { ).map((completionsState, i) => {
return { return {
completions: completionsState.value, completions: completionsState.value(),
provider: PROVIDERS[i], provider: PROVIDERS[i],
/* the currently matched "command" the completer tried to complete /* the currently matched "command" the completer tried to complete
* we pass this through so that Autocomplete can figure out when to * we pass this through so that Autocomplete can figure out when to
* re-show itself once hidden. * re-show itself once hidden.
*/ */
command: PROVIDERS[i].getCurrentCommand(query, selection, force), command: PROVIDERS[i].getCurrentCommand(query, selection, force),
}; };
}); });
} }

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import q from 'q'; import Promise from 'bluebird';
import React from 'react'; import React from 'react';
import Matrix from "matrix-js-sdk"; import Matrix from "matrix-js-sdk";
@ -224,7 +224,7 @@ module.exports = React.createClass({
// Used by _viewRoom before getting state from sync // Used by _viewRoom before getting state from sync
this.firstSyncComplete = false; this.firstSyncComplete = false;
this.firstSyncPromise = q.defer(); this.firstSyncPromise = Promise.defer();
if (this.props.config.sync_timeline_limit) { if (this.props.config.sync_timeline_limit) {
MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit; MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit;
@ -323,9 +323,9 @@ module.exports = React.createClass({
return; 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. // asynchronous ones.
return q().then(() => { return Promise.resolve().then(() => {
return Lifecycle.loadSession({ return Lifecycle.loadSession({
fragmentQueryParams: this.props.startingFragmentQueryParams, fragmentQueryParams: this.props.startingFragmentQueryParams,
enableGuest: this.props.enableGuest, 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, // Wait for the first sync to complete so that if a room does have an alias,
// it would have been retrieved. // it would have been retrieved.
let waitFor = q(null); let waitFor = Promise.resolve(null);
if (!this.firstSyncComplete) { if (!this.firstSyncComplete) {
if (!this.firstSyncPromise) { if (!this.firstSyncPromise) {
console.warn('Cannot view a room before first sync. room_id:', roomInfo.room_id); console.warn('Cannot view a room before first sync. room_id:', roomInfo.room_id);
@ -1039,7 +1039,7 @@ module.exports = React.createClass({
// since we're about to start the client and therefore about // since we're about to start the client and therefore about
// to do the first sync // to do the first sync
this.firstSyncComplete = false; this.firstSyncComplete = false;
this.firstSyncPromise = q.defer(); this.firstSyncPromise = Promise.defer();
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
// Allow the JS SDK to reap timeline events. This reduces the amount of // Allow the JS SDK to reap timeline events. This reduces the amount of

View file

@ -22,7 +22,7 @@ limitations under the License.
var React = require("react"); var React = require("react");
var ReactDOM = require("react-dom"); var ReactDOM = require("react-dom");
var q = require("q"); import Promise from 'bluebird';
var classNames = require("classnames"); var classNames = require("classnames");
var Matrix = require("matrix-js-sdk"); var Matrix = require("matrix-js-sdk");
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
@ -775,7 +775,7 @@ module.exports = React.createClass({
onSearchResultsFillRequest: function(backwards) { onSearchResultsFillRequest: function(backwards) {
if (!backwards) { if (!backwards) {
return q(false); return Promise.resolve(false);
} }
if (this.state.searchResults.next_batch) { if (this.state.searchResults.next_batch) {
@ -785,7 +785,7 @@ module.exports = React.createClass({
return this._handleSearchResult(searchPromise); return this._handleSearchResult(searchPromise);
} else { } else {
debuglog("no more search results"); debuglog("no more search results");
return q(false); return Promise.resolve(false);
} }
}, },
@ -846,7 +846,7 @@ module.exports = React.createClass({
return; return;
} }
q().then(() => { Promise.resolve().then(() => {
const signUrl = this.props.thirdPartyInvite ? const signUrl = this.props.thirdPartyInvite ?
this.props.thirdPartyInvite.inviteSignUrl : undefined; this.props.thirdPartyInvite.inviteSignUrl : undefined;
dis.dispatch({ dis.dispatch({
@ -865,7 +865,7 @@ module.exports = React.createClass({
} }
} }
} }
return q(); return Promise.resolve();
}); });
}, },

View file

@ -17,7 +17,7 @@ limitations under the License.
var React = require("react"); var React = require("react");
var ReactDOM = require("react-dom"); var ReactDOM = require("react-dom");
var GeminiScrollbar = require('react-gemini-scrollbar'); var GeminiScrollbar = require('react-gemini-scrollbar');
var q = require("q"); import Promise from 'bluebird';
var KeyCode = require('../../KeyCode'); var KeyCode = require('../../KeyCode');
var DEBUG_SCROLL = false; var DEBUG_SCROLL = false;
@ -145,7 +145,7 @@ module.exports = React.createClass({
return { return {
stickyBottom: true, stickyBottom: true,
startAtBottom: true, startAtBottom: true,
onFillRequest: function(backwards) { return q(false); }, onFillRequest: function(backwards) { return Promise.resolve(false); },
onUnfillRequest: function(backwards, scrollToken) {}, onUnfillRequest: function(backwards, scrollToken) {},
onScroll: function() {}, onScroll: function() {},
}; };
@ -386,19 +386,12 @@ module.exports = React.createClass({
debuglog("ScrollPanel: starting "+dir+" fill"); debuglog("ScrollPanel: starting "+dir+" fill");
// onFillRequest can end up calling us recursively (via onScroll // onFillRequest can end up calling us recursively (via onScroll
// events) so make sure we set this before firing off the call. That // events) so make sure we set this before firing off the call.
// does present the risk that we might not ever actually fire off the
// fill request, so wrap it in a try/catch.
this._pendingFillRequests[dir] = true; 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; this._pendingFillRequests[dir] = false;
}).then((hasMoreResults) => { }).then((hasMoreResults) => {
if (this.unmounted) { if (this.unmounted) {

View file

@ -17,7 +17,7 @@ limitations under the License.
var React = require('react'); var React = require('react');
var ReactDOM = require("react-dom"); var ReactDOM = require("react-dom");
var q = require("q"); import Promise from 'bluebird';
var Matrix = require("matrix-js-sdk"); var Matrix = require("matrix-js-sdk");
var EventTimeline = Matrix.EventTimeline; var EventTimeline = Matrix.EventTimeline;
@ -311,13 +311,13 @@ var TimelinePanel = React.createClass({
if (!this.state[canPaginateKey]) { if (!this.state[canPaginateKey]) {
debuglog("TimelinePanel: have given up", dir, "paginating this timeline"); debuglog("TimelinePanel: have given up", dir, "paginating this timeline");
return q(false); return Promise.resolve(false);
} }
if(!this._timelineWindow.canPaginate(dir)) { if(!this._timelineWindow.canPaginate(dir)) {
debuglog("TimelinePanel: can't", dir, "paginate any further"); debuglog("TimelinePanel: can't", dir, "paginate any further");
this.setState({[canPaginateKey]: false}); this.setState({[canPaginateKey]: false});
return q(false); return Promise.resolve(false);
} }
debuglog("TimelinePanel: Initiating paginate; backwards:"+backwards); debuglog("TimelinePanel: Initiating paginate; backwards:"+backwards);

View file

@ -22,7 +22,7 @@ const PlatformPeg = require("../../PlatformPeg");
const Modal = require('../../Modal'); const Modal = require('../../Modal');
const dis = require("../../dispatcher"); const dis = require("../../dispatcher");
import sessionStore from '../../stores/SessionStore'; import sessionStore from '../../stores/SessionStore';
const q = require('q'); import Promise from 'bluebird';
const packageJson = require('../../../package.json'); const packageJson = require('../../../package.json');
const UserSettingsStore = require('../../UserSettingsStore'); const UserSettingsStore = require('../../UserSettingsStore');
const CallMediaHandler = require('../../CallMediaHandler'); const CallMediaHandler = require('../../CallMediaHandler');
@ -93,6 +93,10 @@ const SETTINGS_LABELS = [
id: 'enableSyntaxHighlightLanguageDetection', id: 'enableSyntaxHighlightLanguageDetection',
label: 'Enable automatic language detection for syntax highlighting', label: 'Enable automatic language detection for syntax highlighting',
}, },
{
id: 'MessageComposerInput.autoReplaceEmoji',
label: 'Automatically replace plain text Emoji',
},
/* /*
{ {
id: 'useFixedWidthFont', id: 'useFixedWidthFont',
@ -199,7 +203,7 @@ module.exports = React.createClass({
this._addThreepid = null; this._addThreepid = null;
if (PlatformPeg.get()) { if (PlatformPeg.get()) {
q().then(() => { Promise.resolve().then(() => {
return PlatformPeg.get().getAppVersion(); return PlatformPeg.get().getAppVersion();
}).done((appVersion) => { }).done((appVersion) => {
if (this._unmounted) return; if (this._unmounted) return;
@ -297,7 +301,7 @@ module.exports = React.createClass({
}, },
_refreshMediaDevices: function() { _refreshMediaDevices: function() {
q().then(() => { Promise.resolve().then(() => {
return CallMediaHandler.getDevices(); return CallMediaHandler.getDevices();
}).then((mediaDevices) => { }).then((mediaDevices) => {
// console.log("got mediaDevices", mediaDevices, this._unmounted); // console.log("got mediaDevices", mediaDevices, this._unmounted);
@ -312,7 +316,7 @@ module.exports = React.createClass({
_refreshFromServer: function() { _refreshFromServer: function() {
const self = this; const self = this;
q.all([ Promise.all([
UserSettingsStore.loadProfileInfo(), UserSettingsStore.loadThreePids(), UserSettingsStore.loadProfileInfo(), UserSettingsStore.loadThreePids(),
]).done(function(resps) { ]).done(function(resps) {
self.setState({ self.setState({
@ -564,15 +568,16 @@ module.exports = React.createClass({
}); });
// reject the invites // reject the invites
const promises = rooms.map((room) => { 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 Promise.all(promises).then(() => {
// after trying to reject all the invites.
q.allSettled(promises).then(() => {
this.setState({ this.setState({
rejectingInvites: false, rejectingInvites: false,
}); });
}).done(); });
}, },
_onExportE2eKeysClicked: function() { _onExportE2eKeysClicked: function() {

View file

@ -17,7 +17,7 @@ limitations under the License.
import Matrix from 'matrix-js-sdk'; import Matrix from 'matrix-js-sdk';
import q from 'q'; import Promise from 'bluebird';
import React from 'react'; import React from 'react';
import sdk from '../../../index'; import sdk from '../../../index';
@ -180,7 +180,7 @@ module.exports = React.createClass({
// will just nop. The point of this being we might not have the email address // 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 // that the user registered with at this stage (depending on whether this
// is the client they initiated registration). // is the client they initiated registration).
let trackPromise = q(null); let trackPromise = Promise.resolve(null);
if (this._rtsClient && extra.emailSid) { if (this._rtsClient && extra.emailSid) {
// Track referral if this.props.referrer set, get team_token in order to // Track referral if this.props.referrer set, get team_token in order to
// retrieve team config and see welcome page etc. // retrieve team config and see welcome page etc.
@ -232,7 +232,7 @@ module.exports = React.createClass({
_setupPushers: function(matrixClient) { _setupPushers: function(matrixClient) {
if (!this.props.brand) { if (!this.props.brand) {
return q(); return Promise.resolve();
} }
return matrixClient.getPushers().then((resp)=>{ return matrixClient.getPushers().then((resp)=>{
const pushers = resp.pushers; const pushers = resp.pushers;

View file

@ -23,7 +23,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
import DMRoomMap from '../../../utils/DMRoomMap'; import DMRoomMap from '../../../utils/DMRoomMap';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton from '../elements/AccessibleButton';
import q from 'q'; import Promise from 'bluebird';
import dis from '../../../dispatcher'; import dis from '../../../dispatcher';
const TRUNCATE_QUERY_LIST = 40; const TRUNCATE_QUERY_LIST = 40;
@ -498,7 +498,7 @@ module.exports = React.createClass({
} }
// wait a bit to let the user finish typing // wait a bit to let the user finish typing
return q.delay(500).then(() => { return Promise.delay(500).then(() => {
if (cancelled) return null; if (cancelled) return null;
return MatrixClientPeg.get().lookupThreePid(medium, address); return MatrixClientPeg.get().lookupThreePid(medium, address);
}).then((res) => { }).then((res) => {

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import q from 'q'; import Promise from 'bluebird';
import React from 'react'; import React from 'react';
import sdk from '../../../index'; import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';

View file

@ -21,7 +21,9 @@ import React from 'react';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
import ScalarAuthClient from '../../../ScalarAuthClient'; import ScalarAuthClient from '../../../ScalarAuthClient';
import SdkConfig from '../../../SdkConfig'; import SdkConfig from '../../../SdkConfig';
import Modal from '../../../Modal';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import sdk from '../../../index';
const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:']; const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
@ -33,6 +35,7 @@ export default React.createClass({
url: React.PropTypes.string.isRequired, url: React.PropTypes.string.isRequired,
name: React.PropTypes.string.isRequired, name: React.PropTypes.string.isRequired,
room: React.PropTypes.object.isRequired, room: React.PropTypes.object.isRequired,
type: React.PropTypes.string.isRequired,
}, },
getDefaultProps: function() { getDefaultProps: function() {
@ -86,8 +89,13 @@ export default React.createClass({
}); });
}, },
_onEditClick: function() { _onEditClick: function(e) {
console.log("Edit widget %s", this.props.id); 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() { _onDeleteClick: function() {
@ -120,10 +128,10 @@ export default React.createClass({
<div> Loading... </div> <div> Loading... </div>
); );
} else { } 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 // 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 // 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. // a link to it.
const sandboxFlags = "allow-forms allow-popups allow-popups-to-escape-sandbox "+ const sandboxFlags = "allow-forms allow-popups allow-popups-to-escape-sandbox "+
"allow-same-origin allow-scripts"; "allow-same-origin allow-scripts";
@ -140,18 +148,22 @@ export default React.createClass({
</div> </div>
); );
} }
// editing is done in scalar
const showEditButton = Boolean(this._scalarClient);
return ( return (
<div className={this.props.fullWidth ? "mx_AppTileFullWidth" : "mx_AppTile"} id={this.props.id}> <div className={this.props.fullWidth ? "mx_AppTileFullWidth" : "mx_AppTile"} id={this.props.id}>
<div className="mx_AppTileMenuBar"> <div className="mx_AppTileMenuBar">
{this.formatAppTileName()} {this.formatAppTileName()}
<span className="mx_AppTileMenuBarWidgets"> <span className="mx_AppTileMenuBarWidgets">
{/* Edit widget */} {/* Edit widget */}
{/* <img {showEditButton && <img
src="img/edit.svg" src="img/edit.svg"
className="mx_filterFlipColor mx_AppTileMenuBarWidget mx_AppTileMenuBarWidgetPadding" className="mx_filterFlipColor mx_AppTileMenuBarWidget mx_AppTileMenuBarWidgetPadding"
width="8" height="8" alt="Edit" width="8" height="8" alt="Edit"
onClick={this._onEditClick} onClick={this._onEditClick}
/> */} />}
{/* Delete widget */} {/* Delete widget */}
<img src="img/cancel.svg" <img src="img/cancel.svg"

View file

@ -16,7 +16,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import sdk from '../../../index'; import sdk from '../../../index';
import q from 'q'; import Promise from 'bluebird';
/** /**
* A component which wraps an EditableText, with a spinner while updates take * A component which wraps an EditableText, with a spinner while updates take
@ -148,5 +148,5 @@ EditableTextContainer.defaultProps = {
initialValue: "", initialValue: "",
placeholder: "", placeholder: "",
blurToSubmit: false, blurToSubmit: false,
onSubmit: function(v) {return q(); }, onSubmit: function(v) {return Promise.resolve(); },
}; };

View file

@ -24,7 +24,7 @@ import Modal from '../../../Modal';
import sdk from '../../../index'; import sdk from '../../../index';
import dis from '../../../dispatcher'; import dis from '../../../dispatcher';
import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile'; import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile';
import q from 'q'; import Promise from 'bluebird';
import UserSettingsStore from '../../../UserSettingsStore'; import UserSettingsStore from '../../../UserSettingsStore';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
@ -123,7 +123,7 @@ module.exports = React.createClass({
this.fixupHeight(); this.fixupHeight();
const content = this.props.mxEvent.getContent(); const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) { if (content.file !== undefined && this.state.decryptedUrl === null) {
let thumbnailPromise = q(null); let thumbnailPromise = Promise.resolve(null);
if (content.info.thumbnail_file) { if (content.info.thumbnail_file) {
thumbnailPromise = decryptFile( thumbnailPromise = decryptFile(
content.info.thumbnail_file, content.info.thumbnail_file,

View file

@ -20,7 +20,7 @@ import React from 'react';
import MFileBody from './MFileBody'; import MFileBody from './MFileBody';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile'; import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile';
import q from 'q'; import Promise from 'bluebird';
import UserSettingsStore from '../../../UserSettingsStore'; import UserSettingsStore from '../../../UserSettingsStore';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
@ -89,7 +89,7 @@ module.exports = React.createClass({
componentDidMount: function() { componentDidMount: function() {
const content = this.props.mxEvent.getContent(); const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) { if (content.file !== undefined && this.state.decryptedUrl === null) {
var thumbnailPromise = q(null); var thumbnailPromise = Promise.resolve(null);
if (content.info.thumbnail_file) { if (content.info.thumbnail_file) {
thumbnailPromise = decryptFile( thumbnailPromise = decryptFile(
content.info.thumbnail_file content.info.thumbnail_file

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
var q = require("q"); import Promise from 'bluebird';
var React = require('react'); var React = require('react');
var ObjectUtils = require("../../../ObjectUtils"); var ObjectUtils = require("../../../ObjectUtils");
var MatrixClientPeg = require('../../../MatrixClientPeg'); var MatrixClientPeg = require('../../../MatrixClientPeg');
@ -104,7 +104,7 @@ module.exports = React.createClass({
} }
if (oldCanonicalAlias !== this.state.canonicalAlias) { if (oldCanonicalAlias !== this.state.canonicalAlias) {
console.log("AliasSettings: Updating canonical alias"); console.log("AliasSettings: Updating canonical alias");
promises = [q.all(promises).then( promises = [Promise.all(promises).then(
MatrixClientPeg.get().sendStateEvent( MatrixClientPeg.get().sendStateEvent(
this.props.roomId, "m.room.canonical_alias", { this.props.roomId, "m.room.canonical_alias", {
alias: this.state.canonicalAlias alias: this.state.canonicalAlias

View file

@ -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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
var q = require("q"); import Promise from 'bluebird';
var React = require('react'); var React = require('react');
var sdk = require('../../../index'); var sdk = require('../../../index');
@ -72,7 +72,7 @@ module.exports = React.createClass({
saveSettings: function() { // : Promise saveSettings: function() { // : Promise
if (!this.state.hasChanged) { 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(); var originalState = this.getInitialState();
if (originalState.primary_color !== this.state.primary_color || 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) { _getColorIndex: function(scheme) {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
var q = require("q"); import Promise from 'bluebird';
var React = require('react'); var React = require('react');
var MatrixClientPeg = require('../../../MatrixClientPeg'); var MatrixClientPeg = require('../../../MatrixClientPeg');
var sdk = require("../../../index"); var sdk = require("../../../index");

View file

@ -169,6 +169,7 @@ module.exports = React.createClass({
id={app.id} id={app.id}
url={app.url} url={app.url}
name={app.name} name={app.name}
type={app.type}
fullWidth={arr.length<2 ? true : false} fullWidth={arr.length<2 ? true : false}
room={this.props.room} room={this.props.room}
userId={this.props.userId} userId={this.props.userId}

View file

@ -5,7 +5,7 @@ import flatMap from 'lodash/flatMap';
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
import sdk from '../../../index'; import sdk from '../../../index';
import type {Completion} from '../../../autocomplete/Autocompleter'; import type {Completion} from '../../../autocomplete/Autocompleter';
import Q from 'q'; import Promise from 'bluebird';
import UserSettingsStore from '../../../UserSettingsStore'; import UserSettingsStore from '../../../UserSettingsStore';
import {getCompletions} from '../../../autocomplete/Autocompleter'; import {getCompletions} from '../../../autocomplete/Autocompleter';
@ -64,7 +64,7 @@ export default class Autocomplete extends React.Component {
// Hide the autocomplete box // Hide the autocomplete box
hide: true, hide: true,
}); });
return Q(null); return Promise.resolve(null);
} }
let autocompleteDelay = UserSettingsStore.getLocalSetting('autocompleteDelay', 200); let autocompleteDelay = UserSettingsStore.getLocalSetting('autocompleteDelay', 200);
@ -73,7 +73,7 @@ export default class Autocomplete extends React.Component {
autocompleteDelay = 0; autocompleteDelay = 0;
} }
const deferred = Q.defer(); const deferred = Promise.defer();
this.debounceCompletionsRequest = setTimeout(() => { this.debounceCompletionsRequest = setTimeout(() => {
this.processQuery(query, selection).then(() => { this.processQuery(query, selection).then(() => {
deferred.resolve(); deferred.resolve();
@ -176,7 +176,7 @@ export default class Autocomplete extends React.Component {
} }
forceComplete() { forceComplete() {
const done = Q.defer(); const done = Promise.defer();
this.setState({ this.setState({
forceComplete: true, forceComplete: true,
hide: false, hide: false,

View file

@ -17,7 +17,7 @@ var React = require('react');
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
var classNames = require('classnames'); var classNames = require('classnames');
var Matrix = require("matrix-js-sdk"); var Matrix = require("matrix-js-sdk");
var q = require('q'); import Promise from 'bluebird';
var MatrixClientPeg = require("../../../MatrixClientPeg"); var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal"); var Modal = require("../../../Modal");
var Entities = require("../../../Entities"); var Entities = require("../../../Entities");

View file

@ -41,7 +41,6 @@ export default class MessageComposer extends React.Component {
this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this); this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this);
this.onInputStateChanged = this.onInputStateChanged.bind(this); this.onInputStateChanged = this.onInputStateChanged.bind(this);
this.onEvent = this.onEvent.bind(this); this.onEvent = this.onEvent.bind(this);
this.onPageUnload = this.onPageUnload.bind(this);
this.state = { this.state = {
autocompleteQuery: '', autocompleteQuery: '',
@ -62,21 +61,12 @@ export default class MessageComposer extends React.Component {
// marked as encrypted. // marked as encrypted.
// XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something. // XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something.
MatrixClientPeg.get().on("event", this.onEvent); MatrixClientPeg.get().on("event", this.onEvent);
window.addEventListener('beforeunload', this.onPageUnload);
} }
componentWillUnmount() { componentWillUnmount() {
if (MatrixClientPeg.get()) { if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("event", this.onEvent); MatrixClientPeg.get().removeListener("event", this.onEvent);
} }
window.removeEventListener('beforeunload', this.onPageUnload);
}
onPageUnload(event) {
if (this.messageComposerInput) {
this.messageComposerInput.sentHistory.saveLastTextEntry();
}
} }
onEvent(event) { onEvent(event) {

View file

@ -16,16 +16,17 @@ limitations under the License.
import React from 'react'; import React from 'react';
import type SyntheticKeyboardEvent from 'react/lib/SyntheticKeyboardEvent'; import type SyntheticKeyboardEvent from 'react/lib/SyntheticKeyboardEvent';
import {Editor, EditorState, RichUtils, CompositeDecorator, import {Editor, EditorState, RichUtils, CompositeDecorator, Modifier,
convertFromRaw, convertToRaw, Modifier, EditorChangeType, getDefaultKeyBinding, KeyBindingUtil, ContentState, ContentBlock, SelectionState,
getDefaultKeyBinding, KeyBindingUtil, ContentState, ContentBlock, SelectionState} from 'draft-js'; Entity} from 'draft-js';
import classNames from 'classnames'; import classNames from 'classnames';
import escape from 'lodash/escape'; import escape from 'lodash/escape';
import Q from 'q'; import Promise from 'bluebird';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
import type {MatrixClient} from 'matrix-js-sdk/lib/matrix'; import type {MatrixClient} from 'matrix-js-sdk/lib/matrix';
import {RoomMember} from 'matrix-js-sdk';
import SlashCommands from '../../../SlashCommands'; import SlashCommands from '../../../SlashCommands';
import KeyCode from '../../../KeyCode'; import KeyCode from '../../../KeyCode';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
@ -43,6 +44,14 @@ import Markdown from '../../../Markdown';
import ComposerHistoryManager from '../../../ComposerHistoryManager'; import ComposerHistoryManager from '../../../ComposerHistoryManager';
import MessageComposerStore from '../../../stores/MessageComposerStore'; 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();
const REGEX_EMOJI_WHITESPACE = new RegExp('(' + asciiRegexp + ')\\s$');
const TYPING_USER_TIMEOUT = 10000, TYPING_SERVER_TIMEOUT = 30000; const TYPING_USER_TIMEOUT = 10000, TYPING_SERVER_TIMEOUT = 30000;
const ZWS_CODE = 8203; const ZWS_CODE = 8203;
@ -156,15 +165,72 @@ export default class MessageComposerInput extends React.Component {
this.client = MatrixClientPeg.get(); 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: * "Does the right thing" to create an EditorState, based on:
* - whether we've got rich text mode enabled * - whether we've got rich text mode enabled
* - contentState was passed in * - contentState was passed in
*/ */
createEditorState(richText: boolean, contentState: ?ContentState): EditorState { createEditorState(richText: boolean, contentState: ?ContentState): EditorState {
let decorators = richText ? RichText.getScopedRTDecorators(this.props) : const decorators = richText ? RichText.getScopedRTDecorators(this.props) :
RichText.getScopedMDDecorators(this.props), RichText.getScopedMDDecorators(this.props);
compositeDecorator = new CompositeDecorator(decorators); 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 ? <MemberAvatar member={member} width={16} height={16}/> : 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 ? <RoomAvatar room={room} width={16} height={16}/> : null;
}
if (isUserPill || isRoomPill) {
return (
<span className={classes}>
{avatar}
{props.children}
</span>
);
}
return (
<a href={url}>
{props.children}
</a>
);
},
});
const compositeDecorator = new CompositeDecorator(decorators);
let editorState = null; let editorState = null;
if (contentState) { if (contentState) {
@ -319,6 +385,60 @@ export default class MessageComposerInput extends React.Component {
onEditorContentChanged = (editorState: EditorState) => { onEditorContentChanged = (editorState: EditorState) => {
editorState = RichText.attachImmutableEntitiesToEmoji(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);
}
// 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 */ /* Since a modification was made, set originalEditorState to null, since newState is now our original */
this.setState({ this.setState({
editorState, editorState,
@ -517,10 +637,13 @@ export default class MessageComposerInput extends React.Component {
} }
const currentBlockType = RichUtils.getCurrentBlockType(this.state.editorState); const currentBlockType = RichUtils.getCurrentBlockType(this.state.editorState);
// If we're in any of these three types of blocks, shift enter should insert soft newlines if(
// And just enter should end the block ['code-block', 'blockquote', 'unordered-list-item', 'ordered-list-item']
// XXX: Empirically enter does not end these blocks .includes(currentBlockType)
if(['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; return false;
} }
@ -581,6 +704,15 @@ export default class MessageComposerInput extends React.Component {
} }
}); });
} }
if (!shouldSendHTML) {
const hasLink = blocks.some((block) => {
return block.getCharacterList().filter((c) => {
const entityKey = c.getEntity();
return entityKey && Entity.get(entityKey).getType() === 'LINK';
}).size > 0;
});
shouldSendHTML = hasLink;
}
if (shouldSendHTML) { if (shouldSendHTML) {
contentHTML = HtmlUtils.processHtmlForSending( contentHTML = HtmlUtils.processHtmlForSending(
RichText.contentStateToHTML(contentState), RichText.contentStateToHTML(contentState),

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import q from 'q'; import Promise from 'bluebird';
import React from 'react'; import React from 'react';
import { _t, _tJsx } from '../../../languageHandler'; import { _t, _tJsx } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
@ -183,8 +183,14 @@ 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() { 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 // 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. // 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 // We can be a bit cheeky here and set a loading flag, and listen for the callback on that
@ -194,8 +200,18 @@ module.exports = React.createClass({
this.setState({ _loading: false}); 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 stateWasSetDefer.promise.then(() => {
return q.allSettled(this._calcSavePromises()); return Promise.all(
this._calcSavePromises().map(mapPromiseToSnapshot),
);
}); });
}, },
@ -282,7 +298,7 @@ module.exports = React.createClass({
// color scheme // color scheme
var p; var p;
p = this.saveColor(); p = this.saveColor();
if (!q.isFulfilled(p)) { if (!p.isFulfilled()) {
promises.push(p); promises.push(p);
} }
@ -294,7 +310,7 @@ module.exports = React.createClass({
// encryption // encryption
p = this.saveEnableEncryption(); p = this.saveEnableEncryption();
if (!q.isFulfilled(p)) { if (!p.isFulfilled()) {
promises.push(p); promises.push(p);
} }
@ -305,25 +321,25 @@ module.exports = React.createClass({
}, },
saveAliases: function() { saveAliases: function() {
if (!this.refs.alias_settings) { return [q()]; } if (!this.refs.alias_settings) { return [Promise.resolve()]; }
return this.refs.alias_settings.saveSettings(); return this.refs.alias_settings.saveSettings();
}, },
saveColor: function() { saveColor: function() {
if (!this.refs.color_settings) { return q(); } if (!this.refs.color_settings) { return Promise.resolve(); }
return this.refs.color_settings.saveSettings(); return this.refs.color_settings.saveSettings();
}, },
saveUrlPreviewSettings: function() { 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(); return this.refs.url_preview_settings.saveSettings();
}, },
saveEnableEncryption: function() { saveEnableEncryption: function() {
if (!this.refs.encrypt) { return q(); } if (!this.refs.encrypt) { return Promise.resolve(); }
var encrypt = this.refs.encrypt.checked; var encrypt = this.refs.encrypt.checked;
if (!encrypt) { return q(); } if (!encrypt) { return Promise.resolve(); }
var roomId = this.props.room.roomId; var roomId = this.props.room.roomId;
return MatrixClientPeg.get().sendStateEvent( return MatrixClientPeg.get().sendStateEvent(

View file

@ -21,7 +21,7 @@ var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal"); var Modal = require("../../../Modal");
var sdk = require("../../../index"); var sdk = require("../../../index");
import q from 'q'; import Promise from 'bluebird';
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton from '../elements/AccessibleButton';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
@ -161,7 +161,7 @@ module.exports = React.createClass({
}, },
_optionallySetEmail: function() { _optionallySetEmail: function() {
const deferred = q.defer(); const deferred = Promise.defer();
// Ask for an email otherwise the user has no way to reset their password // Ask for an email otherwise the user has no way to reset their password
const SetEmailDialog = sdk.getComponent("dialogs.SetEmailDialog"); const SetEmailDialog = sdk.getComponent("dialogs.SetEmailDialog");
Modal.createDialog(SetEmailDialog, { Modal.createDialog(SetEmailDialog, {

View file

@ -21,7 +21,7 @@ import { _t } from './languageHandler';
import dis from "./dispatcher"; import dis from "./dispatcher";
import * as Rooms from "./Rooms"; import * as Rooms from "./Rooms";
import q from 'q'; import Promise from 'bluebird';
/** /**
* Create a new room, and switch to it. * Create a new room, and switch to it.
@ -42,7 +42,7 @@ function createRoom(opts) {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
if (client.isGuest()) { if (client.isGuest()) {
dis.dispatch({action: 'view_set_mxid'}); dis.dispatch({action: 'view_set_mxid'});
return q(null); return Promise.resolve(null);
} }
const defaultPreset = opts.dmUserId ? 'trusted_private_chat' : 'private_chat'; const defaultPreset = opts.dmUserId ? 'trusted_private_chat' : 'private_chat';
@ -92,7 +92,7 @@ function createRoom(opts) {
if (opts.dmUserId) { if (opts.dmUserId) {
return Rooms.setDMRoom(roomId, opts.dmUserId); return Rooms.setDMRoom(roomId, opts.dmUserId);
} else { } else {
return q(); return Promise.resolve();
} }
}).then(function() { }).then(function() {
// NB createRoom doesn't block on the client seeing the echo that the // NB createRoom doesn't block on the client seeing the echo that the

View file

@ -956,5 +956,6 @@
"Featured Rooms:": "Featured Rooms:", "Featured Rooms:": "Featured Rooms:",
"Error whilst fetching joined groups": "Error whilst fetching joined groups", "Error whilst fetching joined groups": "Error whilst fetching joined groups",
"Featured Users:": "Featured Users:", "Featured Users:": "Featured Users:",
"Edit Group": "Edit Group" "Edit Group": "Edit Group",
"Automatically replace plain text Emoji": "Automatically replace plain text Emoji"
} }

View file

@ -17,7 +17,7 @@ limitations under the License.
import request from 'browser-request'; import request from 'browser-request';
import counterpart from 'counterpart'; import counterpart from 'counterpart';
import q from 'q'; import Promise from 'bluebird';
import React from 'react'; import React from 'react';
import UserSettingsStore from './UserSettingsStore'; import UserSettingsStore from './UserSettingsStore';
@ -231,7 +231,7 @@ export function getCurrentLanguage() {
} }
function getLangsJson() { function getLangsJson() {
const deferred = q.defer(); const deferred = Promise.defer();
request( request(
{ method: "GET", url: i18nFolder + 'languages.json' }, { method: "GET", url: i18nFolder + 'languages.json' },
@ -247,7 +247,7 @@ function getLangsJson() {
} }
function getLanguage(langPath) { function getLanguage(langPath) {
const deferred = q.defer(); const deferred = Promise.defer();
let response_return = {}; let response_return = {};
request( request(

View file

@ -20,7 +20,7 @@ import encrypt from 'browser-encrypt-attachment';
import 'isomorphic-fetch'; import 'isomorphic-fetch';
// Grab the client so that we can turn mxc:// URLs into https:// URLS. // Grab the client so that we can turn mxc:// URLs into https:// URLS.
import MatrixClientPeg from '../MatrixClientPeg'; import MatrixClientPeg from '../MatrixClientPeg';
import q from 'q'; import Promise from 'bluebird';
/** /**
@ -28,7 +28,7 @@ import q from 'q';
* @return {Promise} A promise that resolves with the data:// URI. * @return {Promise} A promise that resolves with the data:// URI.
*/ */
export function readBlobAsDataUri(file) { export function readBlobAsDataUri(file) {
var deferred = q.defer(); var deferred = Promise.defer();
var reader = new FileReader(); var reader = new FileReader();
reader.onload = function(e) { reader.onload = function(e) {
deferred.resolve(e.target.result); deferred.resolve(e.target.result);
@ -53,7 +53,7 @@ export function readBlobAsDataUri(file) {
export function decryptFile(file) { export function decryptFile(file) {
const url = MatrixClientPeg.get().mxcUrlToHttp(file.url); const url = MatrixClientPeg.get().mxcUrlToHttp(file.url);
// Download the encrypted file as an array buffer. // 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(); return response.arrayBuffer();
}).then(function(responseData) { }).then(function(responseData) {
// Decrypt the array buffer using the information taken from // Decrypt the array buffer using the information taken from

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import {getAddressType, inviteToRoom} from '../Invite'; 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 * Invites multiple addresses to a room, handling rate limiting from the server
@ -55,7 +55,7 @@ export default class MultiInviter {
this.errorTexts[addr] = 'Unrecognised address'; this.errorTexts[addr] = 'Unrecognised address';
} }
} }
this.deferred = q.defer(); this.deferred = Promise.defer();
this._inviteMore(0); this._inviteMore(0);
return this.deferred.promise; return this.deferred.promise;

View file

@ -18,7 +18,7 @@ var React = require('react');
var ReactDOM = require("react-dom"); var ReactDOM = require("react-dom");
var ReactTestUtils = require('react-addons-test-utils'); var ReactTestUtils = require('react-addons-test-utils');
var expect = require('expect'); var expect = require('expect');
var q = require('q'); import Promise from 'bluebird';
var sdk = require('matrix-react-sdk'); var sdk = require('matrix-react-sdk');
@ -58,7 +58,7 @@ var Tester = React.createClass({
if (handler) { if (handler) {
res = handler(); res = handler();
} else { } else {
res = q(false); res = Promise.resolve(false);
} }
if (defer) { if (defer) {
@ -74,7 +74,7 @@ var Tester = React.createClass({
/* returns a promise which will resolve when the fill happens */ /* returns a promise which will resolve when the fill happens */
awaitFill: function(dir) { awaitFill: function(dir) {
console.log("ScrollPanel Tester: awaiting " + dir + " fill"); console.log("ScrollPanel Tester: awaiting " + dir + " fill");
var defer = q.defer(); var defer = Promise.defer();
this._fillDefers[dir] = defer; this._fillDefers[dir] = defer;
return defer.promise; return defer.promise;
}, },
@ -94,7 +94,7 @@ var Tester = React.createClass({
/* returns a promise which will resolve when a scroll event happens */ /* returns a promise which will resolve when a scroll event happens */
awaitScroll: function() { awaitScroll: function() {
console.log("Awaiting scroll"); console.log("Awaiting scroll");
this._scrollDefer = q.defer(); this._scrollDefer = Promise.defer();
return this._scrollDefer.promise; return this._scrollDefer.promise;
}, },
@ -168,7 +168,7 @@ describe('ScrollPanel', function() {
const sp = tester.scrollPanel(); const sp = tester.scrollPanel();
let retriesRemaining = 1; let retriesRemaining = 1;
const awaitReady = function() { const awaitReady = function() {
return q().then(() => { return Promise.resolve().then(() => {
if (sp._pendingFillRequests.b === false && if (sp._pendingFillRequests.b === false &&
sp._pendingFillRequests.f === false sp._pendingFillRequests.f === false
) { ) {
@ -195,7 +195,7 @@ describe('ScrollPanel', function() {
it('should handle scrollEvent strangeness', function() { it('should handle scrollEvent strangeness', function() {
const events = []; const events = [];
return q().then(() => { return Promise.resolve().then(() => {
// initialise with a load of events // initialise with a load of events
for (let i = 0; i < 20; i++) { for (let i = 0; i < 20; i++) {
events.push(i+80); events.push(i+80);
@ -227,7 +227,7 @@ describe('ScrollPanel', function() {
it('should not get stuck in #528 workaround', function(done) { it('should not get stuck in #528 workaround', function(done) {
var events = []; var events = [];
q().then(() => { Promise.resolve().then(() => {
// initialise with a bunch of events // initialise with a bunch of events
for (var i = 0; i < 40; i++) { for (var i = 0; i < 40; i++) {
events.push(i); events.push(i);

View file

@ -18,7 +18,7 @@ var React = require('react');
var ReactDOM = require('react-dom'); var ReactDOM = require('react-dom');
var ReactTestUtils = require('react-addons-test-utils'); var ReactTestUtils = require('react-addons-test-utils');
var expect = require('expect'); var expect = require('expect');
var q = require('q'); import Promise from 'bluebird';
var sinon = require('sinon'); var sinon = require('sinon');
var jssdk = require('matrix-js-sdk'); var jssdk = require('matrix-js-sdk');
@ -145,20 +145,20 @@ describe('TimelinePanel', function() {
// panel isn't paginating // panel isn't paginating
var awaitPaginationCompletion = function() { var awaitPaginationCompletion = function() {
if(!panel.state.forwardPaginating) if(!panel.state.forwardPaginating)
return q(); return Promise.resolve();
else else
return q.delay(0).then(awaitPaginationCompletion); return Promise.delay(0).then(awaitPaginationCompletion);
}; };
// helper function which will return a promise which resolves when // helper function which will return a promise which resolves when
// the TimelinePanel fires a scroll event // the TimelinePanel fires a scroll event
var awaitScroll = function() { var awaitScroll = function() {
scrollDefer = q.defer(); scrollDefer = Promise.defer();
return scrollDefer.promise; return scrollDefer.promise;
}; };
// let the first round of pagination finish off // let the first round of pagination finish off
q.delay(5).then(() => { Promise.delay(5).then(() => {
expect(panel.state.canBackPaginate).toBe(false); expect(panel.state.canBackPaginate).toBe(false);
expect(scryEventTiles(panel).length).toEqual(N_EVENTS); expect(scryEventTiles(panel).length).toEqual(N_EVENTS);
@ -214,7 +214,7 @@ describe('TimelinePanel', function() {
client.paginateEventTimeline = sinon.spy((tl, opts) => { client.paginateEventTimeline = sinon.spy((tl, opts) => {
console.log("paginate:", opts); console.log("paginate:", opts);
expect(opts.backwards).toBe(true); expect(opts.backwards).toBe(true);
return q(true); return Promise.resolve(true);
}); });
var rendered = ReactDOM.render( var rendered = ReactDOM.render(
@ -279,7 +279,7 @@ describe('TimelinePanel', function() {
// helper function which will return a promise which resolves when // helper function which will return a promise which resolves when
// the TimelinePanel fires a scroll event // the TimelinePanel fires a scroll event
var awaitScroll = function() { var awaitScroll = function() {
scrollDefer = q.defer(); scrollDefer = Promise.defer();
return scrollDefer.promise.then(() => { return scrollDefer.promise.then(() => {
console.log("got scroll event; scrollTop now " + console.log("got scroll event; scrollTop now " +

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import expect from 'expect'; import expect from 'expect';
import q from 'q'; import Promise from 'bluebird';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import ReactTestUtils from 'react-addons-test-utils'; import ReactTestUtils from 'react-addons-test-utils';
@ -50,7 +50,7 @@ describe('InteractiveAuthDialog', function () {
it('Should successfully complete a password flow', function() { it('Should successfully complete a password flow', function() {
const onFinished = sinon.spy(); 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 // tell the stub matrixclient to return a real userid
var client = MatrixClientPeg.get(); var client = MatrixClientPeg.get();
@ -110,7 +110,7 @@ describe('InteractiveAuthDialog', function () {
); );
// let the request complete // let the request complete
return q.delay(1); return Promise.delay(1);
}).then(() => { }).then(() => {
expect(onFinished.callCount).toEqual(1); expect(onFinished.callCount).toEqual(1);
expect(onFinished.calledWithExactly(true, {a:1})).toBe(true); expect(onFinished.calledWithExactly(true, {a:1})).toBe(true);

View file

@ -3,7 +3,7 @@ import ReactTestUtils from 'react-addons-test-utils';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import expect, {createSpy} from 'expect'; import expect, {createSpy} from 'expect';
import sinon from 'sinon'; import sinon from 'sinon';
import Q from 'q'; import Promise from 'bluebird';
import * as testUtils from '../../../test-utils'; import * as testUtils from '../../../test-utils';
import sdk from 'matrix-react-sdk'; import sdk from 'matrix-react-sdk';
import UserSettingsStore from '../../../../src/UserSettingsStore'; import UserSettingsStore from '../../../../src/UserSettingsStore';
@ -47,7 +47,7 @@ describe('MessageComposerInput', () => {
// warnings // warnings
// (please can we make the components not setState() after // (please can we make the components not setState() after
// they are unmounted?) // they are unmounted?)
Q.delay(10).done(() => { Promise.delay(10).done(() => {
if (parentDiv) { if (parentDiv) {
ReactDOM.unmountComponentAtNode(parentDiv); ReactDOM.unmountComponentAtNode(parentDiv);
parentDiv.remove(); parentDiv.remove();

View file

@ -7,7 +7,7 @@ import RoomViewStore from '../../src/stores/RoomViewStore';
import peg from '../../src/MatrixClientPeg'; import peg from '../../src/MatrixClientPeg';
import * as testUtils from '../test-utils'; import * as testUtils from '../test-utils';
import q from 'q'; import Promise from 'bluebird';
const dispatch = testUtils.getDispatchForStore(RoomViewStore); const dispatch = testUtils.getDispatchForStore(RoomViewStore);
@ -39,7 +39,7 @@ describe('RoomViewStore', function() {
}); });
it('can be used to view a room by alias and join', function(done) { 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) => { peg.get().joinRoom = (roomAddress) => {
expect(roomAddress).toBe("#somealias2:aser.ver"); expect(roomAddress).toBe("#somealias2:aser.ver");
done(); done();

View file

@ -1,7 +1,7 @@
"use strict"; "use strict";
import sinon from 'sinon'; import sinon from 'sinon';
import q from 'q'; import Promise from 'bluebird';
import peg from '../src/MatrixClientPeg'; import peg from '../src/MatrixClientPeg';
import dis from '../src/dispatcher'; import dis from '../src/dispatcher';
@ -75,12 +75,12 @@ export function createTestClient() {
on: sinon.stub(), on: sinon.stub(),
removeListener: sinon.stub(), removeListener: sinon.stub(),
isRoomEncrypted: sinon.stub().returns(false), isRoomEncrypted: sinon.stub().returns(false),
peekInRoom: sinon.stub().returns(q(mkStubRoom())), peekInRoom: sinon.stub().returns(Promise.resolve(mkStubRoom())),
paginateEventTimeline: sinon.stub().returns(q()), paginateEventTimeline: sinon.stub().returns(Promise.resolve()),
sendReadReceipt: sinon.stub().returns(q()), sendReadReceipt: sinon.stub().returns(Promise.resolve()),
getRoomIdForAlias: sinon.stub().returns(q()), getRoomIdForAlias: sinon.stub().returns(Promise.resolve()),
getProfileInfo: sinon.stub().returns(q({})), getProfileInfo: sinon.stub().returns(Promise.resolve({})),
getAccountData: (type) => { getAccountData: (type) => {
return mkEvent({ return mkEvent({
type, type,
@ -89,9 +89,9 @@ export function createTestClient() {
}); });
}, },
setAccountData: sinon.stub(), setAccountData: sinon.stub(),
sendTyping: sinon.stub().returns(q({})), sendTyping: sinon.stub().returns(Promise.resolve({})),
sendTextMessage: () => q({}), sendTextMessage: () => Promise.resolve({}),
sendHtmlMessage: () => q({}), sendHtmlMessage: () => Promise.resolve({}),
getSyncState: () => "SYNCING", getSyncState: () => "SYNCING",
generateClientSecret: () => "t35tcl1Ent5ECr3T", generateClientSecret: () => "t35tcl1Ent5ECr3T",
isGuest: () => false, isGuest: () => false,
@ -101,13 +101,13 @@ export function createTestClient() {
export function createTestRtsClient(teamMap, sidMap) { export function createTestRtsClient(teamMap, sidMap) {
return { return {
getTeamsConfig() { getTeamsConfig() {
return q(Object.keys(teamMap).map((token) => teamMap[token])); return Promise.resolve(Object.keys(teamMap).map((token) => teamMap[token]));
}, },
trackReferral(referrer, emailSid, clientSecret) { trackReferral(referrer, emailSid, clientSecret) {
return q({team_token: sidMap[emailSid]}); return Promise.resolve({team_token: sidMap[emailSid]});
}, },
getTeam(teamToken) { getTeam(teamToken) {
return q(teamMap[teamToken]); return Promise.resolve(teamMap[teamToken]);
}, },
}; };
} }