diff --git a/karma.conf.js b/karma.conf.js index 6d3047bb3b..3495a981be 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -135,17 +135,24 @@ module.exports = function (config) { }, ], noParse: [ + // for cross platform compatibility use [\\\/] as the path separator + // this ensures that the regex trips on both Windows and *nix + // don't parse the languages within highlight.js. They // cause stack overflows // (https://github.com/webpack/webpack/issues/1721), and // there is no need for webpack to parse them - they can // just be included as-is. - /highlight\.js\/lib\/languages/, + /highlight\.js[\\\/]lib[\\\/]languages/, + + // olm takes ages for webpack to process, and it's already heavily + // optimised, so there is little to gain by us uglifying it. + /olm[\\\/](javascript[\\\/])?olm\.js$/, // also disable parsing for sinon, because it // tries to do voodoo with 'require' which upsets // webpack (https://github.com/webpack/webpack/issues/304) - /sinon\/pkg\/sinon\.js$/, + /sinon[\\\/]pkg[\\\/]sinon\.js$/, ], }, resolve: { diff --git a/src/CallHandler.js b/src/CallHandler.js index 42cc681d08..5199ef0a67 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -313,7 +313,7 @@ function _onAction(payload) { console.error("Conference call failed: " + err); Modal.createDialog(ErrorDialog, { title: "Failed to set up conference call", - description: "Conference call failed.", + description: "Conference call failed. " + ((err && err.message) ? err.message : ""), }); }); } diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index a8e20f5ec1..96934d205e 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -25,6 +25,9 @@ import emojione from 'emojione'; import classNames from 'classnames'; emojione.imagePathSVG = 'emojione/svg/'; +// Store PNG path for displaying many flags at once (for increased performance over SVG) +emojione.imagePathPNG = 'emojione/png/'; +// Use SVGs for emojis emojione.imageType = 'svg'; const EMOJI_REGEX = new RegExp(emojione.unicodeRegexp+"+", "gi"); @@ -64,16 +67,23 @@ export function unicodeToImage(str) { * emoji. * * @param alt {string} String to use for the image alt text + * @param useSvg {boolean} Whether to use SVG image src. If False, PNG will be used. * @param unicode {integer} One or more integers representing unicode characters * @returns A img node with the corresponding emoji */ -export function charactersToImageNode(alt, ...unicode) { +export function charactersToImageNode(alt, useSvg, ...unicode) { const fileName = unicode.map((u) => { return u.toString(16); }).join('-'); - return {alt}; + const path = useSvg ? emojione.imagePathSVG : emojione.imagePathPNG; + const fileType = useSvg ? 'svg' : 'png'; + return {alt}; } + export function stripParagraphs(html: string): string { const contentDiv = document.createElement('div'); contentDiv.innerHTML = html; diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index ef9d8d112a..4c012b42a8 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -117,9 +117,10 @@ export default React.createClass({ } break; + case KeyCode.UP: case KeyCode.DOWN: - if (ev.altKey) { + if (ev.altKey && !ev.shiftKey && !ev.ctrlKey && !ev.metaKey) { var action = ev.keyCode == KeyCode.UP ? 'view_prev_room' : 'view_next_room'; dis.dispatch({action: action}); @@ -129,13 +130,15 @@ export default React.createClass({ case KeyCode.PAGE_UP: case KeyCode.PAGE_DOWN: - this._onScrollKeyPressed(ev); - handled = true; + if (!ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) { + this._onScrollKeyPressed(ev); + handled = true; + } break; case KeyCode.HOME: case KeyCode.END: - if (ev.ctrlKey) { + if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) { this._onScrollKeyPressed(ev); handled = true; } @@ -153,6 +156,9 @@ export default React.createClass({ if (this.refs.roomView) { this.refs.roomView.handleScrollKey(ev); } + else if (this.refs.roomDirectory) { + this.refs.roomDirectory.handleScrollKey(ev); + } }, render: function() { @@ -213,6 +219,7 @@ export default React.createClass({ case PageTypes.RoomDirectory: page_element = ; diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index b449ff3094..9b8aa3426a 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -413,7 +413,7 @@ module.exports = React.createClass({ console.error("Failed to leave room " + payload.room_id + " " + err); Modal.createDialog(ErrorDialog, { title: "Failed to leave room", - description: "Server may be unavailable, overloaded, or you hit a bug." + description: (err && err.message ? err.message : "Server may be unavailable, overloaded, or you hit a bug."), }); }); } diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 0f8d35f525..8d50789eb0 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -388,6 +388,8 @@ module.exports = React.createClass({ isVisibleReadMarker = visible; } + // XXX: there should be no need for a ghost tile - we should just use a + // a dispatch (user_activity_end) to start the RM animation. if (eventId == this.currentGhostEventId) { // if we're showing an animation, continue to show it. ret.push(this._getReadMarkerGhostTile()); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index ea221e98b7..9cd5070ad1 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -26,6 +26,7 @@ var q = require("q"); var classNames = require("classnames"); var Matrix = require("matrix-js-sdk"); +var UserSettingsStore = require('../../UserSettingsStore'); var MatrixClientPeg = require("../../MatrixClientPeg"); var ContentMessages = require("../../ContentMessages"); var Modal = require("../../Modal"); @@ -953,7 +954,7 @@ module.exports = React.createClass({ console.error("Failed to upload file " + file + " " + error); Modal.createDialog(ErrorDialog, { title: "Failed to upload file", - description: "Server may be unavailable, overloaded, or the file too big", + description: ((error && error.message) ? error.message : "Server may be unavailable, overloaded, or the file too big"), }); }); }, @@ -1040,7 +1041,7 @@ module.exports = React.createClass({ console.error("Search failed: " + error); Modal.createDialog(ErrorDialog, { title: "Search failed", - description: "Server may be unavailable, overloaded, or search timed out :(" + description: ((error && error.message) ? error.message : "Server may be unavailable, overloaded, or search timed out :("), }); }).finally(function() { self.setState({ @@ -1191,6 +1192,7 @@ module.exports = React.createClass({ editingRoomSettings: false, forwardingMessage: null, }); + dis.dispatch({action: 'focus_composer'}); }, onLeaveClick: function() { @@ -1736,7 +1738,7 @@ module.exports = React.createClass({ var messagePanel = (