Commit 21a9ea73 authored by JC Brand's avatar JC Brand

converse-message-view: Add hooks before/after message text transformations

parent 502d2aa0
......@@ -565,6 +565,7 @@
expect(msg_obj.get('sender')).toEqual('them');
expect(msg_obj.get('is_delayed')).toEqual(false);
// Now check that the message appears inside the chatbox in the DOM
await new Promise(resolve => view.once('messageInserted', resolve));
const chat_content = view.el.querySelector('.chat-content');
expect(chat_content.querySelector('.chat-msg .chat-msg__text').textContent).toEqual(msgtext);
expect(chat_content.querySelector('.chat-msg__time').textContent.match(/^[0-9][0-9]:[0-9][0-9]/)).toBeTruthy();
......@@ -1603,6 +1604,7 @@
expect(view).toBeDefined();
expect(chatbox.get('fullname') === sender_jid);
await new Promise(resolve => view.once('messageInserted', resolve));
await u.waitUntil(() => view.el.querySelector('.chat-msg__author').textContent.trim() === 'Mercutio');
let author_el = view.el.querySelector('.chat-msg__author');
expect( _.includes(author_el.textContent.trim(), 'Mercutio')).toBeTruthy();
......@@ -3010,9 +3012,9 @@
`xmlns="jabber:client">`+
`<body>hello z3r0 gibson mr.robot, how are you?</body>`+
`<active xmlns="http://jabber.org/protocol/chatstates"/>`+
`<reference begin="18" end="26" type="mention" uri="xmpp:mr.robot@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
`<reference begin="11" end="17" type="mention" uri="xmpp:gibson@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
`<reference begin="6" end="10" type="mention" uri="xmpp:z3r0@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
`<reference begin="11" end="17" type="mention" uri="xmpp:gibson@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
`<reference begin="18" end="26" type="mention" uri="xmpp:mr.robot@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
`<origin-id id="${msg.nodeTree.querySelector('origin-id').getAttribute("id")}" xmlns="urn:xmpp:sid:0"/>`+
`</message>`);
......@@ -3087,9 +3089,9 @@
`xmlns="jabber:client">`+
`<body>hello z3r0 gibson mr.robot, how are you?</body>`+
`<active xmlns="http://jabber.org/protocol/chatstates"/>`+
`<reference begin="18" end="26" type="mention" uri="xmpp:mr.robot@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
`<reference begin="11" end="17" type="mention" uri="xmpp:gibson@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
`<reference begin="6" end="10" type="mention" uri="xmpp:z3r0@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
`<reference begin="11" end="17" type="mention" uri="xmpp:gibson@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
`<reference begin="18" end="26" type="mention" uri="xmpp:mr.robot@montague.lit" xmlns="urn:xmpp:reference:0"/>`+
`<origin-id id="${msg.nodeTree.querySelector('origin-id').getAttribute("id")}" xmlns="urn:xmpp:sid:0"/>`+
`</message>`);
done();
......
......@@ -32,8 +32,8 @@ const AvatarMixin = {
'width': avatar_el.getAttribute('width'),
'height': avatar_el.getAttribute('height'),
}
const image_type = this.model.vcard.get('image_type'),
image = this.model.vcard.get('image');
const image_type = this.model.vcard.get('image_type');
const image = this.model.vcard.get('image');
data['image'] = "data:" + image_type + ";base64," + image;
avatar_el.outerHTML = tpl_avatar(data);
}
......
......@@ -307,7 +307,6 @@ converse.plugins.add('converse-chatview', {
/**
* The View of an open/ongoing chat conversation.
*
* @class
* @namespace _converse.ChatBoxView
* @memberOf _converse
......
......@@ -80,6 +80,11 @@ converse.plugins.add('converse-message-view', {
});
/**
* @class
* @namespace _converse.MessageView
* @memberOf _converse
*/
_converse.MessageView = _converse.ViewWithAvatar.extend({
events: {
'click .chat-msg__edit-modal': 'showMessageVersionsModal',
......@@ -204,14 +209,33 @@ converse.plugins.add('converse-message-view', {
return u.renderImageURL(_converse, url);
},
transformBodyText (text) {
async transformBodyText (text) {
/**
* Synchronous event which provides a hook for transforming a chat message's body text
* before the default transformations have been applied.
* @event _converse#beforeMessageBodyTransformed
* @param { _converse.MessageView } view - The view representing the message
* @param { string } text - The message text
* @example _converse.api.listen.on('beforeMessageBodyTransformed', (view, text) => { ... });
*/
await _converse.api.trigger('beforeMessageBodyTransformed', this, text, {'Synchronous': true});
text = this.isMeCommand() ? text.substring(4) : text;
text = xss.filterXSS(text, {'whiteList': {}, 'onTag': onTagFoundDuringXSSFilter});
text = u.geoUriToHttp(text, _converse.geouri_replacement);
text = u.addMentionsMarkup(text, this.model.get('references'), this.model.collection.chatbox);
text = u.addHyperlinks(text);
text = u.renderNewLines(text);
return u.addEmoji(_converse, text);
text = u.addEmoji(_converse, text);
/**
* Synchronous event which provides a hook for transforming a chat message's body text
* after the default transformations have been applied.
* @event _converse#afterMessageBodyTransformed
* @param { _converse.MessageView } view - The view representing the message
* @param { string } text - The message text
* @example _converse.api.listen.on('afterMessageBodyTransformed', (view, text) => { ... });
*/
await _converse.api.trigger('afterMessageBodyTransformed', this, text, {'Synchronous': true});
return text;
},
async renderChatMessage () {
......@@ -244,13 +268,12 @@ converse.plugins.add('converse-message-view', {
const text = this.getMessageText();
const msg_content = msg.querySelector('.chat-msg__text');
if (text && text !== url) {
msg_content.innerHTML = this.transformBodyText(text);
msg_content.innerHTML = await this.transformBodyText(text);
await u.renderImageURLs(_converse, msg_content);
}
const promise = u.renderImageURLs(_converse, msg_content);
if (this.model.get('type') !== 'headline') {
this.renderAvatar(msg);
}
await promise;
this.replaceElement(msg);
if (this.model.collection) {
// If the model gets destroyed in the meantime, it no
......
......@@ -1463,16 +1463,18 @@ _converse.api = {
* {@link _converse.api.listen.on} or {@link _converse.api.listen.once}
* (see [_converse.api.listen](http://localhost:8000/docs/html/api/-_converse.api.listen.html)).
*
* Some events also double as promises and can be waited on via {@link _converse.api.waitUntil}.
*
* @method _converse.api.trigger
* @param {string} name - The event name
* @param {...any} [argument] - Argument to be passed to the event handler
* @param {object} [options]
* @param {boolean} [options.synchronous] - Whether the event is synchronous or not.
* When a synchronous event is fired, Converse will wait for all
* promises returned by the event's handlers to finish before continuing.
* When a synchronous event is fired, a promise will be returned
* by {@link _converse.api.trigger} which resolves once all the
* event handlers' promises have been resolved.
*/
async trigger (name) {
/* Event emitter and promise resolver */
const args = Array.from(arguments);
const options = args.pop();
if (options && options.synchronous) {
......
......@@ -161,33 +161,29 @@ u.renderImageURL = function (_converse, url) {
};
/**
* Returns a Promise which resolves once all images have been loaded.
* @returns {Promise}
*/
u.renderImageURLs = function (_converse, el) {
/* Returns a Promise which resolves once all images have been loaded.
*/
if (!_converse.show_images_inline) {
return Promise.resolve();
}
const { __ } = _converse;
const list = el.textContent.match(URL_REGEX) || [];
return Promise.all(
_.map(list, url =>
list.map(url =>
new Promise((resolve, reject) => {
if (u.isImageURL(url)) {
return isImage(url).then(img => {
const i = new Image();
i.src = img.src;
i.addEventListener('load', resolve);
// We also resolve for non-images, otherwise the
// Promise.all resolves prematurely.
// We also resolve (instead of reject) for non-images,
// otherwise the Promise.all resolves prematurely.
i.addEventListener('error', resolve);
const { __ } = _converse;
_.each(sizzle(`a[href="${url}"]`, el), (a) => {
a.outerHTML= tpl_image({
'url': url,
'label_download': __('Download')
})
});
sizzle(`a[href="${url}"]`, el)
.forEach(a => (a.outerHTML = tpl_image({url, 'label_download': __('Download')})));
}).catch(resolve)
} else {
return resolve();
......
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