Load attachment data for quotedMessages, processMessage on add
Not ideal that it loads it twice, but AttachmentView is so selfish with its blob and objectUrl!
This commit is contained in:
parent
d91f40177e
commit
32925ed026
3 changed files with 80 additions and 28 deletions
|
@ -111,6 +111,11 @@
|
||||||
return this.id === this.ourNumber;
|
return this.id === this.ourNumber;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addSingleMessage(message) {
|
||||||
|
this.messageCollection.add(message, { merge: true });
|
||||||
|
this.processQuotes(this.messageCollection);
|
||||||
|
},
|
||||||
|
|
||||||
onMessageError() {
|
onMessageError() {
|
||||||
this.updateVerified();
|
this.updateVerified();
|
||||||
},
|
},
|
||||||
|
@ -1030,11 +1035,13 @@
|
||||||
makeKey(author, id) {
|
makeKey(author, id) {
|
||||||
return `${author}-${id}`;
|
return `${author}-${id}`;
|
||||||
},
|
},
|
||||||
doMessagesMatch(left, right) {
|
doesMessageMatch(id, author, message) {
|
||||||
if (left.get('source') !== right.get('source')) {
|
const messageAuthor = message.getContact().id;
|
||||||
|
|
||||||
|
if (author !== messageAuthor) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (left.get('sent_at') !== right.get('sent_at')) {
|
if (id !== message.get('sent_at')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -1061,7 +1068,19 @@
|
||||||
makeMessagesLookup(messages) {
|
makeMessagesLookup(messages) {
|
||||||
return messages.reduce((acc, message) => {
|
return messages.reduce((acc, message) => {
|
||||||
const { source, sent_at: sentAt } = message.attributes;
|
const { source, sent_at: sentAt } = message.attributes;
|
||||||
const key = this.makeKey(source, sentAt);
|
|
||||||
|
// Checking for notification messages without a sender
|
||||||
|
if (!source && message.isIncoming()) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contact = message.getContact();
|
||||||
|
if (!contact) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const author = contact.id;
|
||||||
|
const key = this.makeKey(author, sentAt);
|
||||||
|
|
||||||
acc[key] = message;
|
acc[key] = message;
|
||||||
|
|
||||||
|
@ -1070,7 +1089,7 @@
|
||||||
},
|
},
|
||||||
async loadQuotedMessageFromDatabase(message) {
|
async loadQuotedMessageFromDatabase(message) {
|
||||||
const { quote } = message.attributes;
|
const { quote } = message.attributes;
|
||||||
const { attachments, id } = quote;
|
const { attachments, id, author } = quote;
|
||||||
const first = attachments[0];
|
const first = attachments[0];
|
||||||
|
|
||||||
// Maybe in the future we could try to pull the thumbnail from a video ourselves,
|
// Maybe in the future we could try to pull the thumbnail from a video ourselves,
|
||||||
|
@ -1081,7 +1100,7 @@
|
||||||
|
|
||||||
const collection = new Whisper.MessageCollection();
|
const collection = new Whisper.MessageCollection();
|
||||||
await collection.fetchSentAt(id);
|
await collection.fetchSentAt(id);
|
||||||
const queryMessage = collection.find(m => this.doMessagesMatch(message, m));
|
const queryMessage = collection.find(m => this.doesMessageMatch(id, author, m));
|
||||||
|
|
||||||
if (!queryMessage) {
|
if (!queryMessage) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1097,8 +1116,6 @@
|
||||||
|
|
||||||
// Note: it would be nice to take the full-size image and downsample it into
|
// Note: it would be nice to take the full-size image and downsample it into
|
||||||
// a true thumbnail here.
|
// a true thumbnail here.
|
||||||
// Note: if the attachment is a video, then this object URL won't make any sense
|
|
||||||
// when we try to use it in an img tag.
|
|
||||||
queryMessage.updateImageUrl();
|
queryMessage.updateImageUrl();
|
||||||
|
|
||||||
// We need to differentiate between messages we load from database and those already
|
// We need to differentiate between messages we load from database and those already
|
||||||
|
@ -1110,6 +1127,36 @@
|
||||||
this.forceRender(message);
|
this.forceRender(message);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
async loadQuotedMessage(message, quotedMessage) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
message.quotedMessage = quotedMessage;
|
||||||
|
|
||||||
|
const { quote } = message.attributes;
|
||||||
|
const { attachments } = quote;
|
||||||
|
const first = attachments[0];
|
||||||
|
|
||||||
|
// Maybe in the future we could try to pull thumbnails video ourselves,
|
||||||
|
// but for now we will rely on incoming thumbnails only.
|
||||||
|
console.log({ first, contentType: first ? first.contentType : null });
|
||||||
|
if (!first || !MIME.isImage(first.contentType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const quotedAttachments = quotedMessage.get('attachments') || [];
|
||||||
|
console.log({ quotedMessage, quotedAttachments });
|
||||||
|
if (quotedAttachments.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryFirst = quotedAttachments[0];
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
quotedMessage.attributes.attachments[0] = await loadAttachmentData(queryFirst);
|
||||||
|
|
||||||
|
// Note: it would be nice to take the full-size image and downsample it into
|
||||||
|
// a true thumbnail here.
|
||||||
|
quotedMessage.updateImageUrl();
|
||||||
|
console.log({ quotedMessage });
|
||||||
|
},
|
||||||
async loadQuoteThumbnail(message) {
|
async loadQuoteThumbnail(message) {
|
||||||
const { quote } = message.attributes;
|
const { quote } = message.attributes;
|
||||||
const { attachments } = quote;
|
const { attachments } = quote;
|
||||||
|
@ -1133,7 +1180,6 @@
|
||||||
this.forceRender(message);
|
this.forceRender(message);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
async processQuotes(messages) {
|
async processQuotes(messages) {
|
||||||
const lookup = this.makeMessagesLookup(messages);
|
const lookup = this.makeMessagesLookup(messages);
|
||||||
|
|
||||||
|
@ -1143,11 +1189,6 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { attachments } = quote;
|
|
||||||
if (!this.needData(attachments)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we already have a quoted message, then we exit early. If we don't have it,
|
// If we already have a quoted message, then we exit early. If we don't have it,
|
||||||
// then we'll continue to look again for an in-memory message to use. Why? This
|
// then we'll continue to look again for an in-memory message to use. Why? This
|
||||||
// will enable us to scroll to it when the user clicks.
|
// will enable us to scroll to it when the user clicks.
|
||||||
|
@ -1162,11 +1203,18 @@
|
||||||
|
|
||||||
if (quotedMessage) {
|
if (quotedMessage) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
message.quotedMessage = quotedMessage;
|
await this.loadQuotedMessage(message, quotedMessage);
|
||||||
this.forceRender(message);
|
this.forceRender(message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We only go further if we need more data for this message. It's always important
|
||||||
|
// to grab the quoted message to allow for navigating to it by clicking.
|
||||||
|
const { attachments } = quote;
|
||||||
|
if (!this.needData(attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// We've don't want to go to the database or load thumbnails a second time.
|
// We've don't want to go to the database or load thumbnails a second time.
|
||||||
if (message.quoteIsProcessed) {
|
if (message.quoteIsProcessed) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -530,11 +530,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
scrollToMessage: function(providedOptions) {
|
scrollToMessage: function(options = {}) {
|
||||||
const options = providedOptions || {};
|
|
||||||
const { id } = options;
|
const { id } = options;
|
||||||
|
|
||||||
if (id) {
|
if (!id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,7 +542,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
el.scrollIntoView();
|
el[0].scrollIntoView();
|
||||||
},
|
},
|
||||||
|
|
||||||
scrollToBottom: function() {
|
scrollToBottom: function() {
|
||||||
|
@ -686,7 +685,7 @@
|
||||||
// This is debounced, so it won't hit the database too often.
|
// This is debounced, so it won't hit the database too often.
|
||||||
this.lazyUpdateVerified();
|
this.lazyUpdateVerified();
|
||||||
|
|
||||||
this.model.messageCollection.add(message, {merge: true});
|
this.model.addSingleMessage(message);
|
||||||
message.setToExpire();
|
message.setToExpire();
|
||||||
|
|
||||||
if (message.isOutgoing()) {
|
if (message.isOutgoing()) {
|
||||||
|
|
|
@ -194,7 +194,7 @@
|
||||||
this.listenTo(this.model, 'change:delivered', this.renderDelivered);
|
this.listenTo(this.model, 'change:delivered', this.renderDelivered);
|
||||||
this.listenTo(this.model, 'change:read_by', this.renderRead);
|
this.listenTo(this.model, 'change:read_by', this.renderRead);
|
||||||
this.listenTo(this.model, 'change:expirationStartTimestamp', this.renderExpiring);
|
this.listenTo(this.model, 'change:expirationStartTimestamp', this.renderExpiring);
|
||||||
this.listenTo(this.model, 'change', this.renderSent);
|
this.listenTo(this.model, 'change', this.onChange);
|
||||||
this.listenTo(this.model, 'change:flags change:group_update', this.renderControl);
|
this.listenTo(this.model, 'change:flags change:group_update', this.renderControl);
|
||||||
this.listenTo(this.model, 'destroy', this.onDestroy);
|
this.listenTo(this.model, 'destroy', this.onDestroy);
|
||||||
this.listenTo(this.model, 'unload', this.onUnload);
|
this.listenTo(this.model, 'unload', this.onUnload);
|
||||||
|
@ -274,6 +274,10 @@
|
||||||
}
|
}
|
||||||
this.onUnload();
|
this.onUnload();
|
||||||
},
|
},
|
||||||
|
onChange() {
|
||||||
|
this.renderRead();
|
||||||
|
this.renderQuote();
|
||||||
|
},
|
||||||
select(e) {
|
select(e) {
|
||||||
this.$el.trigger('select', { message: this.model });
|
this.$el.trigger('select', { message: this.model });
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
@ -379,17 +383,19 @@
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
renderQuote() {
|
renderQuote() {
|
||||||
const VOICE_FLAG = textsecure.protobuf.AttachmentPointer.Flags.VOICE_MESSAGE;
|
|
||||||
const objectUrl = this.getQuoteObjectUrl();
|
|
||||||
const quote = this.model.get('quote');
|
const quote = this.model.get('quote');
|
||||||
if (!quote) {
|
if (!quote) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VOICE_FLAG = textsecure.protobuf.AttachmentPointer.Flags.VOICE_MESSAGE;
|
||||||
|
const objectUrl = this.getQuoteObjectUrl();
|
||||||
|
|
||||||
|
|
||||||
function processAttachment(attachment) {
|
function processAttachment(attachment) {
|
||||||
const thumbnail = !attachment.thumbnail
|
const thumbnail = !objectUrl
|
||||||
? null
|
? null
|
||||||
: Object.assign({}, attachment.thumbnail, {
|
: Object.assign({}, attachment.thumbnail || {}, {
|
||||||
objectUrl,
|
objectUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -411,7 +417,7 @@
|
||||||
const isIncoming = this.model.isIncoming();
|
const isIncoming = this.model.isIncoming();
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
attachments: quote.attachments && quote.attachments.map(processAttachment),
|
attachments: (quote.attachments || []).map(processAttachment),
|
||||||
authorColor,
|
authorColor,
|
||||||
authorProfileName,
|
authorProfileName,
|
||||||
authorTitle,
|
authorTitle,
|
||||||
|
@ -420,14 +426,13 @@
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
const { quotedMessage } = this.model;
|
const { quotedMessage } = this.model;
|
||||||
if (quotedMessage) {
|
if (quotedMessage) {
|
||||||
this.trigger('scroll-to-message', { id: quotedMessage.id });
|
this.model.trigger('scroll-to-message', { id: quotedMessage.id });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
text: quote.text,
|
text: quote.text,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.replyView) {
|
if (this.replyView) {
|
||||||
this.replyView.remove();
|
|
||||||
this.replyView = null;
|
this.replyView = null;
|
||||||
} else if (contact) {
|
} else if (contact) {
|
||||||
this.listenTo(contact, 'change:color', this.renderQuote);
|
this.listenTo(contact, 'change:color', this.renderQuote);
|
||||||
|
|
Loading…
Reference in a new issue