Commit e181aaf9 authored by JC Brand's avatar JC Brand

Make the message view's `render` method async

So that we first render dynamic content (e.g. images) before inserting
it into the chat.

Also, add the `show_images_inline` setting (which is the cause of this
whole change).

Updated tests to handle this new change and start using async/await
instead of promise callbacks.
parent 2426f9b7
This diff is collapsed.
......@@ -2252,9 +2252,7 @@
}
},
"backbone.browserStorage": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/backbone.browserStorage/-/backbone.browserStorage-0.0.3.tgz",
"integrity": "sha1-ikIi8I2bHQslLR14/1CUuNCKc2s=",
"version": "github:jcbrand/Backbone.browserStorage#7079bf7bf9a43474da1d48e31e3cda6c4a716382",
"dev": true,
"requires": {
"backbone": "1.3.3",
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -95,21 +95,18 @@
{ whitelisted_plugins: ['converse-roomslist'],
allow_bookmarks: false // Makes testing easier, otherwise we
// have to mock stanza traffic.
}, function (done, _converse) {
}, async function (done, _converse) {
let view;
const IQ_stanzas = _converse.connection.IQ_stanzas;
const room_jid = 'coven@chat.shakespeare.lit';
test_utils.openControlBox();
_converse.api.rooms.open(room_jid, {'nick': 'some1'})
.then(() => {
return test_utils.waitUntil(() => _.get(_.filter(
await _converse.api.rooms.open(room_jid, {'nick': 'some1'});
const last_stanza = await test_utils.waitUntil(() => _.get(_.filter(
IQ_stanzas,
iq => iq.nodeTree.querySelector(
`iq[to="${room_jid}"] query[xmlns="http://jabber.org/protocol/disco#info"]`
)).pop(), 'nodeTree'));
}).then(last_stanza => {
view = _converse.chatboxviews.get(room_jid);
const view = _converse.chatboxviews.get(room_jid);
const IQ_id = last_stanza.getAttribute('id');
const features_stanza = $iq({
'from': 'coven@chat.shakespeare.lit',
......@@ -139,9 +136,8 @@
.c('field', {'type':'text-single', 'var':'muc#roominfo_occupants', 'label':'Number of occupants'})
.c('value').t(0);
_converse.connection._dataRecv(test_utils.createRequest(features_stanza));
return test_utils.waitUntil(() => view.model.get('connection_status') === converse.ROOMSTATUS.CONNECTING)
}).then(function () {
var presence = $pres({
await test_utils.waitUntil(() => view.model.get('connection_status') === converse.ROOMSTATUS.CONNECTING)
let presence = $pres({
to: _converse.connection.jid,
from: 'coven@chat.shakespeare.lit/some1',
id: 'DC352437-C019-40EC-B590-AF29E879AF97'
......@@ -160,9 +156,7 @@
info_el.click();
const modal = view.model.room_details_modal;
return test_utils.waitUntil(() => u.isVisible(modal.el), 2000);
}).then(() => {
const modal = view.model.room_details_modal;
await test_utils.waitUntil(() => u.isVisible(modal.el), 2000);
let els = modal.el.querySelectorAll('p.room-info');
expect(els[0].textContent).toBe("Name: A Dark Cave")
expect(els[1].textContent).toBe("Groupchat address (JID): coven@chat.shakespeare.lit")
......@@ -177,7 +171,7 @@
'Not anonymous - All other groupchat participants can see your XMPP username'+
'Not moderated - Participants entering this groupchat can write right away'
);
const presence = $pres({
presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
from: 'coven@chat.shakespeare.lit/newguy'
})
......@@ -201,7 +195,6 @@
expect(els[4].textContent).toBe("Topic author: someone")
expect(els[5].textContent).toBe("Online users: 2")
done();
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}));
it("can be closed", mock.initConverseWithPromises(
......
This diff is collapsed.
......@@ -297,7 +297,7 @@
'message': _converse.chatboxes.getMessageBody(stanza),
'references': this.getReferencesFromStanza(stanza),
'older_versions': older_versions,
'edited': true
'edited': moment().format()
});
return true;
}
......@@ -395,7 +395,7 @@
older_versions.push(message.get('message'));
message.save({
'correcting': false,
'edited': true,
'edited': moment().format(),
'message': attrs.message,
'older_versions': older_versions,
'references': attrs.references
......
......@@ -17,8 +17,10 @@
const { Backbone, _ } = converse.env;
const AvatarMixin = {
renderAvatar () {
const canvas_el = this.el.querySelector('canvas');
renderAvatar (el) {
el = el || this.el;
const canvas_el = el.querySelector('canvas');
if (_.isNull(canvas_el)) {
return;
}
......@@ -27,6 +29,7 @@
img_src = "data:" + image_type + ";base64," + image,
img = new Image();
return new Promise((resolve, reject) => {
img.onload = () => {
const ctx = canvas_el.getContext('2d'),
ratio = img.width / img.height;
......@@ -38,8 +41,10 @@
} else {
ctx.drawImage(img, 0, 0, canvas_el.width, canvas_el.height*ratio);
}
resolve();
};
img.src = img_src;
});
},
};
......
......@@ -312,6 +312,7 @@
this.model.messages.on('add', this.onMessageAdded, this);
this.model.messages.on('rendered', this.scrollDown, this);
this.model.messages.on('edited', (view) => this.markFollowups(view.el));
this.model.on('show', this.show, this);
this.model.on('destroy', this.remove, this);
......@@ -673,7 +674,8 @@
if (view.model.get('type') === 'error') {
const previous_msg_el = this.content.querySelector(`[data-msgid="${view.model.get('msgid')}"]`);
if (previous_msg_el) {
return previous_msg_el.insertAdjacentElement('afterend', view.el);
previous_msg_el.insertAdjacentElement('afterend', view.el);
return this.trigger('messageInserted', view.el);
}
}
const current_msg_date = moment(view.model.get('time')) || moment,
......@@ -692,6 +694,7 @@
previous_msg_el.insertAdjacentElement('afterend', view.el);
this.markFollowups(view.el);
}
return this.trigger('messageInserted', view.el);
},
markFollowups (el) {
......@@ -731,7 +734,7 @@
}
},
showMessage (message) {
async showMessage (message) {
/* Inserts a chat message into the content area of the chat box.
*
* Will also insert a new day indicator if the message is on a
......@@ -741,6 +744,8 @@
* (Backbone.Model) message: The message object
*/
const view = new _converse.MessageView({'model': message});
await view.render();
this.clearChatStateNotification(message);
this.insertMessage(view);
this.insertDayIndicator(view.el);
......
......@@ -41,8 +41,11 @@
{ __ } = _converse;
_converse.MessageVersionsModal = _converse.BootstrapModal.extend({
_converse.api.settings.update({
'show_images_inline': true
});
_converse.MessageVersionsModal = _converse.BootstrapModal.extend({
toHTML () {
return tpl_message_versions_modal(_.extend(
this.model.toJSON(), {
......@@ -61,17 +64,11 @@
if (this.model.vcard) {
this.model.vcard.on('change', this.render, this);
}
this.model.on('change:correcting', this.onMessageCorrection, this);
this.model.on('change:message', this.render, this);
this.model.on('change:progress', this.renderFileUploadProgresBar, this);
this.model.on('change:type', this.render, this);
this.model.on('change:upload', this.render, this);
this.model.on('change', this.onChanged, this);
this.model.on('destroy', this.remove, this);
this.render();
},
render () {
const is_followup = u.hasClass('chat-msg--followup', this.el);
async render () {
let msg;
if (this.model.isOnlyChatStateNotification()) {
this.renderChatStateNotification()
......@@ -80,20 +77,32 @@
} else if (this.model.get('type') === 'error') {
this.renderErrorMessage();
} else {
this.renderChatMessage();
}
if (is_followup) {
u.addClass('chat-msg--followup', this.el);
await this.renderChatMessage();
}
return this.el;
},
onMessageCorrection () {
this.render();
if (!this.model.get('correcting') && this.model.changed.message) {
async onChanged (item) {
// Jot down whether it was edited because the `changed`
// attr gets removed when this.render() gets called further
// down.
const edited = item.changed.edited;
if (this.model.changed.progress) {
return this.renderFileUploadProgresBar();
}
if (_.filter(['correcting', 'message', 'type', 'upload'],
prop => Object.prototype.hasOwnProperty.call(this.model.changed, prop)).length) {
await this.render();
}
if (edited) {
this.onMessageEdited();
}
},
onMessageEdited () {
this.el.addEventListener('animationend', () => u.removeClass('onload', this.el));
this.model.collection.trigger('edited', this);
u.addClass('onload', this.el);
}
},
replaceElement (msg) {
......@@ -104,7 +113,7 @@
return this.el;
},
renderChatMessage () {
async renderChatMessage () {
const is_me_message = this.isMeCommand(),
moment_time = moment(this.model.get('time')),
role = this.model.vcard ? this.model.vcard.get('role') : null,
......@@ -148,14 +157,14 @@
_.partial(u.addEmoji, _converse, _)
)(text);
}
u.renderImageURLs(_converse, msg_content).then(() => {
this.model.collection.trigger('rendered');
});
this.replaceElement(msg);
if (_converse.show_images_inline) {
await u.renderImageURLs(_converse, msg_content);
}
if (this.model.get('type') !== 'headline') {
this.renderAvatar();
await this.renderAvatar(msg);
}
this.replaceElement(msg);
this.model.collection.trigger('rendered', this);
},
renderErrorMessage () {
......
......@@ -478,6 +478,7 @@
initialize () {
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
this.model.on('change', this.render, this);
this.model.occupants.on('add', this.render, this);
this.model.occupants.on('change', this.render, this);
},
......
......@@ -71,7 +71,7 @@
'warn': _.get(console, 'log') ? console.log.bind(console) : _.noop
}, console);
var isImage = function (url) {
const isImage = function (url) {
return new Promise((resolve, reject) => {
var img = new Image();
var timer = window.setTimeout(function () {
......
......@@ -297,13 +297,15 @@
.c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
}
utils.sendMessage = function (chatboxview, message) {
chatboxview.el.querySelector('.chat-textarea').value = message;
chatboxview.keyPressed({
target: chatboxview.el.querySelector('textarea.chat-textarea'),
utils.sendMessage = function (view, message) {
const promise = new Promise((resolve, reject) => view.on('messageInserted', resolve));
view.el.querySelector('.chat-textarea').value = message;
view.keyPressed({
target: view.el.querySelector('textarea.chat-textarea'),
preventDefault: _.noop,
keyCode: 13
});
return promise;
};
return utils;
}));
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