diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js
index 2ab635081f..c0792e6d14 100644
--- a/src/HtmlUtils.js
+++ b/src/HtmlUtils.js
@@ -24,8 +24,39 @@ import escape from 'lodash/escape';
import emojione from 'emojione';
import classNames from 'classnames';
+emojione.imagePathSVG = 'emojione/svg/';
+emojione.imageType = 'svg';
+
const EMOJI_REGEX = new RegExp(emojione.unicodeRegexp+"+", "gi");
+/* modified from https://github.com/Ranks/emojione/blob/master/lib/js/emojione.js
+ * because we want to include emoji shortnames in title text
+ */
+export function unicodeToImage(str) {
+ let replaceWith, unicode, alt;
+ const mappedUnicode = emojione.mapUnicodeToShort();
+
+ str = str.replace(emojione.regUnicode, function(unicodeChar) {
+ if ( (typeof unicodeChar === 'undefined') || (unicodeChar === '') || (!(unicodeChar in emojione.jsEscapeMap)) ) {
+ // if the unicodeChar doesnt exist just return the entire match
+ return unicodeChar;
+ }
+ else {
+ // get the unicode codepoint from the actual char
+ unicode = emojione.jsEscapeMap[unicodeChar];
+
+ // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname
+ alt = (emojione.unicodeAlt) ? emojione.convert(unicode.toUpperCase()) : mappedUnicode[unicode];
+ const title = mappedUnicode[unicode];
+
+ replaceWith = `
`;
+ return replaceWith;
+ }
+ });
+
+ return str;
+};
+
var sanitizeHtmlParams = {
allowedTags: [
'font', // custom to matrix for IRC-style font coloring
@@ -211,8 +242,7 @@ module.exports = {
};
}
safeBody = sanitizeHtml(body, sanitizeHtmlParams);
- emojione.imageType = 'svg';
- safeBody = emojione.unicodeToImage(safeBody);
+ safeBody = unicodeToImage(safeBody);
}
finally {
delete sanitizeHtmlParams.textFilter;
@@ -239,7 +269,6 @@ module.exports = {
},
emojifyText: function(text) {
- emojione.imageType = 'svg';
return {
__html: emojione.unicodeToImage(escape(text)),
};
diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js
index 574144e95b..37a50ee8d8 100644
--- a/src/autocomplete/EmojiProvider.js
+++ b/src/autocomplete/EmojiProvider.js
@@ -26,7 +26,7 @@ export default class EmojiProvider extends AutocompleteProvider {
completion: shortnameToUnicode(shortname),
component: (
- {shortname}
+ {shortname}
),
range,
diff --git a/src/component-index.js b/src/component-index.js
index 97f8882b82..c7355b5b4c 100644
--- a/src/component-index.js
+++ b/src/component-index.js
@@ -54,6 +54,7 @@ module.exports.components['views.dialogs.SetDisplayNameDialog'] = require('./com
module.exports.components['views.dialogs.TextInputDialog'] = require('./components/views/dialogs/TextInputDialog');
module.exports.components['views.elements.EditableText'] = require('./components/views/elements/EditableText');
module.exports.components['views.elements.EditableTextContainer'] = require('./components/views/elements/EditableTextContainer');
+module.exports.components['views.elements.EmojiText'] = require('./components/views/elements/EmojiText');
module.exports.components['views.elements.PowerSelector'] = require('./components/views/elements/PowerSelector');
module.exports.components['views.elements.ProgressBar'] = require('./components/views/elements/ProgressBar');
module.exports.components['views.elements.TintableSvg'] = require('./components/views/elements/TintableSvg');
@@ -72,6 +73,7 @@ module.exports.components['views.messages.MFileBody'] = require('./components/vi
module.exports.components['views.messages.MImageBody'] = require('./components/views/messages/MImageBody');
module.exports.components['views.messages.MVideoBody'] = require('./components/views/messages/MVideoBody');
module.exports.components['views.messages.MessageEvent'] = require('./components/views/messages/MessageEvent');
+module.exports.components['views.messages.SenderProfile'] = require('./components/views/messages/SenderProfile');
module.exports.components['views.messages.TextualBody'] = require('./components/views/messages/TextualBody');
module.exports.components['views.messages.TextualEvent'] = require('./components/views/messages/TextualEvent');
module.exports.components['views.messages.UnknownBody'] = require('./components/views/messages/UnknownBody');
diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js
index 9a0d3dbbdd..c6f2d6500b 100644
--- a/src/components/structures/RoomStatusBar.js
+++ b/src/components/structures/RoomStatusBar.js
@@ -189,6 +189,7 @@ module.exports = React.createClass({
_getContent: function() {
var TabCompleteBar = sdk.getComponent('rooms.TabCompleteBar');
var TintableSvg = sdk.getComponent("elements.TintableSvg");
+ const EmojiText = sdk.getComponent('elements.EmojiText');
// no conn bar trumps unread count since you can't get unread messages
// without a connection! (technically may already have some but meh)
@@ -262,7 +263,7 @@ module.exports = React.createClass({
if (typingString) {
return (
- {typingString}
+ {typingString}
);
}
diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js
index f0a36c6608..47f0a76891 100644
--- a/src/components/views/avatars/BaseAvatar.js
+++ b/src/components/views/avatars/BaseAvatar.js
@@ -18,7 +18,7 @@ limitations under the License.
var React = require('react');
var AvatarLogic = require("../../../Avatar");
-import {emojifyText} from '../../../HtmlUtils';
+import sdk from '../../../index';
module.exports = React.createClass({
displayName: 'BaseAvatar',
@@ -133,6 +133,7 @@ module.exports = React.createClass({
},
render: function() {
+ const EmojiText = sdk.getComponent('elements.EmojiText');
var imageUrl = this.state.imageUrls[this.state.urlsIndex];
const {
@@ -142,15 +143,13 @@ module.exports = React.createClass({
} = this.props;
if (imageUrl === this.state.defaultImageUrl) {
- var initialLetter = emojifyText(this._getInitialLetter(name));
+ const initialLetter = this._getInitialLetter(name);
return (
-
-
+ lineHeight: height + "px" }}>{initialLetter}
diff --git a/src/components/views/elements/EmojiText.js b/src/components/views/elements/EmojiText.js
new file mode 100644
index 0000000000..cb6cd2ef5e
--- /dev/null
+++ b/src/components/views/elements/EmojiText.js
@@ -0,0 +1,33 @@
+/*
+ Copyright 2016 Aviral Dasgupta
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+import React from 'react';
+import {emojifyText} from '../../../HtmlUtils';
+
+export default function EmojiText(props) {
+ const {element, children, ...restProps} = props;
+ restProps.dangerouslySetInnerHTML = emojifyText(children);
+ return React.createElement(element, restProps);
+}
+
+EmojiText.propTypes = {
+ element: React.PropTypes.string,
+ children: React.PropTypes.string.isRequired,
+};
+
+EmojiText.defaultProps = {
+ element: 'span',
+};
diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js
new file mode 100644
index 0000000000..9e6fba2127
--- /dev/null
+++ b/src/components/views/messages/SenderProfile.js
@@ -0,0 +1,42 @@
+/*
+ Copyright 2015, 2016 OpenMarket Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+'use strict';
+
+import React from 'react';
+import sdk from '../../../index';
+
+export default function SenderProfile(props) {
+ const EmojiText = sdk.getComponent('elements.EmojiText');
+ const {mxEvent} = props;
+ const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
+ const {msgtype} = mxEvent.getContent();
+
+ if (msgtype === 'm.emote') {
+ return ; // emote message must include the name so don't duplicate it
+ }
+
+ return (
+ {`${name || ''} ${props.aux || ''}`}
+ );
+}
+
+SenderProfile.propTypes = {
+ mxEvent: React.PropTypes.object.isRequired, // event whose sender we're showing
+ aux: React.PropTypes.string, // stuff to go after the sender name, if anything
+ onClick: React.PropTypes.func,
+};
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 8c6cf455dc..19ca9657c2 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -177,6 +177,7 @@ module.exports = React.createClass({
},
render: function() {
+ const EmojiText = sdk.getComponent('elements.EmojiText');
var mxEvent = this.props.mxEvent;
var content = mxEvent.getContent();
var body = HtmlUtils.bodyToHtml(content, this.props.highlights, {});
@@ -200,10 +201,10 @@ module.exports = React.createClass({
switch (content.msgtype) {
case "m.emote":
- var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
+ const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
return (
- * { name } { body }
+ * {name} { body }
{ widgets }
);
diff --git a/src/components/views/messages/TextualEvent.js b/src/components/views/messages/TextualEvent.js
index 251a44a30a..7cab98ea84 100644
--- a/src/components/views/messages/TextualEvent.js
+++ b/src/components/views/messages/TextualEvent.js
@@ -19,7 +19,7 @@ limitations under the License.
var React = require('react');
var TextForEvent = require('../../../TextForEvent');
-import {emojifyText} from '../../../HtmlUtils';
+import sdk from '../../../index';
module.exports = React.createClass({
displayName: 'TextualEvent',
@@ -31,13 +31,11 @@ module.exports = React.createClass({
},
render: function() {
+ const EmojiText = sdk.getComponent('elements.EmojiText');
var text = TextForEvent.textForEvent(this.props.mxEvent);
if (text == null || text.length === 0) return null;
- let textHTML = emojifyText(TextForEvent.textForEvent(this.props.mxEvent));
-
return (
-
-
+ {text}
);
},
});
diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js
index 8a99b4c565..d29137ffc2 100644
--- a/src/components/views/rooms/EntityTile.js
+++ b/src/components/views/rooms/EntityTile.js
@@ -20,7 +20,6 @@ var React = require('react');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var sdk = require('../../../index');
-import {emojifyText} from '../../../HtmlUtils';
var PRESENCE_CLASS = {
@@ -103,8 +102,9 @@ module.exports = React.createClass({
var mainClassName = "mx_EntityTile ";
mainClassName += presenceClass + (this.props.className ? (" " + this.props.className) : "");
var nameEl;
- let nameHTML = emojifyText(this.props.name);
+ const {name} = this.props;
+ const EmojiText = sdk.getComponent('elements.EmojiText');
if (this.state.hover && !this.props.suppressOnHover) {
var activeAgo = this.props.presenceLastActiveAgo ?
(Date.now() - (this.props.presenceLastTs - this.props.presenceLastActiveAgo)) : -1;
@@ -114,7 +114,7 @@ module.exports = React.createClass({
nameEl = (

-
+
{name}
@@ -123,8 +123,7 @@ module.exports = React.createClass({
}
else {
nameEl = (
-
-
+
{name}
);
}
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index c087e7dc71..0cca402be6 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -32,7 +32,6 @@ 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',
@@ -637,10 +636,11 @@ module.exports = React.createClass({
}
- let memberNameHTML = emojifyText(this.props.member.name);
+ const memberName = this.props.member.name;
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
var PowerSelector = sdk.getComponent('elements.PowerSelector');
+ const EmojiText = sdk.getComponent('elements.EmojiText');
return (

@@ -648,7 +648,7 @@ module.exports = React.createClass({
-
+ {memberNameHTML}
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js
index 8c1b2aaff8..ff023ee043 100644
--- a/src/components/views/rooms/RoomHeader.js
+++ b/src/components/views/rooms/RoomHeader.js
@@ -24,7 +24,6 @@ var Modal = require("../../../Modal");
var linkify = require('linkifyjs');
var linkifyElement = require('linkifyjs/element');
var linkifyMatrix = require('../../../linkify-matrix');
-import {emojifyText} from '../../../HtmlUtils';
linkifyMatrix(linkify);
@@ -145,6 +144,7 @@ module.exports = React.createClass({
var RoomAvatar = sdk.getComponent("avatars.RoomAvatar");
var ChangeAvatar = sdk.getComponent("settings.ChangeAvatar");
var TintableSvg = sdk.getComponent("elements.TintableSvg");
+ const EmojiText = sdk.getComponent('elements.EmojiText');
var header;
var name = null;
@@ -212,13 +212,12 @@ module.exports = React.createClass({
roomName = this.props.room.name;
}
- let roomNameHTML = emojifyText(roomName);
name =
-
+
{roomName}
{ searchStatus }
-
+
;
}
if (can_set_room_topic) {
diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js
index 111ead05b7..07b4f2618b 100644
--- a/src/components/views/rooms/RoomTile.js
+++ b/src/components/views/rooms/RoomTile.js
@@ -22,7 +22,6 @@ var dis = require("../../../dispatcher");
var MatrixClientPeg = require('../../../MatrixClientPeg');
var sdk = require('../../../index');
var ContextualMenu = require('../../structures/ContextualMenu');
-import {emojifyText} from '../../../HtmlUtils';
module.exports = React.createClass({
displayName: 'RoomTile',
@@ -187,6 +186,7 @@ module.exports = React.createClass({
badge =
{ badgeContent }
;
+ const EmojiText = sdk.getComponent('elements.EmojiText');
var label;
var tooltip;
if (!this.props.collapsed) {
@@ -196,13 +196,12 @@ module.exports = React.createClass({
'mx_RoomTile_badgeShown': this.props.highlight || (notificationCount > 0 && !this.state.areNotifsMuted) || this.state.badgeHover || this.state.menu,
});
- let nameHTML = emojifyText(name);
if (this.props.selected) {
- let nameSelected =
;
+ let nameSelected =
{name};
label =
{ nameSelected }
;
} else {
- label =
;
+ label =
{name};
}
}
else if (this.state.hover) {