Serialize sending and adding messages to a convo

Previously, if a message was sent in between the receive time of an
incoming message and the time it is actually added to the conversation's
message collection (which only occurs later after several async
callbacks), the incoming message would be inserted not-at-the-end of the
collection since it is ordered by receive time. This tricked the front
end into assuming the message was an older message instead of a new one.

Fixes #490

// FREEBIE
This commit is contained in:
lilia 2016-03-15 13:09:06 -07:00
parent f9a3c7817e
commit 762cb68721
2 changed files with 128 additions and 108 deletions

View file

@ -112,36 +112,51 @@
this.set({tokens: tokens});
},
queueJob: function(callback) {
var previous = this.pending || Promise.resolve();
var current = this.pending = previous.then(callback, callback);
current.then(function() {
if (this.pending === current) {
delete this.pending;
}
}.bind(this));
return current;
},
sendMessage: function(body, attachments) {
var now = Date.now();
var message = this.messageCollection.add({
body : body,
conversationId : this.id,
type : 'outgoing',
attachments : attachments,
sent_at : now,
received_at : now
});
if (this.isPrivate()) {
message.set({destination: this.id});
}
message.save();
this.queueJob(function() {
var now = Date.now();
var message = this.messageCollection.add({
body : body,
conversationId : this.id,
type : 'outgoing',
attachments : attachments,
sent_at : now,
received_at : now
});
if (this.isPrivate()) {
message.set({destination: this.id});
}
message.save();
this.save({
unreadCount : 0,
active_at : now,
timestamp : now,
lastMessage : message.getNotificationText()
});
this.save({
unreadCount : 0,
active_at : now,
timestamp : now,
lastMessage : message.getNotificationText()
});
var sendFunc;
if (this.get('type') == 'private') {
sendFunc = textsecure.messaging.sendMessageToNumber;
}
else {
sendFunc = textsecure.messaging.sendMessageToGroup;
}
message.send(sendFunc(this.get('id'), body, attachments, now));
var sendFunc;
if (this.get('type') == 'private') {
sendFunc = textsecure.messaging.sendMessageToNumber;
}
else {
sendFunc = textsecure.messaging.sendMessageToGroup;
}
message.send(sendFunc(this.get('id'), body, attachments, now));
}.bind(this));
},
isSearchable: function() {

View file

@ -267,92 +267,97 @@
conversationId = dataMessage.group.id;
}
var conversation = ConversationController.create({id: conversationId});
conversation.fetch().always(function() {
var now = new Date().getTime();
var attributes = { type: 'private' };
if (dataMessage.group) {
var group_update = null;
attributes = {
type: 'group',
groupId: dataMessage.group.id,
};
if (dataMessage.group.type === textsecure.protobuf.GroupContext.Type.UPDATE) {
attributes = {
type : 'group',
groupId : dataMessage.group.id,
name : dataMessage.group.name,
avatar : dataMessage.group.avatar,
members : dataMessage.group.members,
};
group_update = conversation.changedAttributes(_.pick(dataMessage.group, 'name', 'avatar')) || {};
var difference = _.difference(dataMessage.group.members, conversation.get('members'));
if (difference.length > 0) {
group_update.joined = difference;
}
}
else if (dataMessage.group.type === textsecure.protobuf.GroupContext.Type.QUIT) {
if (source == textsecure.storage.user.getNumber()) {
group_update = { left: "You" };
} else {
group_update = { left: source };
}
attributes.members = _.without(conversation.get('members'), source);
}
conversation.queueJob(function() {
return new Promise(function(resolve) {
conversation.fetch().always(function() {
var now = new Date().getTime();
var attributes = { type: 'private' };
if (dataMessage.group) {
var group_update = null;
attributes = {
type: 'group',
groupId: dataMessage.group.id,
};
if (dataMessage.group.type === textsecure.protobuf.GroupContext.Type.UPDATE) {
attributes = {
type : 'group',
groupId : dataMessage.group.id,
name : dataMessage.group.name,
avatar : dataMessage.group.avatar,
members : dataMessage.group.members,
};
group_update = conversation.changedAttributes(_.pick(dataMessage.group, 'name', 'avatar')) || {};
var difference = _.difference(dataMessage.group.members, conversation.get('members'));
if (difference.length > 0) {
group_update.joined = difference;
}
}
else if (dataMessage.group.type === textsecure.protobuf.GroupContext.Type.QUIT) {
if (source == textsecure.storage.user.getNumber()) {
group_update = { left: "You" };
} else {
group_update = { left: source };
}
attributes.members = _.without(conversation.get('members'), source);
}
if (group_update !== null) {
message.set({group_update: group_update});
}
}
if (type === 'outgoing') {
// lazy hack - check for receipts that arrived early.
var recipients;
if (dataMessage.group && dataMessage.group.id) { // group sync
recipients = conversation.get('members') || [];
} else {
recipients = [ conversation.id ];
}
window.receipts.filter(function(receipt) {
return (receipt.get('timestamp') === timestamp) &&
(recipients.indexOf(receipt.get('source')) > -1);
}).forEach(function(receipt) {
window.receipts.remove(receipt);
message.set({
delivered: (message.get('delivered') || 0) + 1
});
});
}
attributes.active_at = now;
if (type === 'incoming') {
attributes.unreadCount = conversation.get('unreadCount') + 1;
}
conversation.set(attributes);
if (group_update !== null) {
message.set({group_update: group_update});
}
}
if (type === 'outgoing') {
// lazy hack - check for receipts that arrived early.
var recipients;
if (dataMessage.group && dataMessage.group.id) { // group sync
recipients = conversation.get('members') || [];
} else {
recipients = [ conversation.id ];
}
window.receipts.filter(function(receipt) {
return (receipt.get('timestamp') === timestamp) &&
(recipients.indexOf(receipt.get('source')) > -1);
}).forEach(function(receipt) {
window.receipts.remove(receipt);
message.set({
delivered: (message.get('delivered') || 0) + 1
body : dataMessage.body,
conversationId : conversation.id,
attachments : dataMessage.attachments,
decrypted_at : now,
flags : dataMessage.flags,
errors : []
});
});
}
attributes.active_at = now;
if (type === 'incoming') {
attributes.unreadCount = conversation.get('unreadCount') + 1;
}
conversation.set(attributes);
message.set({
body : dataMessage.body,
conversationId : conversation.id,
attachments : dataMessage.attachments,
decrypted_at : now,
flags : dataMessage.flags,
errors : []
});
var conversation_timestamp = conversation.get('timestamp');
if (!conversation_timestamp || message.get('sent_at') > conversation_timestamp) {
conversation.set({
timestamp: message.get('sent_at'),
lastMessage: message.getNotificationText()
});
}
else if (!conversation.get('lastMessage')) {
conversation.set({
lastMessage: message.getNotificationText()
});
}
var conversation_timestamp = conversation.get('timestamp');
if (!conversation_timestamp || message.get('sent_at') > conversation_timestamp) {
conversation.set({
timestamp: message.get('sent_at'),
lastMessage: message.getNotificationText()
});
}
else if (!conversation.get('lastMessage')) {
conversation.set({
lastMessage: message.getNotificationText()
});
}
message.save().then(function() {
conversation.save().then(function() {
conversation.trigger('newmessage', message);
conversation.notify(message);
message.save().then(function() {
conversation.save().then(function() {
conversation.trigger('newmessage', message);
conversation.notify(message);
resolve();
});
});
});
});
});