From 48f2c4a69680839000e1347a5cb83d3df66057cd Mon Sep 17 00:00:00 2001 From: Aviral Dasgupta Date: Tue, 5 Jul 2016 03:13:53 +0530 Subject: [PATCH 1/6] feat: render unicode emoji as emojione images --- src/HtmlUtils.js | 51 +++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index dbcb59a20a..629851f9e8 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -20,6 +20,8 @@ var React = require('react'); var sanitizeHtml = require('sanitize-html'); var highlight = require('highlight.js'); var linkifyMatrix = require('./linkify-matrix'); +import escape from 'lodash/escape'; +import {unicodeToImage} from 'emojione'; var sanitizeHtmlParams = { allowedTags: [ @@ -185,40 +187,31 @@ module.exports = { opts = opts || {}; var isHtml = (content.format === "org.matrix.custom.html"); + let body = isHtml ? content.formatted_body : escape(content.body); var safeBody; - if (isHtml) { - // XXX: We sanitize the HTML whilst also highlighting its text nodes, to avoid accidentally trying - // to highlight HTML tags themselves. However, this does mean that we don't highlight textnodes which - // are interrupted by HTML tags (not that we did before) - e.g. foobar won't get highlighted - // by an attempt to search for 'foobar'. Then again, the search query probably wouldn't work either - try { - if (highlights && highlights.length > 0) { - var highlighter = new HtmlHighlighter("mx_EventTile_searchHighlight", opts.highlightLink); - var safeHighlights = highlights.map(function(highlight) { - return sanitizeHtml(highlight, sanitizeHtmlParams); - }); - // XXX: hacky bodge to temporarily apply a textFilter to the sanitizeHtmlParams structure. - sanitizeHtmlParams.textFilter = function(safeText) { - return highlighter.applyHighlights(safeText, safeHighlights).join(''); - }; - } - safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); - } - finally { - delete sanitizeHtmlParams.textFilter; - } - return ; - } else { - safeBody = content.body; + // XXX: We sanitize the HTML whilst also highlighting its text nodes, to avoid accidentally trying + // to highlight HTML tags themselves. However, this does mean that we don't highlight textnodes which + // are interrupted by HTML tags (not that we did before) - e.g. foobar won't get highlighted + // by an attempt to search for 'foobar'. Then again, the search query probably wouldn't work either + try { if (highlights && highlights.length > 0) { - var highlighter = new TextHighlighter("mx_EventTile_searchHighlight", opts.highlightLink); - return highlighter.applyHighlights(safeBody, highlights); - } - else { - return safeBody; + var highlighter = new HtmlHighlighter("mx_EventTile_searchHighlight", opts.highlightLink); + var safeHighlights = highlights.map(function(highlight) { + return sanitizeHtml(highlight, sanitizeHtmlParams); + }); + // XXX: hacky bodge to temporarily apply a textFilter to the sanitizeHtmlParams structure. + sanitizeHtmlParams.textFilter = function(safeText) { + return highlighter.applyHighlights(safeText, safeHighlights).join(''); + }; } + safeBody = sanitizeHtml(body, sanitizeHtmlParams); + safeBody = unicodeToImage(safeBody); } + finally { + delete sanitizeHtmlParams.textFilter; + } + return ; }, highlightDom: function(element) { From 4069886cbda08459fa5145fb2771cd77f7e95277 Mon Sep 17 00:00:00 2001 From: Aviral Dasgupta Date: Tue, 5 Jul 2016 04:04:57 +0530 Subject: [PATCH 2/6] feat: large emoji support --- src/HtmlUtils.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 629851f9e8..91bb063ee2 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -21,7 +21,10 @@ var sanitizeHtml = require('sanitize-html'); var highlight = require('highlight.js'); var linkifyMatrix = require('./linkify-matrix'); import escape from 'lodash/escape'; -import {unicodeToImage} from 'emojione'; +import {unicodeToImage, unicodeRegexp} from 'emojione'; +import classNames from 'classnames'; + +const EMOJI_REGEX = new RegExp(unicodeRegexp+"+", "gi"); var sanitizeHtmlParams = { allowedTags: [ @@ -211,7 +214,15 @@ module.exports = { finally { delete sanitizeHtmlParams.textFilter; } - return ; + + EMOJI_REGEX.lastIndex = 0; + let match = EMOJI_REGEX.exec(body); + let emojiBody = match && match[0] && match[0].length === body.length; + + let className = classNames('markdown-body', { + 'emoji-body': emojiBody, + }); + return ; }, highlightDom: function(element) { From 9c0dc7428920fab3fffac9d576b7cffa4388eb98 Mon Sep 17 00:00:00 2001 From: Aviral Dasgupta Date: Tue, 5 Jul 2016 09:58:28 +0530 Subject: [PATCH 3/6] feat: use svg emoji --- src/HtmlUtils.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 91bb063ee2..67123fe3dd 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -21,10 +21,10 @@ var sanitizeHtml = require('sanitize-html'); var highlight = require('highlight.js'); var linkifyMatrix = require('./linkify-matrix'); import escape from 'lodash/escape'; -import {unicodeToImage, unicodeRegexp} from 'emojione'; +import emojione from 'emojione'; import classNames from 'classnames'; -const EMOJI_REGEX = new RegExp(unicodeRegexp+"+", "gi"); +const EMOJI_REGEX = new RegExp(emojione.unicodeRegexp+"+", "gi"); var sanitizeHtmlParams = { allowedTags: [ @@ -209,7 +209,8 @@ module.exports = { }; } safeBody = sanitizeHtml(body, sanitizeHtmlParams); - safeBody = unicodeToImage(safeBody); + emojione.imageType = 'svg'; + safeBody = emojione.unicodeToImage(safeBody); } finally { delete sanitizeHtmlParams.textFilter; From 020f1f4320ff05fead13aca72d40d0e765c91c9b Mon Sep 17 00:00:00 2001 From: Aviral Dasgupta Date: Tue, 5 Jul 2016 10:16:17 +0530 Subject: [PATCH 4/6] feat: emojify ALL THE THINGS! --- src/HtmlUtils.js | 8 +++++++- src/components/views/avatars/BaseAvatar.js | 7 ++++--- src/components/views/messages/TextualEvent.js | 7 ++++--- src/components/views/rooms/EntityTile.js | 7 ++++--- src/components/views/rooms/RoomHeader.js | 5 ++++- src/components/views/rooms/RoomTile.js | 8 ++++++-- 6 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 67123fe3dd..bfc7c5bfb8 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -233,5 +233,11 @@ module.exports = { } }, -} + emojifyText: function(text) { + emojione.imageType = 'svg'; + return { + __html: emojione.unicodeToImage(escape(text)), + }; + }, +}; diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 121540a8c0..66f8e27b88 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -18,6 +18,7 @@ limitations under the License. var React = require('react'); var AvatarLogic = require("../../../Avatar"); +import {emojifyText} from '../../../HtmlUtils'; module.exports = React.createClass({ displayName: 'BaseAvatar', @@ -137,14 +138,14 @@ module.exports = React.createClass({ var imageUrl = this.state.imageUrls[this.state.urlsIndex]; if (imageUrl === this.state.defaultImageUrl) { - var initialLetter = this._getInitialLetter(this.props.name); + var initialLetter = emojifyText(this._getInitialLetter(this.props.name)); return ( - {TextForEvent.textForEvent(this.props.mxEvent)} +
); }, diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js index acc424b098..91874ed45a 100644 --- a/src/components/views/rooms/EntityTile.js +++ b/src/components/views/rooms/EntityTile.js @@ -20,6 +20,7 @@ var React = require('react'); var MatrixClientPeg = require('../../../MatrixClientPeg'); var sdk = require('../../../index'); +import {emojifyText} from '../../../HtmlUtils'; var PRESENCE_CLASS = { @@ -82,6 +83,7 @@ module.exports = React.createClass({ var mainClassName = "mx_EntityTile "; mainClassName += presenceClass + (this.props.className ? (" " + this.props.className) : ""); var nameEl; + let nameHTML = emojifyText(this.props.name); if (this.state.hover && !this.props.suppressOnHover) { var activeAgo = this.props.presenceLastActiveAgo ? @@ -92,7 +94,7 @@ module.exports = React.createClass({ nameEl = (
-
{ this.props.name }
+
@@ -101,8 +103,7 @@ module.exports = React.createClass({ } else { nameEl = ( -
- { this.props.name } +
); } diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 448a46b84f..0bafc90fe5 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -24,6 +24,7 @@ var Modal = require("../../../Modal"); var linkify = require('linkifyjs'); var linkifyElement = require('linkifyjs/element'); var linkifyMatrix = require('../../../linkify-matrix'); +import {emojifyText} from '../../../HtmlUtils'; linkifyMatrix(linkify); @@ -211,9 +212,11 @@ module.exports = React.createClass({ roomName = this.props.room.name; } + let roomNameHTML = emojifyText(roomName); + name =
-
{ roomName }
+
{ searchStatus }
diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 55971cdd60..da9f97ab65 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -21,6 +21,7 @@ var classNames = require('classnames'); var dis = require("../../../dispatcher"); var MatrixClientPeg = require('../../../MatrixClientPeg'); var sdk = require('../../../index'); +import {emojifyText} from '../../../HtmlUtils'; module.exports = React.createClass({ displayName: 'RoomTile', @@ -104,10 +105,13 @@ module.exports = React.createClass({ var label; if (!this.props.collapsed) { var className = 'mx_RoomTile_name' + (this.props.isInvite ? ' mx_RoomTile_invite' : ''); + let nameHTML = emojifyText(name); if (this.props.selected) { - name = { name }; + name = ; + label =
{ name }
; + } else { + label =
; } - label =
{ name }
; } else if (this.state.hover) { var RoomTooltip = sdk.getComponent("rooms.RoomTooltip"); From 6a133bc034745209e84addc76f2cd52831ee4f29 Mon Sep 17 00:00:00 2001 From: Aviral Dasgupta Date: Tue, 5 Jul 2016 10:24:18 +0530 Subject: [PATCH 5/6] feat: and emojify name in MemberInfo --- src/components/views/rooms/MemberInfo.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index ddd0d1f6c6..0e14776e82 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -32,6 +32,7 @@ var Modal = require("../../../Modal"); var sdk = require('../../../index'); var UserSettingsStore = require('../../../UserSettingsStore'); var createRoom = require('../../../createRoom'); +import {emojifyText} from '../../../HtmlUtils'; module.exports = React.createClass({ displayName: 'MemberInfo', @@ -601,6 +602,8 @@ module.exports = React.createClass({
} + let memberNameHTML = emojifyText(this.props.member.name); + var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); var PowerSelector = sdk.getComponent('elements.PowerSelector'); return ( @@ -610,7 +613,7 @@ module.exports = React.createClass({
-

{ this.props.member.name }

+

From a9a3d31b3fabe899fab57fdf9a732a691f71026e Mon Sep 17 00:00:00 2001 From: Aviral Dasgupta Date: Tue, 5 Jul 2016 10:43:09 +0530 Subject: [PATCH 6/6] feat: improve emoji-body detection --- src/HtmlUtils.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index bfc7c5bfb8..4c296c95d9 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -217,8 +217,9 @@ module.exports = { } EMOJI_REGEX.lastIndex = 0; - let match = EMOJI_REGEX.exec(body); - let emojiBody = match && match[0] && match[0].length === body.length; + let contentBodyTrimmed = content.body.trim(); + let match = EMOJI_REGEX.exec(contentBodyTrimmed); + let emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length; let className = classNames('markdown-body', { 'emoji-body': emojiBody,