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 source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
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,19 +29,22 @@
img_src = "data:" + image_type + ";base64," + image,
img = new Image();
img.onload = () => {
const ctx = canvas_el.getContext('2d'),
ratio = img.width / img.height;
ctx.clearRect(0, 0, canvas_el.width, canvas_el.height);
if (ratio < 1) {
const scaled_img_with = canvas_el.width*ratio,
x = Math.floor((canvas_el.width-scaled_img_with)/2);
ctx.drawImage(img, x, 0, scaled_img_with, canvas_el.height);
} else {
ctx.drawImage(img, 0, 0, canvas_el.width, canvas_el.height*ratio);
}
};
img.src = img_src;
return new Promise((resolve, reject) => {
img.onload = () => {
const ctx = canvas_el.getContext('2d'),
ratio = img.width / img.height;
ctx.clearRect(0, 0, canvas_el.width, canvas_el.height);
if (ratio < 1) {
const scaled_img_with = canvas_el.width*ratio,
x = Math.floor((canvas_el.width-scaled_img_with)/2);
ctx.drawImage(img, x, 0, scaled_img_with, canvas_el.height);
} 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,22 +77,34 @@
} 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) {
this.el.addEventListener('animationend', () => u.removeClass('onload', this.el));
u.addClass('onload', this.el);
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) {
if (!_.isNil(this.el.parentElement)) {
this.el.parentElement.replaceChild(msg, this.el);
......@@ -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