From 9930937707f29eb8b3617f549adfe040680c8953 Mon Sep 17 00:00:00 2001 From: lilia Date: Wed, 18 Mar 2015 16:26:55 -0700 Subject: [PATCH] Move handlePushMessageContent to message model And retool message handling flow to helps us avoid instantiating duplicate message and conversation models. --- js/background.js | 105 ++------------------------------ js/models/messages.js | 88 ++++++++++++++++++++++++-- js/panel_controller.js | 13 ++-- js/views/message_detail_view.js | 2 + js/views/message_view.js | 2 +- 5 files changed, 98 insertions(+), 112 deletions(-) diff --git a/js/background.js b/js/background.js index e0bb7dd2..00d60f95 100644 --- a/js/background.js +++ b/js/background.js @@ -17,8 +17,6 @@ ;(function() { 'use strict'; var socket; - var conversations = new Whisper.ConversationCollection(); - var messages = new Whisper.MessageCollection(); if (!localStorage.getItem('first_install_ran')) { localStorage.setItem('first_install_ran', 1); @@ -80,13 +78,13 @@ var now = new Date().getTime(); var timestamp = pushMessage.timestamp.toNumber(); - var conversation = conversations.add({ + var conversation = getConversation({ id : pushMessage.source, type : 'private' - }, { merge : true } ); + }); conversation.fetch().always(function() { - var message = messages.add({ + var message = conversation.messageCollection.add({ source : pushMessage.source, sourceDevice : pushMessage.sourceDevice, relay : pushMessage.relay, @@ -105,7 +103,7 @@ return new Promise(function(resolve) { resolve(textsecure.protocol_wrapper.handleIncomingPushMessageProto(pushMessage).then( function(pushMessageContent) { - handlePushMessageContent(pushMessageContent, message); + message.handlePushMessageContent(pushMessageContent); } )); }).catch(function(e) { @@ -129,101 +127,6 @@ }); } - extension.on('message:decrypted', function(options) { - var message = messages.add({id: options.message_id}); - message.fetch().then(function() { - var pushMessageContent = handlePushMessageContent( - new textsecure.protobuf.PushMessageContent(options.data), - message - ); - }); - }); - - function getConversationId(pushMessageContent) { - if (pushMessageContent.sync) { - return pushMessageContent.sync.destination; - } else if (pushMessageContent.group) { - return pushMessageContent.group.id; - } - } - - function handlePushMessageContent(pushMessageContent, message) { - // This function can be called from the background script on an - // incoming message or from the frontend after the user accepts an - // identity key change. - var source = message.get('source'); - var timestamp = message.get('sent_at'); - return textsecure.processDecrypted(pushMessageContent, source).then(function(pushMessageContent) { - var type = 'incoming'; - if (pushMessageContent.sync) { - type = 'outgoing'; - timestamp = pushMessageContent.sync.timestamp; - } - var now = new Date().getTime(); - - var conversationId = getConversationId(pushMessageContent) || source; - var conversation = new Whisper.Conversation({id: conversationId}); - var attributes = {}; - conversation.fetch().always(function() { - if (pushMessageContent.group) { - var group_update = {}; - if (pushMessageContent.group.type === textsecure.protobuf.PushMessageContent.GroupContext.Type.UPDATE) { - attributes = { - type : 'group', - groupId : pushMessageContent.group.id, - name : pushMessageContent.group.name, - avatar : pushMessageContent.group.avatar, - members : pushMessageContent.group.members, - }; - group_update = conversation.changedAttributes(_.pick(pushMessageContent.group, 'name', 'avatar')); - var difference = _.difference(pushMessageContent.group.members, conversation.get('members')); - if (difference.length > 0) { - group_update.joined = difference; - } - } - else if (pushMessageContent.group.type === textsecure.protobuf.PushMessageContent.GroupContext.Type.QUIT) { - group_update = { left: source }; - attributes = { members: _.without(conversation.get('members'), source) }; - } - - if (_.keys(group_update).length > 0) { - message.set({group_update: group_update}); - } - } - attributes.active_at = now; - if (type === 'incoming') { - attributes.unreadCount = conversation.get('unreadCount') + 1; - } - conversation.set(attributes); - - message.set({ - body : pushMessageContent.body, - conversationId : conversation.id, - attachments : pushMessageContent.attachments, - decrypted_at : now, - type : type, - sent_at : timestamp, - flags : pushMessageContent.flags, - errors : [] - }); - - if (message.get('sent_at') > conversation.get('timestamp')) { - conversation.set({ - timestamp: message.get('sent_at'), - lastMessage: message.get('body') - }); - } - - conversation.save().then(function() { - message.save().then(function() { - extension.trigger('message', message); // notify frontend listeners - notifyConversation(message); - }); - }); - }); - }); - } - function onDeliveryReceipt(pushMessage) { var timestamp = pushMessage.timestamp.toNumber(); var messages = new Whisper.MessageCollection(); diff --git a/js/models/messages.js b/js/models/messages.js index 5183082a..8a5a387d 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -77,10 +77,7 @@ var promise = new textsecure.ReplayableError(error).replay(); if (this.isIncoming()) { promise.then(function(pushMessageContent) { - extension.trigger('message:decrypted', { - message_id: this.id, - data: pushMessageContent - }); + this.handlePushMessageContent(pushMessageContent); this.save('errors', []); }.bind(this)).catch(function(e) { //this.save('errors', [_.pick(e, ['name', 'message'])]); @@ -98,7 +95,90 @@ }.bind(this)); } } + }, + handlePushMessageContent: function(pushMessageContent) { + // This function can be called from the background script on an + // incoming message or from the frontend after the user accepts an + // identity key change. + var message = this; + var source = message.get('source'); + var timestamp = message.get('sent_at'); + return textsecure.processDecrypted(pushMessageContent, source).then(function(pushMessageContent) { + var type = 'incoming'; + if (pushMessageContent.sync) { + type = 'outgoing'; + timestamp = pushMessageContent.sync.timestamp; + } + var now = new Date().getTime(); + + var conversationId = source; + if (pushMessageContent.sync) { + conversationId = pushMessageContent.sync.destination; + } else if (pushMessageContent.group) { + conversationId = pushMessageContent.group.id; + } + var conversation = new Whisper.Conversation({id: conversationId}); + var attributes = {}; + conversation.fetch().always(function() { + if (pushMessageContent.group) { + var group_update = {}; + if (pushMessageContent.group.type === textsecure.protobuf.PushMessageContent.GroupContext.Type.UPDATE) { + attributes = { + type : 'group', + groupId : pushMessageContent.group.id, + name : pushMessageContent.group.name, + avatar : pushMessageContent.group.avatar, + members : pushMessageContent.group.members, + }; + group_update = conversation.changedAttributes(_.pick(pushMessageContent.group, 'name', 'avatar')); + var difference = _.difference(pushMessageContent.group.members, conversation.get('members')); + if (difference.length > 0) { + group_update.joined = difference; + } + } + else if (pushMessageContent.group.type === textsecure.protobuf.PushMessageContent.GroupContext.Type.QUIT) { + group_update = { left: source }; + attributes = { members: _.without(conversation.get('members'), source) }; + } + + if (_.keys(group_update).length > 0) { + message.set({group_update: group_update}); + } + } + attributes.active_at = now; + if (type === 'incoming') { + attributes.unreadCount = conversation.get('unreadCount') + 1; + } + conversation.set(attributes); + + message.set({ + body : pushMessageContent.body, + conversationId : conversation.id, + attachments : pushMessageContent.attachments, + decrypted_at : now, + type : type, + sent_at : timestamp, + flags : pushMessageContent.flags, + errors : [] + }); + + if (message.get('sent_at') > conversation.get('timestamp')) { + conversation.set({ + timestamp: message.get('sent_at'), + lastMessage: message.get('body') + }); + } + + conversation.save().then(function() { + message.save().then(function() { + extension.trigger('message', message); // notify frontend listeners + notifyConversation(message); + }); + }); + }); + }); } + }); Whisper.MessageCollection = Backbone.Collection.extend({ diff --git a/js/panel_controller.js b/js/panel_controller.js index a908da67..2b732eb1 100644 --- a/js/panel_controller.js +++ b/js/panel_controller.js @@ -41,15 +41,15 @@ windowMap.remove('windowId', windowId); } - function getConversation(modelId) { - var conversation = window.inbox.get(modelId) || {id: modelId}; - conversation = conversations.add(conversation); + window.getConversation = function(attrs) { + var conversation = window.inbox.get(attrs.id) || attrs; + conversation = conversations.add(attrs); return conversation; - } + }; window.notifyConversation = function(message) { if (Whisper.Notifications.isEnabled()) { - var conversation = getConversation(message.get('conversationId')); + var conversation = getConversation({id: message.get('conversationId')}); conversation.fetch().then(function() { var notification = new Notification(conversation.getTitle(), { body: message.get('body'), @@ -60,13 +60,14 @@ openConversation(conversation.id); }; }); + conversation.fetchMessages(); } else { openConversation(message.get('conversationId')); } }; window.openConversation = function openConversation (modelId) { - var conversation = getConversation(modelId); + var conversation = getConversation({id: modelId}); conversation.fetch().then(function() { conversation.fetchContacts(); }); diff --git a/js/views/message_detail_view.js b/js/views/message_detail_view.js index 7a70643b..4fa92eec 100644 --- a/js/views/message_detail_view.js +++ b/js/views/message_detail_view.js @@ -46,6 +46,8 @@ initialize: function(options) { this.view = new Whisper.MessageView({model: this.model}); this.conversation = options.conversation; + + this.listenTo(this.model, 'change', this.render); }, events: { 'click .back': 'goBack', diff --git a/js/views/message_view.js b/js/views/message_view.js index 214248bc..7a8a073a 100644 --- a/js/views/message_view.js +++ b/js/views/message_view.js @@ -21,7 +21,7 @@ tagName: 'div', template: $('#message').html(), initialize: function() { - this.listenTo(this.model, 'change:body', this.render); + this.listenTo(this.model, 'change:body change:errors', this.render); this.listenTo(this.model, 'change:delivered', this.renderDelivered); }, className: function() {