Commit be20b8e1 authored by JC Brand's avatar JC Brand

emojis: fix rendering of custom emojis

parent 77a0a01e
...@@ -12,6 +12,7 @@ module.exports = function(config) { ...@@ -12,6 +12,7 @@ module.exports = function(config) {
{ pattern: "dist/emojis.js", served: true }, { pattern: "dist/emojis.js", served: true },
"dist/converse.js", "dist/converse.js",
"dist/converse.css", "dist/converse.css",
{ pattern: "dist/images/**/*.*", included: false },
{ pattern: "dist/webfonts/**/*.*", included: false }, { pattern: "dist/webfonts/**/*.*", included: false },
{ pattern: "dist/\@fortawesome/fontawesome-free/sprites/solid.svg", { pattern: "dist/\@fortawesome/fontawesome-free/sprites/solid.svg",
watched: false, watched: false,
...@@ -60,7 +61,8 @@ module.exports = function(config) { ...@@ -60,7 +61,8 @@ module.exports = function(config) {
], ],
proxies: { proxies: {
"/dist/\@fortawesome/fontawesome-free/sprites/solid.svg": "/base/dist/\@fortawesome/fontawesome-free/sprites/solid.svg" "/dist/\@fortawesome/fontawesome-free/sprites/solid.svg": "/base/dist/\@fortawesome/fontawesome-free/sprites/solid.svg",
"/dist/images/custom_emojis/": "/base/dist/images/custom_emojis/"
}, },
client: { client: {
......
...@@ -100,7 +100,6 @@ describe("Emojis", function () { ...@@ -100,7 +100,6 @@ describe("Emojis", function () {
done(); done();
})); }));
it("allows you to search for particular emojis", it("allows you to search for particular emojis",
mock.initConverse( mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {}, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
...@@ -239,5 +238,51 @@ describe("Emojis", function () { ...@@ -239,5 +238,51 @@ describe("Emojis", function () {
expect(u.hasClass('chat-msg__text--larger', message)).toBe(true); expect(u.hasClass('chat-msg__text--larger', message)).toBe(true);
done() done()
})); }));
it("can show custom emojis",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'],
{ emoji_categories: {
"smileys": ":grinning:",
"people": ":thumbsup:",
"activity": ":soccer:",
"travel": ":motorcycle:",
"objects": ":bomb:",
"nature": ":rainbow:",
"food": ":hotdog:",
"symbols": ":musical_note:",
"flags": ":flag_ac:",
"custom": ':xmpp:'
} },
async function (done, _converse) {
await mock.waitForRoster(_converse, 'current', 1);
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
await mock.openChatBoxFor(_converse, contact_jid);
const view = _converse.api.chatviews.get(contact_jid);
const toolbar = await u.waitUntil(() => view.el.querySelector('ul.chat-toolbar'));
expect(toolbar.querySelectorAll('li.toggle-smiley__container').length).toBe(1);
toolbar.querySelector('a.toggle-smiley').click();
await u.waitUntil(() => u.isVisible(view.el.querySelector('.emoji-picker__lists')), 1000);
const picker = await u.waitUntil(() => view.el.querySelector('.emoji-picker__container'), 1000);
const custom_category = picker.querySelector('.pick-category[data-category="custom"]');
expect(custom_category.innerHTML.replace(/<!---->/g, '').trim()).toBe(
'<img class="emoji" draggable="false" title=":xmpp:" alt=":xmpp:" src="/dist/images/custom_emojis/xmpp.png">');
const textarea = view.el.querySelector('textarea.chat-textarea');
textarea.value = 'Running tests for :converse:';
view.onKeyDown({
target: textarea,
preventDefault: function preventDefault () {},
keyCode: 13 // Enter
});
await new Promise(resolve => view.model.messages.once('rendered', resolve));
const body = view.el.querySelector('converse-chat-message-body');
expect(body.innerHTML.replace(/<!---->/g, '').trim()).toBe(
'Running tests for <img class="emoji" draggable="false" title=":converse:" alt=":converse:" src="/dist/images/custom_emojis/converse.png">');
done();
}));
}); });
}); });
...@@ -951,21 +951,23 @@ converse.plugins.add('converse-chat', { ...@@ -951,21 +951,23 @@ converse.plugins.add('converse-chat', {
getOutgoingMessageAttributes (text, spoiler_hint) { getOutgoingMessageAttributes (text, spoiler_hint) {
const is_spoiler = this.get('composing_spoiler'); const is_spoiler = this.get('composing_spoiler');
const origin_id = u.getUniqueId(); const origin_id = u.getUniqueId();
const body = text ? u.httpToGeoUri(u.shortnameToUnicode(text), _converse) : undefined;
return { return {
'from': _converse.bare_jid,
'fullname': _converse.xmppstatus.get('fullname'),
'id': origin_id, 'id': origin_id,
'is_only_emojis': text ? u.isOnlyEmojis(text) : false,
'jid': this.get('jid'), 'jid': this.get('jid'),
'nickname': this.get('nickname'), 'message': body,
'msgid': origin_id, 'msgid': origin_id,
'origin_id': origin_id, 'nickname': this.get('nickname'),
'fullname': _converse.xmppstatus.get('fullname'),
'from': _converse.bare_jid,
'is_only_emojis': text ? u.isOnlyEmojis(text) : false,
'sender': 'me', 'sender': 'me',
'time': (new Date()).toISOString(),
'message': text ? u.httpToGeoUri(u.shortnameToUnicode(text), _converse) : undefined,
'is_spoiler': is_spoiler,
'spoiler_hint': is_spoiler ? spoiler_hint : undefined, 'spoiler_hint': is_spoiler ? spoiler_hint : undefined,
'type': this.get('message_type') 'time': (new Date()).toISOString(),
'type': this.get('message_type'),
body,
is_spoiler,
origin_id
} }
}, },
......
This diff is collapsed.
...@@ -964,8 +964,6 @@ converse.plugins.add('converse-muc', { ...@@ -964,8 +964,6 @@ converse.plugins.add('converse-muc', {
if (!raw_mentions) return [original_message, []]; if (!raw_mentions) return [original_message, []];
const known_nicknames = this.getAllKnownNicknames(); const known_nicknames = this.getAllKnownNicknames();
const known_nicknames_with_at_regex = this.getAllKnownNicknamesRegex();
const getMatchesForNickRegex = nick_regex => [...findRegexInMessage(nick_regex)];
const getMatchingNickname = p.findFirstMatchInArray(known_nicknames); const getMatchingNickname = p.findFirstMatchInArray(known_nicknames);
const uriFromNickname = nickname => { const uriFromNickname = nickname => {
...@@ -985,7 +983,7 @@ converse.plugins.add('converse-muc', { ...@@ -985,7 +983,7 @@ converse.plugins.add('converse-muc', {
return { begin, end, value, type, uri } return { begin, end, value, type, uri }
} }
const mentions = getMatchesForNickRegex(known_nicknames_with_at_regex); const mentions = [...findRegexInMessage(this.getAllKnownNicknamesRegex())];
const references = mentions.map(matchToReference); const references = mentions.map(matchToReference);
const [updated_message, updated_references] = p.reduceTextFromReferences( const [updated_message, updated_references] = p.reduceTextFromReferences(
......
{ {
"custom": { "custom": {
":converse:":{"sn":":converse:","url":"/dist/custom_emojis/converse.png","c":"custom"}, ":converse:":{"sn":":converse:","url":"/dist/images/custom_emojis/converse.png","c":"custom"},
":xmpp:":{"sn":":xmpp:","url":"/dist/custom_emojis/xmpp.png","c":"custom"} ":xmpp:":{"sn":":xmpp:","url":"/dist/images/custom_emojis/xmpp.png","c":"custom"}
}, },
"smileys": { "smileys": {
":smiley:":{"sn":":smiley:","cp":"1f603","sns":[],"c":"smileys"}, ":smiley:":{"sn":":smiley:","cp":"1f603","sns":[],"c":"smileys"},
......
...@@ -30,12 +30,12 @@ class MessageBodyRenderer extends String { ...@@ -30,12 +30,12 @@ class MessageBodyRenderer extends String {
text = text.replace(/\n\n+/g, '\n\n'); text = text.replace(/\n\n+/g, '\n\n');
text = u.geoUriToHttp(text, _converse.geouri_replacement); text = u.geoUriToHttp(text, _converse.geouri_replacement);
const process = (text) => { let list = await Promise.all(u.addHyperlinks(text));
text = u.addEmoji(text);
return addMentionsMarkup(text, this.model.get('references'), this.model.collection.chatbox); list = list.reduce((acc, i) => isString(i) ? [...acc, ...u.addEmoji(i)] : [...acc, i], []);
}
const list = await Promise.all(u.addHyperlinks(text)); const addMentions = text => addMentionsMarkup(text, this.model.get('references'), this.model.collection.chatbox)
this.list = list.reduce((acc, i) => isString(i) ? [...acc, ...process(i)] : [...acc, i], []); list = list.reduce((acc, i) => isString(i) ? [...acc, ...addMentions(i)] : [...acc, i], []);
/** /**
* Synchronous event which provides a hook for transforming a chat message's body text * Synchronous event which provides a hook for transforming a chat message's body text
* after the default transformations have been applied. * after the default transformations have been applied.
...@@ -45,8 +45,7 @@ class MessageBodyRenderer extends String { ...@@ -45,8 +45,7 @@ class MessageBodyRenderer extends String {
* @example _converse.api.listen.on('afterMessageBodyTransformed', (view, text) => { ... }); * @example _converse.api.listen.on('afterMessageBodyTransformed', (view, text) => { ... });
*/ */
await api.trigger('afterMessageBodyTransformed', this.model, text, {'Synchronous': true}); await api.trigger('afterMessageBodyTransformed', this.model, text, {'Synchronous': true});
return list;
return this.list;
} }
async render () { async render () {
......
import { html } from "lit-html"; import { html } from "lit-html";
import { __ } from '@converse/headless/i18n'; import { __ } from '@converse/headless/i18n';
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
import xss from "xss/dist/xss";
const i18n_search = __('Search'); const i18n_search = __('Search');
...@@ -10,7 +8,6 @@ const skintones = ['tone1', 'tone2', 'tone3', 'tone4', 'tone5']; ...@@ -10,7 +8,6 @@ const skintones = ['tone1', 'tone2', 'tone3', 'tone4', 'tone5'];
const emoji_category = (o) => { const emoji_category = (o) => {
const category_emoji = unsafeHTML(xss.filterXSS(o.transformCategory(o.emoji_categories[o.category]), {'whiteList': {'img': ['class', 'draggable' ,'alt', 'src', 'title']}}));
return html` return html`
<li data-category="${o.category}" <li data-category="${o.category}"
class="emoji-category ${o.category} ${(o.current_category === o.category) ? 'picked' : ''}" class="emoji-category ${o.category} ${(o.current_category === o.category) ? 'picked' : ''}"
...@@ -19,7 +16,7 @@ const emoji_category = (o) => { ...@@ -19,7 +16,7 @@ const emoji_category = (o) => {
<a class="pick-category" <a class="pick-category"
@click=${o.onCategoryPicked} @click=${o.onCategoryPicked}
href="#emoji-picker-${o.category}" href="#emoji-picker-${o.category}"
data-category="${o.category}">${category_emoji} </a> data-category="${o.category}">${o.transformCategory(o.emoji_categories[o.category])} </a>
</li> </li>
`; `;
} }
...@@ -30,12 +27,10 @@ const emoji_picker_header = (o) => html` ...@@ -30,12 +27,10 @@ const emoji_picker_header = (o) => html`
</ul> </ul>
`; `;
const emoji_item = (o) => { const emoji_item = (o) => {
const emoji = unsafeHTML(xss.filterXSS(o.transform(o.emoji.sn), {'whiteList': {'img': ['class', 'draggable' ,'alt', 'src', 'title']}}));
return html` return html`
<li class="emoji insert-emoji ${o.shouldBeHidden(o.emoji.sn) ? 'hidden' : ''}" data-emoji="${o.emoji.sn}" title="${o.emoji.sn}"> <li class="emoji insert-emoji ${o.shouldBeHidden(o.emoji.sn) ? 'hidden' : ''}" data-emoji="${o.emoji.sn}" title="${o.emoji.sn}">
<a href="#" @click=${o.onEmojiPicked} data-emoji="${o.emoji.sn}">${emoji}</a> <a href="#" @click=${o.onEmojiPicked} data-emoji="${o.emoji.sn}">${o.transform(o.emoji.sn)}</a>
</li> </li>
`; `;
} }
...@@ -58,11 +53,9 @@ const emojis_for_category = (o) => html` ...@@ -58,11 +53,9 @@ const emojis_for_category = (o) => html`
const skintone_emoji = (o) => { const skintone_emoji = (o) => {
const shortname = ':'+o.skintone+':';
const emoji = unsafeHTML(xss.filterXSS(o.transform(shortname), {'whiteList': {'img': ['class', 'draggable' ,'alt', 'src', 'title']}}));
return html` return html`
<li data-skintone="${o.skintone}" class="emoji-skintone ${(o.current_skintone === o.skintone) ? 'picked' : ''}"> <li data-skintone="${o.skintone}" class="emoji-skintone ${(o.current_skintone === o.skintone) ? 'picked' : ''}">
<a class="pick-skintone" href="#" data-skintone="${o.skintone}" @click=${o.onSkintonePicked}>${emoji}</a> <a class="pick-skintone" href="#" data-skintone="${o.skintone}" @click=${o.onSkintonePicked}>${o.transform(':'+o.skintone+':')}</a>
</li> </li>
`; `;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment