Forráskód Böngészése

Add a title attribute to emojis in messages

so that you can see the shortname when you hover your mouse over an emoji.
JC Brand 5 éve
szülő
commit
b7a40dad41

+ 1 - 1
.eslintrc.json

@@ -117,7 +117,7 @@
         "no-console": "off",
         "no-catch-shadow": "off",
         "no-cond-assign": [
-            "error",
+            "off",
             "except-parens"
         ],
         "no-confusing-arrow": "off",

+ 1 - 1
src/components/emoji-picker.js

@@ -43,7 +43,7 @@ export class EmojiPicker extends CustomElement {
             'query': this.query,
             'search_results': this.search_results,
             'shouldBeHidden': shortname => this.shouldBeHidden(shortname),
-            'transformCategory': shortname => u.getEmojiRenderer()(this.getTonedShortname(shortname))
+            'transformCategory': shortname => u.shortnamesToEmojis(this.getTonedShortname(shortname))
         });
     }
 

+ 1 - 1
src/headless/converse-chat.js

@@ -951,7 +951,7 @@ converse.plugins.add('converse-chat', {
             getOutgoingMessageAttributes (text, spoiler_hint) {
                 const is_spoiler = this.get('composing_spoiler');
                 const origin_id = u.getUniqueId();
-                const body = text ? u.httpToGeoUri(u.shortnameToUnicode(text), _converse) : undefined;
+                const body = text ? u.httpToGeoUri(u.shortnamesToUnicode(text), _converse) : undefined;
                 return {
                     'from': _converse.bare_jid,
                     'fullname': _converse.xmppstatus.get('fullname'),

+ 91 - 69
src/headless/converse-emoji.js

@@ -34,8 +34,7 @@ const ASCII_REPLACE_REGEX = new RegExp("<object[^>]*>.*?<\/object>|<span[^>]*>.*
 
 
 function convert (unicode) {
-    // Converts unicode code points and code pairs
-    // to their respective characters
+    // Converts unicode code points and code pairs to their respective characters
     if (unicode.indexOf("-") > -1) {
         const parts = [],
               s = unicode.split('-');
@@ -82,44 +81,80 @@ function convertASCII2Emoji (str) {
 }
 
 
-function getEmojiMarkup (shortname, unicode_only) {
-    if ((typeof shortname === 'undefined') || (shortname === '') || (!_converse.emoji_shortnames.includes(shortname))) {
-        // if the shortname doesnt exist just return the entire match
-        return shortname;
-    }
-    const codepoint = _converse.emojis_map[shortname].cp;
-    if (codepoint) {
-        return convert(codepoint.toUpperCase());
-    } else if (unicode_only) {
+function getEmojiMarkup (data, options={unicode_only: false, add_title_wrapper: false}) {
+    const emoji = data.emoji;
+    const shortname = data.shortname;
+    if (emoji) {
+        if (api.settings.get('use_system_emojis')) {
+            return options.add_title_wrapper ? html`<span title="${shortname}">${emoji}</span>` : emoji;
+        } else {
+            const path = api.settings.get('emoji_image_path');
+            return html`<img class="emoji"
+                draggable="false"
+                alt="${emoji}"
+                src="${path}/72x72/${data.cp}.png"/>`;
+        }
+    } else if (options.unicode_only) {
         return shortname;
     } else {
-        return html`<img class="emoji" draggable="false" title="${shortname}" alt="${shortname}" src="${_converse.emojis_map[shortname].url}">`;
+        return html`<img class="emoji"
+            draggable="false"
+            title="${shortname}"
+            alt="${shortname}"
+            src="${_converse.emojis_by_sn[shortname].url}">`;
     }
 }
 
 
-function addEmojisMarkup (text, unicode_only=false) {
-    const original_text = text;
-    let list = [text];
+function getShortnameReferences (text) {
     const references = [...text.matchAll(shortnames_regex)];
-    if (references.length) {
-        references.map(ref => {
-            ref.begin = ref.index;
-            ref.end = ref.index+ref[0].length;
-            return ref;
-        })
+    return references.map(ref => {
+        const cp = _converse.emojis_by_sn[ref[0]].cp;
+        return {
+            cp,
+            'begin': ref.index,
+            'end': ref.index+ref[0].length,
+            'shortname': ref[0],
+            'emoji': cp ? convert(cp) : null
+        }
+    });
+}
+
+
+function getCodePointReferences (text) {
+    const references = [];
+    const how = {
+        callback: (icon_id) => {
+            const emoji = convert(icon_id);
+            const begin = text.indexOf(emoji);
+            references.push({
+                'emoji': emoji,
+                'end': begin + emoji.length,
+                'shortname': u.getEmojisByAtrribute('cp')[icon_id]['sn'],
+                begin,
+                cp: icon_id
+            });
+            return false;
+        }
+    };
+    twemoji.default.parse(text, how);
+    return references;
+}
+
+
+function addEmojisMarkup (text, options) {
+    let list = [text];
+    [...getShortnameReferences(text), ...getCodePointReferences(text)]
         .sort((a, b) => b.begin - a.begin)
         .forEach(ref => {
             const text = list.shift();
-            const shortname = original_text.slice(ref.begin, ref.end);
-            const emoji = getEmojiMarkup(shortname, unicode_only);
+            const emoji = getEmojiMarkup(ref, options);
             if (isString(emoji)) {
                 list = [text.slice(0, ref.begin) + emoji + text.slice(ref.end), ...list];
             } else {
                 list = [text.slice(0, ref.begin), emoji, text.slice(ref.end), ...list];
             }
         });
-    }
     return list;
 }
 
@@ -190,33 +225,16 @@ converse.plugins.add('converse-emoji', {
         const emojis_by_attribute = {};
 
         Object.assign(u, {
-            /**
-             * Based on the value of `use_system_emojis` will return either
-             * a function that converts emoji shortnames into unicode glyphs
-             * (see {@link u.shortnamesToEmojis} or one that converts them into images.
-             * @returns {function}
-             */
-            getEmojiRenderer () {
-                const how = {
-                    'attributes': icon => {
-                        const codepoint = twemoji.default.convert.toCodePoint(icon);
-                        return {'title': `${u.getEmojisByAtrribute('cp')[codepoint]['sn']} ${icon}`}
-                    }
-                };
-                const transform = u.shortnamesToEmojis;
-                return api.settings.get('use_system_emojis') ? transform : text => twemoji.default.parse(transform(text), how);
-            },
-
             /**
              * Replaces emoji shortnames in the passed-in string with unicode or image-based emojis
              * (based on the value of `use_system_emojis`).
              * @method u.addEmoji
-             * @param {string} text = The text
-             * @returns {string} The text with shortnames replaced with emoji
-             *  unicodes or images.
+             * @param { String } text = The text
+             * @returns { String } The text with shortnames replaced with emoji unicodes or images.
              */
             addEmoji (text) {
-                return u.getEmojiRenderer()(text);
+                const options = {add_title_wrapper: true, unicode_only: false};
+                return u.shortnamesToEmojis(text, options);
             },
 
             /**
@@ -231,34 +249,41 @@ converse.plugins.add('converse-emoji', {
              * an `url` attribute which points to the source for the image.
              *
              * @method u.shortnamesToEmojis
-             * @param {String} str - String containg the shortname(s)
-             * @param {Boolean} unicode_only - Whether emojis are rendered as
-             * unicode codepoints. If so, the returned result will be an array
-             * with containing one string, because the emojis themselves will
-             * also be strings. If set to false, emojis will be represented by
-             * lit-html TemplateResult objects.
+             * @param { String } str - String containg the shortname(s)
+             * @param { Object } options
+             * @param { Boolean } options.unicode_only - Whether emojis are rendered as
+             *  unicode codepoints. If so, the returned result will be an array
+             *  with containing one string, because the emojis themselves will
+             *  also be strings. If set to false, emojis will be represented by
+             *  lit-html TemplateResult objects.
+             * @param { Boolean } options.add_title_wrapper - Whether unicode
+             *  codepoints should be wrapped with a `<span>` element with a
+             *  title, so that the shortname is shown upon hovering with the
+             *  mouse.
              * @returns {Array} An array of at least one string, or otherwise
              * strings and lit-html TemplateResult objects.
              */
-            shortnamesToEmojis (str, unicode_only=false) {
+            shortnamesToEmojis (str, options={unicode_only: false, add_title_wrapper: false}) {
                 str = convertASCII2Emoji(str);
-                return addEmojisMarkup(str, unicode_only);
+                return addEmojisMarkup(str, options);
             },
 
             /**
-             * Returns unicode represented by the passed in shortname.
-             * @method u.shortnameToUnicode
-             * @param {string} str - String containing the shortname(s)
+             * Replaces all shortnames in the passed in string with their
+             * unicode (emoji) representation.
+             * @method u.shortnamesToUnicode
+             * @param { String } str - String containing the shortname(s)
+             * @returns { String }
              */
-            shortnameToUnicode (str) {
-                return u.shortnamesToEmojis(str, true)[0];
+            shortnamesToUnicode (str) {
+                return u.shortnamesToEmojis(str, {'unicode_only': true})[0];
             },
 
             /**
              * Determines whether the passed in string is just a single emoji shortname;
              * @method u.isOnlyEmojis
-             * @param {string} shortname - A string which migh be just an emoji shortname
-             * @returns {boolean}
+             * @param { String } shortname - A string which migh be just an emoji shortname
+             * @returns { Boolean }
              */
             isOnlyEmojis (text) {
                 const words = text.trim().split(/\s+/);
@@ -266,7 +291,7 @@ converse.plugins.add('converse-emoji', {
                     return false;
                 }
                 const rejects = words.filter(text => {
-                    const result = twemoji.default.parse(u.shortnameToUnicode(text));
+                    const result = twemoji.default.parse(u.shortnamesToUnicode(text));
                     const match = result.match(/<img class="emoji" draggable="false" alt=".*?" src=".*?\.png"\/>/);
                     return !match || match.length !== 1;
                 });
@@ -275,9 +300,9 @@ converse.plugins.add('converse-emoji', {
 
             /**
              * @method u.getEmojisByAtrribute
-             * @param {string} attr - The attribute according to which the
+             * @param { String } attr - The attribute according to which the
              *  returned map should be keyed.
-             * @returns {object} - Map of emojis with the passed in attribute values
+             * @returns { Object } - Map of emojis with the passed in attribute values
              *  as keys and a list of emojis for a particular category as values.
              */
             getEmojisByAtrribute (attr) {
@@ -303,10 +328,7 @@ converse.plugins.add('converse-emoji', {
         // We extend the default converse.js API to add methods specific to MUC groupchats.
         Object.assign(api, {
             /**
-             * The "rooms" namespace groups methods relevant to chatrooms
-             * (aka groupchats).
-             *
-             * @namespace api.rooms
+             * @namespace api.emojis
              * @memberOf api
              */
             emojis: {
@@ -323,8 +345,8 @@ converse.plugins.add('converse-emoji', {
                     const { default: json } = await import(/*webpackChunkName: "emojis" */ './emojis.json');
                     _converse.emojis.json = json;
                     _converse.emojis.categories = Object.keys(_converse.emojis.json);
-                    _converse.emojis_map = _converse.emojis.categories.reduce((result, cat) => Object.assign(result, _converse.emojis.json[cat]), {});
-                    _converse.emojis_list = Object.values(_converse.emojis_map);
+                    _converse.emojis_by_sn = _converse.emojis.categories.reduce((result, cat) => Object.assign(result, _converse.emojis.json[cat]), {});
+                    _converse.emojis_list = Object.values(_converse.emojis_by_sn);
                     _converse.emojis_list.sort((a, b) => a.sn < b.sn ? -1 : (a.sn > b.sn ? 1 : 0));
                     _converse.emoji_shortnames = _converse.emojis_list.map(m => m.sn);
 

+ 1 - 1
src/headless/converse-muc.js

@@ -997,7 +997,7 @@ converse.plugins.add('converse-muc', {
                 const is_spoiler = this.get('composing_spoiler');
                 const [text, references] = this.parseTextForReferences(original_message);
                 const origin_id = u.getUniqueId();
-                const body = text ? u.httpToGeoUri(u.shortnameToUnicode(text), _converse) : undefined;
+                const body = text ? u.httpToGeoUri(u.shortnamesToUnicode(text), _converse) : undefined;
                 return {
                     body,
                     is_spoiler,

+ 3 - 6
src/templates/emoji_picker.js

@@ -1,5 +1,5 @@
 import { __ } from '@converse/headless/i18n';
-import { _converse, api } from "@converse/headless/converse-core";
+import { _converse, converse, api } from "@converse/headless/converse-core";
 import { html } from "lit-html";
 
 const u = converse.env.utils;
@@ -32,7 +32,7 @@ const emoji_picker_header = (o) => html`
 const emoji_item = (o) => {
     return html`
         <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}">${o.transform(o.emoji.sn)}</a>
+            <a href="#" @click=${o.onEmojiPicked} data-emoji="${o.emoji.sn}">${u.shortnamesToEmojis(o.emoji.sn)}</a>
         </li>
     `;
 }
@@ -53,16 +53,14 @@ const emojis_for_category = (o) => html`
     </ul>
 `;
 
-
 const skintone_emoji = (o) => {
     return html`
         <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}>${o.transform(':'+o.skintone+':')}</a>
+            <a class="pick-skintone" href="#" data-skintone="${o.skintone}" @click=${o.onSkintonePicked}>${u.shortnamesToEmojis(':'+o.skintone+':')}</a>
         </li>
     `;
 }
 
-
 const all_emojis = (o) => html`
     <span ?hidden=${o.query} class="emoji-lists__container emoji-lists__container--browse">
         ${Object.keys(o.emoji_categories).map(category => (o.emoji_categories[category] ? emojis_for_category(Object.assign({category}, o)) : ''))}
@@ -73,7 +71,6 @@ const all_emojis = (o) => html`
 export default (o) => {
     o.emoji_categories = api.settings.get('emoji_categories');
     o.emojis_by_category = _converse.emojis.json;
-    o.transform = u.getEmojiRenderer();
     o.toned_emojis = _converse.emojis.toned;
 
     return html`