diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js
index ef8a39edaf..cadbe44bf6 100644
--- a/src/autocomplete/EmojiProvider.js
+++ b/src/autocomplete/EmojiProvider.js
@@ -118,7 +118,7 @@ export default class EmojiProvider extends AutocompleteProvider {
}
renderCompletions(completions: [React.Component]): ?React.Component {
- return
+ return
{completions}
;
}
diff --git a/src/autocomplete/QueryMatcher.js b/src/autocomplete/QueryMatcher.js
index 07398e7a5f..762b285685 100644
--- a/src/autocomplete/QueryMatcher.js
+++ b/src/autocomplete/QueryMatcher.js
@@ -18,7 +18,7 @@ limitations under the License.
import _at from 'lodash/at';
import _flatMap from 'lodash/flatMap';
import _sortBy from 'lodash/sortBy';
-import _sortedUniq from 'lodash/sortedUniq';
+import _uniq from 'lodash/uniq';
import _keys from 'lodash/keys';
class KeyMap {
@@ -101,7 +101,7 @@ export default class QueryMatcher {
}
});
- return _sortedUniq(_flatMap(_sortBy(results, (candidate) => {
+ return _uniq(_flatMap(_sortBy(results, (candidate) => {
return candidate.index;
}).map((candidate) => {
// return an array of objects (those given to setObjects) that have the given
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 9d999224fa..b5ac45c1c7 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -178,27 +178,27 @@ module.exports = React.createClass({
const href = node.getAttribute("href");
// HtmlUtils transforms `matrix.to` links to local links, so match against
// user or room app links.
- const match = /^#\/(user|room)\/(.*)$/.exec(href);
- if (match) {
+ const match = /^#\/(user|room)\/(.*)$/.exec(href) || [];
+ const resourceType = match[1]; // "user" or "room"
+ const resourceId = match[2]; // user ID or room ID
+ if (match && resourceType && resourceId) {
let avatar;
let roomId;
let room;
- let userId;
let member;
- switch (match[1]) {
+ switch (resourceType) {
case "user":
roomId = this.props.mxEvent.getRoomId();
room = MatrixClientPeg.get().getRoom(roomId);
- userId = match[2];
- member = room.getMember(userId) ||
- new RoomMember(null, userId);
- avatar =
;
+ member = room.getMember(resourceId) ||
+ new RoomMember(null, resourceId);
+ avatar = ;
break;
case "room":
- room = match[2][0] === '#' ?
+ room = resourceId[0] === '#' ?
MatrixClientPeg.get().getRooms().find((r) => {
- return r.getCanonicalAlias() === match[2];
- }) : MatrixClientPeg.get().getRoom(match[2]);
+ return r.getCanonicalAlias() === resourceId;
+ }) : MatrixClientPeg.get().getRoom(resourceId);
if (room) {
avatar = ;
}
diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js
index 6463573a2a..a2bbe7a812 100644
--- a/src/components/views/rooms/MessageComposerInput.js
+++ b/src/components/views/rooms/MessageComposerInput.js
@@ -535,16 +535,19 @@ export default class MessageComposerInput extends React.Component {
if (this.state.isRichtextEnabled) {
// These are block types, not handled by RichUtils by default.
const blockCommands = ['code-block', 'blockquote', 'unordered-list-item', 'ordered-list-item'];
+ const currentBlockType = RichUtils.getCurrentBlockType(this.state.editorState);
if (blockCommands.includes(command)) {
- this.setState({
- editorState: RichUtils.toggleBlockType(this.state.editorState, command),
- });
+ newState = RichUtils.toggleBlockType(this.state.editorState, command);
} else if (command === 'strike') {
// this is the only inline style not handled by Draft by default
- this.setState({
- editorState: RichUtils.toggleInlineStyle(this.state.editorState, 'STRIKETHROUGH'),
- });
+ newState = RichUtils.toggleInlineStyle(this.state.editorState, 'STRIKETHROUGH');
+ } else if (command === 'backspace' && currentBlockType !== 'unstyled') {
+ const currentStartOffset = this.state.editorState.getSelection().getStartOffset();
+ if (currentStartOffset === 0) {
+ // Toggle current block type (setting it to 'unstyled')
+ newState = RichUtils.toggleBlockType(this.state.editorState, currentBlockType);
+ }
}
} else {
let contentState = this.state.editorState.getCurrentContent();
@@ -651,6 +654,7 @@ export default class MessageComposerInput extends React.Component {
// 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.
+ // See handleKeyCommand (when command === 'backspace')
return false;
}
diff --git a/src/utils/MultiInviter.js b/src/utils/MultiInviter.js
index c26cc6b52e..9cdfb947f4 100644
--- a/src/utils/MultiInviter.js
+++ b/src/utils/MultiInviter.js
@@ -36,10 +36,6 @@ export default class MultiInviter {
* Invite users to this room. This may only be called once per
* instance of the class.
*
- * The promise is given progress when each address completes, with an
- * object argument with each completed address with value either
- * 'invited' or 'error'.
- *
* @param {array} addresses Array of addresses to invite
* @returns {Promise} Resolved when all invitations in the queue are complete
*/
@@ -111,7 +107,6 @@ export default class MultiInviter {
if (this._canceled) { return; }
this.completionStates[addr] = 'invited';
- this.deferred.notify(this.completionStates);
this._inviteMore(nextIndex + 1);
}, (err) => {
@@ -136,7 +131,6 @@ export default class MultiInviter {
this.busy = !fatal;
if (!fatal) {
- this.deferred.notify(this.completionStates);
this._inviteMore(nextIndex + 1);
}
});