From b0a9782d31ded59077ac733c0d0558bf8b5bd704 Mon Sep 17 00:00:00 2001 From: JC Brand Date: Tue, 21 Jan 2020 12:45:34 +0100 Subject: [PATCH] emoji: Lazy load the emoji JSON Only load it once the first chat has been opened. --- spec/bookmarks.js | 4 +- spec/chatbox.js | 24 ++-- spec/controlbox.js | 2 +- spec/converse.js | 4 +- spec/emojis.js | 4 +- spec/http-file-upload.js | 8 +- spec/messages.js | 18 +-- spec/muc.js | 22 ++-- spec/omemo.js | 2 +- spec/roomslist.js | 10 +- spec/user-details-modal.js | 10 +- src/headless/converse-chatboxes.js | 4 +- src/headless/converse-emoji.js | 177 +++++++---------------------- src/headless/utils/core.js | 2 +- tests/mock.js | 3 +- 15 files changed, 95 insertions(+), 199 deletions(-) diff --git a/spec/bookmarks.js b/spec/bookmarks.js index e72d63e11..0d339f612 100644 --- a/spec/bookmarks.js +++ b/spec/bookmarks.js @@ -16,9 +16,7 @@ describe("A chat room", function () { - it("can be bookmarked", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, - async function (done, _converse) { + it("can be bookmarked", mock.initConverse(['rosterGroupsFetched'], {}, async function (done, _converse) { await test_utils.waitUntilDiscoConfirmed( _converse, _converse.bare_jid, diff --git a/spec/chatbox.js b/spec/chatbox.js index adf331c1c..77f5f8821 100644 --- a/spec/chatbox.js +++ b/spec/chatbox.js @@ -162,7 +162,7 @@ })); it("can be trimmed to conserve space", - mock.initConverse(['rosterGroupsFetched', 'emojisInitialized'], {}, + mock.initConverse(['rosterGroupsFetched'], {}, async function (done, _converse) { spyOn(_converse.chatboxviews, 'trimChats'); @@ -523,7 +523,7 @@ it("does not open a new chatbox", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current'); @@ -884,7 +884,7 @@ it("is sent if the user has stopped typing since 2 minutes", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { const sent_stanzas = _converse.connection.sent_stanzas; @@ -1190,7 +1190,7 @@ it("is incremented from zero when chatbox was closed after viewing previously received messages and the window is not focused now", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current'); @@ -1271,7 +1271,7 @@ })); it("is incremeted when message is received, chatbox is scrolled down and the window is not focused", - mock.initConverse(['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + mock.initConverse(['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current'); @@ -1291,7 +1291,7 @@ it("is incremeted when message is received, chatbox is scrolled up and the window is not focused", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current', 1); @@ -1309,7 +1309,7 @@ it("is cleared when ChatBoxView was scrolled down and the window become focused", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current', 1); @@ -1328,7 +1328,7 @@ it("is not cleared when ChatBoxView was scrolled up and the windows become focused", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current', 1); @@ -1433,7 +1433,7 @@ it("is cleared when unread messages are viewed which were received in scrolled-up chatbox", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { await test_utils.openControlBox(_converse); @@ -1458,7 +1458,7 @@ it("is not cleared after user clicks on roster view when chatbox is already opened and scrolled up", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current', 1); @@ -1485,7 +1485,7 @@ it("is displayed when scrolled up chatbox is minimized after receiving unread messages", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current', 1); @@ -1513,7 +1513,7 @@ it("is incremented when message is received and windows is not focused", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current', 1); diff --git a/spec/controlbox.js b/spec/controlbox.js index d4d4b74a0..80fdbb1e6 100644 --- a/spec/controlbox.js +++ b/spec/controlbox.js @@ -38,7 +38,7 @@ it("can be used to add contact and it checks for case-sensivity", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched'], {}, async function (done, _converse) { spyOn(_converse.api, "trigger").and.callThrough(); diff --git a/spec/converse.js b/spec/converse.js index a2b07bb25..d740c01ae 100644 --- a/spec/converse.js +++ b/spec/converse.js @@ -214,9 +214,7 @@ describe("The \"contacts\" API", function () { it("has a method 'get' which returns wrapped contacts", - mock.initConverse( - ['emojisInitialized'], {}, - async function (done, _converse) { + mock.initConverse([], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current'); let contact = await _converse.api.contacts.get('non-existing@jabber.org'); diff --git a/spec/emojis.js b/spec/emojis.js index 2b15afd07..2e6007d1a 100644 --- a/spec/emojis.js +++ b/spec/emojis.js @@ -105,7 +105,7 @@ it("allows you to search for particular emojis", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { const muc_jid = 'lounge@montague.lit'; @@ -158,7 +158,7 @@ describe("A Chat Message", function () { it("will display larger if it's only emojis", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {'use_system_emojis': true}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {'use_system_emojis': true}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current'); diff --git a/spec/http-file-upload.js b/spec/http-file-upload.js index 9e62a2674..b3ad4a049 100644 --- a/spec/http-file-upload.js +++ b/spec/http-file-upload.js @@ -171,9 +171,7 @@ describe("A file upload toolbar button", function () { it("does not appear in private chats", - mock.initConverse( - ['emojisInitialized'], {}, - async function (done, _converse) { + mock.initConverse([], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current', 3); test_utils.openControlBox(_converse); @@ -461,9 +459,7 @@ })); it("shows an error message if the file is too large", - mock.initConverse( - ['emojisInitialized'], {}, - async function (done, _converse) { + mock.initConverse([], {}, async function (done, _converse) { const IQ_stanzas = _converse.connection.IQ_stanzas; const IQ_ids = _converse.connection.IQ_ids; diff --git a/spec/messages.js b/spec/messages.js index 7686e463b..f17e6a44f 100644 --- a/spec/messages.js +++ b/spec/messages.js @@ -985,7 +985,7 @@ it("will render images from their URLs", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current'); @@ -1033,7 +1033,7 @@ it("will render the message time as configured", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current'); @@ -1059,7 +1059,7 @@ it("will be correctly identified and rendered as a followup message", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current'); @@ -1381,7 +1381,7 @@ it("will open a chatbox and be displayed inside it", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched'], {}, async function (done, _converse) { const include_nick = false; @@ -1427,7 +1427,7 @@ it("will be trimmed of leading and trailing whitespace", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current', 1, false); @@ -1519,7 +1519,7 @@ it("the VCard for that user is fetched and the chatbox updated with the results", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {'allow_non_roster_messaging': true}, + ['rosterGroupsFetched'], {'allow_non_roster_messaging': true}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current', 0); @@ -1635,7 +1635,7 @@ it("will have the error message displayed after itself", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current', 1); @@ -1847,7 +1847,7 @@ it("is ignored if it's intended for a different resource and filter_by_resource is set to true", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current'); @@ -2098,7 +2098,7 @@ it("is not sent when a markable message is received from someone not on the roster", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {'allow_non_roster_messaging': true}, + ['rosterGroupsFetched'], {'allow_non_roster_messaging': true}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current', 0); diff --git a/spec/muc.js b/spec/muc.js index 7ea12f353..db8caafeb 100644 --- a/spec/muc.js +++ b/spec/muc.js @@ -269,7 +269,7 @@ it("will be created when muc_instant_rooms is set to true", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { let IQ_stanzas = _converse.connection.IQ_stanzas; @@ -507,7 +507,7 @@ it("is opened when an xmpp: URI is clicked inside another groupchat", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched'], {}, async function (done, _converse) { await test_utils.waitForRoster(_converse, 'current'); @@ -535,7 +535,7 @@ it("shows a notification if it's not anonymous", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { const muc_jid = 'coven@chat.shakespeare.lit'; @@ -579,7 +579,7 @@ it("shows join/leave messages when users enter or exit a groupchat", mock.initConverse( - ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) { const muc_jid = 'coven@chat.shakespeare.lit'; @@ -1648,7 +1648,7 @@ it("shows users currently present in the groupchat", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched'], {}, async function (done, _converse) { await test_utils.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo'); @@ -2188,7 +2188,7 @@ it("escapes the subject before rendering it, to avoid JS-injection attacks", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched'], {}, async function (done, _converse) { await test_utils.openAndEnterChatRoom(_converse, 'jdev@conference.jabber.org', 'jc'); @@ -2711,7 +2711,7 @@ it("informs users if they have been kicked out of the groupchat", mock.initConverse( - ['rosterGroupsFetched', 'emojisInitialized'], {}, + ['rosterGroupsFetched'], {}, async function (done, _converse) { /* :-)':'1f606', - '\':-(':'1f613', - '>:-(':'1f620', - ':\'-(':'1f622', - 'O:-)':'1f607', - '0:-3':'1f607', - '0:-)':'1f607', - '0;^)':'1f607', - 'O;-)':'1f607', - '0;-)':'1f607', - 'O:-3':'1f607', - '-__-':'1f611', - ':-Þ':'1f61b', - ':)':'1f606', - '>;)':'1f606', - '>=)':'1f606', - ';-)':'1f609', - '*-)':'1f609', - ';-]':'1f609', - ';^)':'1f609', - '\':(':'1f613', - '\'=(':'1f613', - ':-*':'1f618', - ':^*':'1f618', - '>:P':'1f61c', - 'X-P':'1f61c', - '>:[':'1f61e', - ':-(':'1f61e', - ':-[':'1f61e', - '>:(':'1f620', - ':\'(':'1f622', - ';-(':'1f622', - '>.<':'1f623', - '#-)':'1f635', - '%-)':'1f635', - 'X-)':'1f635', - '\\0/':'1f646', - '\\O/':'1f646', - '0:3':'1f607', - '0:)':'1f607', - 'O:)':'1f607', - 'O=)':'1f607', - 'O:3':'1f607', - 'B-)':'1f60e', - '8-)':'1f60e', - 'B-D':'1f60e', - '8-D':'1f60e', - '-_-':'1f611', - '>:\\':'1f615', - '>:/':'1f615', - ':-/':'1f615', - ':-.':'1f615', - ':-P':'1f61b', - ':Þ':'1f61b', - ':-b':'1f61b', - ':-O':'1f62e', - 'O_O':'1f62e', - '>:O':'1f62e', - ':-X':'1f636', - ':-#':'1f636', - ':-)':'1f642', - '(y)':'1f44d', - '<3':'2764', - ':D':'1f603', - '=D':'1f603', - ';)':'1f609', - '*)':'1f609', - ';]':'1f609', - ';D':'1f609', - ':*':'1f618', - '=*':'1f618', - ':(':'1f61e', - ':[':'1f61e', - '=(':'1f61e', - ':@':'1f620', - ';(':'1f622', - 'D:':'1f628', - ':$':'1f633', - '=$':'1f633', - '#)':'1f635', - '%)':'1f635', - 'X)':'1f635', - 'B)':'1f60e', - '8)':'1f60e', - ':/':'1f615', - ':\\':'1f615', - '=/':'1f615', - '=\\':'1f615', - ':L':'1f615', - '=L':'1f615', - ':P':'1f61b', - '=P':'1f61b', - ':b':'1f61b', - ':O':'1f62e', - ':X':'1f636', - ':#':'1f636', - '=X':'1f636', - '=#':'1f636', - ':)':'1f642', - '=]':'1f642', - '=)':'1f642', - ':]':'1f642' + '*\\0/*':'1f646', '*\\O/*':'1f646', '-___-':'1f611', ':\'-)':'1f602', '\':-)':'1f605', '\':-D':'1f605', '>:-)':'1f606', '\':-(':'1f613', + '>:-(':'1f620', ':\'-(':'1f622', 'O:-)':'1f607', '0:-3':'1f607', '0:-)':'1f607', '0;^)':'1f607', 'O;-)':'1f607', '0;-)':'1f607', 'O:-3':'1f607', + '-__-':'1f611', ':-Þ':'1f61b', ':)':'1f606', '>;)':'1f606', '>=)':'1f606', ';-)':'1f609', '*-)':'1f609', ';-]':'1f609', ';^)':'1f609', '\':(':'1f613', '\'=(':'1f613', + ':-*':'1f618', ':^*':'1f618', '>:P':'1f61c', 'X-P':'1f61c', '>:[':'1f61e', ':-(':'1f61e', ':-[':'1f61e', '>:(':'1f620', ':\'(':'1f622', + ';-(':'1f622', '>.<':'1f623', '#-)':'1f635', '%-)':'1f635', 'X-)':'1f635', '\\0/':'1f646', '\\O/':'1f646', '0:3':'1f607', '0:)':'1f607', + 'O:)':'1f607', 'O=)':'1f607', 'O:3':'1f607', 'B-)':'1f60e', '8-)':'1f60e', 'B-D':'1f60e', '8-D':'1f60e', '-_-':'1f611', '>:\\':'1f615', + '>:/':'1f615', ':-/':'1f615', ':-.':'1f615', ':-P':'1f61b', ':Þ':'1f61b', ':-b':'1f61b', ':-O':'1f62e', 'O_O':'1f62e', '>:O':'1f62e', + ':-X':'1f636', ':-#':'1f636', ':-)':'1f642', '(y)':'1f44d', '<3':'2764', ':D':'1f603', '=D':'1f603', ';)':'1f609', '*)':'1f609', + ';]':'1f609', ';D':'1f609', ':*':'1f618', '=*':'1f618', ':(':'1f61e', ':[':'1f61e', '=(':'1f61e', ':@':'1f620', ';(':'1f622', 'D:':'1f628', + ':$':'1f633', '=$':'1f633', '#)':'1f635', '%)':'1f635', 'X)':'1f635', 'B)':'1f60e', '8)':'1f60e', ':/':'1f615', ':\\':'1f615', '=/':'1f615', + '=\\':'1f615', ':L':'1f615', '=L':'1f615', ':P':'1f61b', '=P':'1f61b', ':b':'1f61b', ':O':'1f62e', ':X':'1f636', ':#':'1f636', '=X':'1f636', + '=#':'1f636', ':)':'1f642', '=]':'1f642', '=)':'1f642', ':]':'1f642' }; @@ -202,6 +100,7 @@ converse.plugins.add('converse-emoji', { } }); + _converse.emojis = {}; _converse.api.promises.add('emojisInitialized', false); twemoji.default.base = _converse.emoji_image_path; @@ -220,8 +119,6 @@ converse.plugins.add('converse-emoji', { } }); - _converse.emojis = {}; - function getTonedEmojis () { if (!_converse.toned_emojis) { _converse.toned_emojis = _.uniq( @@ -366,26 +263,29 @@ converse.plugins.add('converse-emoji', { }); /************************ END Utils ************************/ - 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_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); + async function initializeEmojis () { + if (_converse.emojis.json) { + return; + } + 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_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); - const getShortNames = () => _converse.emoji_shortnames.map(s => s.replace(/[+]/g, "\\$&")).join('|'); - _converse.emojis.shortnames_regex = new RegExp("]*>.*?<\/object>|]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|("+getShortNames()+")", "gi"); + const getShortNames = () => _converse.emoji_shortnames.map(s => s.replace(/[+]/g, "\\$&")).join('|'); + _converse.emojis.shortnames_regex = new RegExp("]*>.*?<\/object>|]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|("+getShortNames()+")", "gi"); - - _converse.emojis.toned = getTonedEmojis(); - - /** - * Triggered once the JSON file representing emoji data has been - * fetched and its save to start calling emoji utility methods. - * @event _converse#emojisInitialized - */ - _converse.api.trigger('emojisInitialized'); + _converse.emojis.toned = getTonedEmojis(); + /** + * Triggered once the JSON file representing emoji data has been + * fetched and its save to start calling emoji utility methods. + * @event _converse#emojisInitialized + */ + _converse.api.trigger('emojisInitialized'); + } /************************ BEGIN Event Handlers ************************/ @@ -395,6 +295,13 @@ converse.plugins.add('converse-emoji', { delete _converse.emojipicker } }); + + _converse.api.listen.on('chatBoxesInitialized', () => { + _converse.chatboxes.on( + 'add', + chat => (chat.get('type') !== _converse.CONTROLBOX_TYPE) && initializeEmojis() + ); + }); /************************ END Event Handlers ************************/ } }); diff --git a/src/headless/utils/core.js b/src/headless/utils/core.js index 3919f18ab..f397a9248 100644 --- a/src/headless/utils/core.js +++ b/src/headless/utils/core.js @@ -384,7 +384,7 @@ u.getResolveablePromise = function () { wrapper.resolve = resolve; wrapper.reject = reject; }) - _.assign(promise, wrapper); + Object.assign(promise, wrapper); promise.then( function (v) { promise.isResolved = true; diff --git a/tests/mock.js b/tests/mock.js index 774a262fc..7d91154d3 100644 --- a/tests/mock.js +++ b/tests/mock.js @@ -227,8 +227,9 @@ db_request.onsuccess = function () { const db = db_request.result; const bare_jid = "romeo@montague.lit"; + let store; try { - const store= db.transaction([bare_jid], "readwrite").objectStore(bare_jid); + store= db.transaction([bare_jid], "readwrite").objectStore(bare_jid); } catch (e) { console.error(e); return promise.resolve();