Improve message download performance

This commit is contained in:
Scott Nonnenberg 2019-09-26 12:56:31 -07:00
parent 957f6f6474
commit 0c09f9620f
32 changed files with 906 additions and 633 deletions

View file

@ -101,14 +101,14 @@
this.messageCollection.on('send-error', this.onMessageError, this);
this.throttledBumpTyping = _.throttle(this.bumpTyping, 300);
const debouncedUpdateLastMessage = _.debounce(
this.debouncedUpdateLastMessage = _.debounce(
this.updateLastMessage.bind(this),
200
);
this.listenTo(
this.messageCollection,
'add remove destroy',
debouncedUpdateLastMessage
this.debouncedUpdateLastMessage
);
this.listenTo(this.messageCollection, 'sent', this.updateLastMessage);
this.listenTo(
@ -268,7 +268,7 @@
},
async updateAndMerge(message) {
this.updateLastMessage();
this.debouncedUpdateLastMessage();
const mergeMessage = () => {
const existing = this.messageCollection.get(message.id);
@ -284,7 +284,7 @@
},
async onExpired(message) {
this.updateLastMessage();
this.debouncedUpdateLastMessage();
const removeMessage = () => {
const { id } = message;
@ -317,7 +317,7 @@
: `${message.source}.${message.sourceDevice}`;
this.clearContactTypingTimer(identifier);
await this.updateLastMessage();
this.debouncedUpdateLastMessage();
},
addSingleMessage(message) {
@ -411,11 +411,7 @@
if (this.get('verified') !== verified) {
this.set({ verified });
// we don't await here because we don't need to wait for this to finish
window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
window.Signal.Data.updateConversation(this.id, this.attributes);
}
return;
@ -479,9 +475,7 @@
}
this.set({ verified });
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
window.Signal.Data.updateConversation(this.id, this.attributes);
// Three situations result in a verification notice in the conversation:
// 1) The message came from an explicit verification in another client (not
@ -1014,9 +1008,7 @@
draft: null,
draftTimestamp: null,
});
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
window.Signal.Data.updateConversation(this.id, this.attributes);
// We're offline!
if (!textsecure.messaging) {
@ -1143,10 +1135,9 @@
conversation.set({
sealedSender: SEALED_SENDER.DISABLED,
});
await window.Signal.Data.updateConversation(
window.Signal.Data.updateConversation(
conversation.id,
conversation.attributes,
{ Conversation: Whisper.Conversation }
conversation.attributes
);
}
})
@ -1175,10 +1166,9 @@
sealedSender: SEALED_SENDER.UNRESTRICTED,
});
}
await window.Signal.Data.updateConversation(
window.Signal.Data.updateConversation(
conversation.id,
conversation.attributes,
{ Conversation: Whisper.Conversation }
conversation.attributes
);
}
})
@ -1299,7 +1289,7 @@
this.set(lastMessageUpdate);
if (this.hasChanged()) {
await window.Signal.Data.updateConversation(this.id, this.attributes, {
window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
}
@ -1307,9 +1297,7 @@
async setArchived(isArchived) {
this.set({ isArchived });
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
window.Signal.Data.updateConversation(this.id, this.attributes);
},
async updateExpirationTimer(
@ -1346,9 +1334,7 @@
const timestamp = (receivedAt || Date.now()) - 1;
this.set({ expireTimer });
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
window.Signal.Data.updateConversation(this.id, this.attributes);
const model = new Whisper.Message({
// Even though this isn't reflected to the user, we want to place the last seen
@ -1516,9 +1502,7 @@
if (this.get('type') === 'group') {
const groupNumbers = this.getRecipients();
this.set({ left: true });
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
window.Signal.Data.updateConversation(this.id, this.attributes);
const model = new Whisper.Message({
group_update: { left: 'You' },
@ -1565,14 +1549,9 @@
_.map(oldUnread, async providedM => {
const m = MessageController.register(providedM.id, providedM);
if (!this.messageCollection.get(m.id)) {
window.log.warn(
'Marked a message as read in the database, but ' +
'it was not in messageCollection.'
);
}
// Note that this will update the message in the database
await m.markRead(options.readAt);
const errors = m.get('errors');
return {
sender: m.get('source'),
@ -1588,9 +1567,7 @@
const unreadCount = unreadMessages.length - read.length;
this.set({ unreadCount });
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
window.Signal.Data.updateConversation(this.id, this.attributes);
// If a message has errors, we don't want to send anything out about it.
// read syncs - let's wait for a client that really understands the message
@ -1783,9 +1760,7 @@
}
if (c.hasChanged()) {
await window.Signal.Data.updateConversation(id, c.attributes, {
Conversation: Whisper.Conversation,
});
window.Signal.Data.updateConversation(id, c.attributes);
}
},
async setProfileName(encryptedName) {
@ -1860,7 +1835,7 @@
await this.deriveAccessKeyIfNeeded();
await window.Signal.Data.updateConversation(this.id, this.attributes, {
window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
}
@ -1883,9 +1858,7 @@
sealedSender: SEALED_SENDER.UNKNOWN,
});
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
window.Signal.Data.updateConversation(this.id, this.attributes);
}
},
@ -1944,9 +1917,7 @@
timestamp: null,
active_at: null,
});
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
window.Signal.Data.updateConversation(this.id, this.attributes);
await window.Signal.Data.removeAllMessagesInConversation(this.id, {
MessageCollection: Whisper.MessageCollection,

View file

@ -1004,7 +1004,9 @@
hasErrors() {
return _.size(this.get('errors')) > 0;
},
async saveErrors(providedErrors) {
async saveErrors(providedErrors, options = {}) {
const { skipSave } = options;
let errors = providedErrors;
if (!(errors instanceof Array)) {
@ -1030,11 +1032,16 @@
errors = errors.concat(this.get('errors') || []);
this.set({ errors });
await window.Signal.Data.saveMessage(this.attributes, {
Message: Whisper.Message,
});
if (!skipSave) {
await window.Signal.Data.saveMessage(this.attributes, {
Message: Whisper.Message,
});
}
},
async markRead(readAt) {
async markRead(readAt, options = {}) {
const { skipSave } = options;
this.unset('unread');
if (this.get('expireTimer') && !this.get('expirationStartTimestamp')) {
@ -1051,9 +1058,11 @@
})
);
await window.Signal.Data.saveMessage(this.attributes, {
Message: Whisper.Message,
});
if (!skipSave) {
await window.Signal.Data.saveMessage(this.attributes, {
Message: Whisper.Message,
});
}
},
isExpiring() {
return this.get('expireTimer') && this.get('expirationStartTimestamp');
@ -1074,7 +1083,9 @@
}
return msFromNow;
},
async setToExpire(force = false) {
async setToExpire(force = false, options = {}) {
const { skipSave } = options;
if (this.isExpiring() && (force || !this.get('expires_at'))) {
const start = this.get('expirationStartTimestamp');
const delta = this.get('expireTimer') * 1000;
@ -1082,7 +1093,7 @@
this.set({ expires_at: expiresAt });
const id = this.get('id');
if (id) {
if (id && !skipSave) {
await window.Signal.Data.saveMessage(this.attributes, {
Message: Whisper.Message,
});
@ -1664,10 +1675,6 @@
sticker,
});
await window.Signal.Data.saveMessage(this.attributes, {
Message: Whisper.Message,
});
return true;
}
@ -1880,6 +1887,7 @@
}
message.set({
id: window.getGuid(),
attachments: dataMessage.attachments,
body: dataMessage.body,
contact: dataMessage.contact,
@ -2024,8 +2032,8 @@
!conversationTimestamp ||
message.get('sent_at') > conversationTimestamp
) {
conversation.lastMessage = message.getNotificationText();
conversation.set({
lastMessage: message.getNotificationText(),
timestamp: message.get('sent_at'),
});
}
@ -2045,12 +2053,6 @@
}
}
const id = await window.Signal.Data.saveMessage(message.attributes, {
Message: Whisper.Message,
});
message.set({ id });
MessageController.register(message.id, message);
if (message.isTapToView() && type === 'outgoing') {
await message.eraseContents();
}
@ -2076,56 +2078,24 @@
}
}
if (message.isUnsupportedMessage()) {
await message.eraseContents();
} else {
// Note that this can save the message again, if jobs were queued. We need to
// call it after we have an id for this message, because the jobs refer back
// to their source message.
await message.queueAttachmentDownloads();
}
await window.Signal.Data.updateConversation(
MessageController.register(message.id, message);
window.Signal.Data.updateConversation(
conversationId,
conversation.attributes,
{ Conversation: Whisper.Conversation }
conversation.attributes
);
conversation.trigger('newmessage', message);
try {
// We go to the database here because, between the message save above and
// the previous line's trigger() call, we might have marked all messages
// unread in the database. This message might already be read!
const fetched = await window.Signal.Data.getMessageById(
message.get('id'),
{
Message: Whisper.Message,
}
);
const previousUnread = message.get('unread');
// Important to update message with latest read state from database
message.merge(fetched);
if (previousUnread !== message.get('unread')) {
window.log.warn(
'Caught race condition on new message read state! ' +
'Manually starting timers.'
);
// We call markRead() even though the message is already
// marked read because we need to start expiration
// timers, etc.
message.markRead();
}
} catch (error) {
window.log.warn(
'handleDataMessage: Message',
message.idForLogging(),
'was deleted'
);
if (message.isUnsupportedMessage()) {
await message.eraseContents();
}
await message.queueAttachmentDownloads();
await window.Signal.Data.saveMessage(message.attributes, {
Message: Whisper.Message,
forceSave: true,
});
conversation.trigger('newmessage', message);
if (message.get('unread')) {
await conversation.notify(message);
}