Skip to content

Commit

Permalink
New MessageController as the single place for in-memory messages
Browse files Browse the repository at this point in the history
  • Loading branch information
scottnonnenberg-signal committed Apr 5, 2019
1 parent 274949b commit 74cb808
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 105 deletions.
1 change: 1 addition & 0 deletions background.html
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ <h3>{{ message }}</h3>
<script type='text/javascript' src='js/registration.js'></script>
<script type='text/javascript' src='js/expire.js'></script>
<script type='text/javascript' src='js/conversation_controller.js'></script>
<script type='text/javascript' src='js/message_controller.js'></script>

<script type='text/javascript' src='js/views/react_wrapper_view.js'></script>
<script type='text/javascript' src='js/views/whisper_view.js'></script>
Expand Down
18 changes: 13 additions & 5 deletions js/delivery_receipts.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/* global Backbone: false */
/* global Whisper: false */
/* global ConversationController: false */
/* global _: false */
/* global
Backbone,
Whisper,
ConversationController,
MessageController,
_
*/

/* eslint-disable more/no-then */

Expand Down Expand Up @@ -45,10 +48,15 @@
const ids = groups.pluck('id');
ids.push(source);

return messages.find(
const target = messages.find(
item =>
!item.isIncoming() && _.contains(ids, item.get('conversationId'))
);
if (!target) {
return null;
}

return MessageController.register(target.id, target);
},
async onReceipt(receipt) {
try {
Expand Down
17 changes: 11 additions & 6 deletions js/expiring_messages.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/* global _: false */
/* global Backbone: false */
/* global i18n: false */
/* global moment: false */
/* global Whisper: false */
/* global
_,
Backbone,
i18n,
MessageController,
moment,
Whisper
*/

// eslint-disable-next-line func-names
(function() {
Expand All @@ -18,7 +21,9 @@
});

await Promise.all(
messages.map(async message => {
messages.map(async fromDB => {
const message = MessageController.register(fromDB.id, fromDB);

window.log.info('Message expired', {
sentAt: message.get('sent_at'),
});
Expand Down
65 changes: 65 additions & 0 deletions js/message_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// eslint-disable-next-line func-names
(function() {
'use strict';

window.Whisper = window.Whisper || {};

const messageLookup = Object.create(null);

const SECOND = 1000;
const MINUTE = SECOND * 60;
const FIVE_MINUTES = MINUTE * 5;
const HOUR = MINUTE * 60;

function register(id, message) {
const existing = messageLookup[id];
if (existing) {
messageLookup[id] = {
message: existing.message,
timestamp: Date.now(),
};
return existing.message;
}

messageLookup[id] = {
message,
timestamp: Date.now(),
};

return message;
}

function unregister(id) {
delete messageLookup[id];
}

function cleanup() {
const messages = Object.values(messageLookup);
const now = Date.now();

for (let i = 0, max = messages.length; i < max; i += 1) {
const { message, timestamp } = messages[i];
const conversation = message.getConversation();

if (
now - timestamp > FIVE_MINUTES &&
(!conversation || !conversation.messageCollection.length)
) {
delete messageLookup[message.id];
}
}
}

function _get() {
return messageLookup;
}

setInterval(cleanup, HOUR);

window.MessageController = {
register,
unregister,
cleanup,
_get,
};
})();
41 changes: 18 additions & 23 deletions js/models/conversations.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Backbone,
libphonenumber,
ConversationController,
MessageController,
libsignal,
storage,
textsecure,
Expand Down Expand Up @@ -673,14 +674,6 @@
},

async onReadMessage(message, readAt) {
const existing = this.messageCollection.get(message.id);
if (existing) {
const fetched = await window.Signal.Data.getMessageById(existing.id, {
Message: Whisper.Message,
});
existing.merge(fetched);
}

// We mark as read everything older than this message - to clean up old stuff
// still marked unread in the database. If the user generally doesn't read in
// the desktop app, so the desktop app only gets read syncs, we can very
Expand Down Expand Up @@ -883,10 +876,23 @@
recipients,
});

const message = this.addSingleMessage(messageWithSchema);
if (this.isPrivate()) {
messageWithSchema.destination = destination;
}
const attributes = {
...messageWithSchema,
id: window.getGuid(),
};

const model = this.addSingleMessage(attributes);
const message = MessageController.register(model.id, model);
await window.Signal.Data.saveMessage(message.attributes, {
forceSave: true,
Message: Whisper.Message,
});

this.set({
lastMessage: message.getNotificationText(),
lastMessage: model.getNotificationText(),
lastMessageStatus: 'sending',
active_at: now,
timestamp: now,
Expand All @@ -896,15 +902,6 @@
Conversation: Whisper.Conversation,
});

if (this.isPrivate()) {
message.set({ destination });
}

const id = await window.Signal.Data.saveMessage(message.attributes, {
Message: Whisper.Message,
});
message.set({ id });

// We're offline!
if (!textsecure.messaging) {
const errors = this.contactCollection.map(contact => {
Expand Down Expand Up @@ -1424,11 +1421,9 @@

let read = await Promise.all(
_.map(oldUnread, async providedM => {
let m = providedM;
const m = MessageController.register(providedM.id, providedM);

if (this.messageCollection.get(m.id)) {
m = this.messageCollection.get(m.id);
} else {
if (!this.messageCollection.get(m.id)) {
window.log.warn(
'Marked a message as read in the database, but ' +
'it was not in messageCollection.'
Expand Down
34 changes: 21 additions & 13 deletions js/models/messages.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
/* global _: false */
/* global Backbone: false */
/* global storage: false */
/* global filesize: false */
/* global ConversationController: false */
/* global getAccountManager: false */
/* global i18n: false */
/* global Signal: false */
/* global textsecure: false */
/* global Whisper: false */
/* global
_,
Backbone,
storage,
filesize,
ConversationController,
MessageController,
getAccountManager,
i18n,
Signal,
textsecure,
Whisper
*/

/* eslint-disable more/no-then */

Expand Down Expand Up @@ -261,6 +264,7 @@
this.cleanup();
},
async cleanup() {
MessageController.unregister(this.id);
this.unload();
await deleteExternalMessageFiles(this.attributes);
},
Expand Down Expand Up @@ -1394,17 +1398,18 @@
const collection = await window.Signal.Data.getMessagesBySentAt(id, {
MessageCollection: Whisper.MessageCollection,
});
const queryMessage = collection.find(item => {
const found = collection.find(item => {
const messageAuthor = item.getContact();

return messageAuthor && author === messageAuthor.id;
});

if (!queryMessage) {
if (!found) {
quote.referencedMessageNotFound = true;
return message;
}

const queryMessage = MessageController.register(found.id, found);
quote.text = queryMessage.get('body');
if (firstAttachment) {
firstAttachment.thumbnail = null;
Expand Down Expand Up @@ -1730,6 +1735,7 @@
Message: Whisper.Message,
});
message.set({ id });
MessageController.register(message.id, message);

// 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
Expand Down Expand Up @@ -1933,7 +1939,9 @@
}
);

const models = messages.filter(message => Boolean(message.id));
const models = messages
.filter(message => Boolean(message.id))
.map(message => MessageController.register(message.id, message));
const eliminated = messages.length - models.length;
if (eliminated > 0) {
window.log.warn(
Expand Down
52 changes: 7 additions & 45 deletions js/modules/attachment_downloads.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* global Whisper, Signal, setTimeout, clearTimeout */
/* global Whisper, Signal, setTimeout, clearTimeout, MessageController */

const { isFunction, isNumber, omit } = require('lodash');
const getGuid = require('uuid/v4');
Expand Down Expand Up @@ -37,7 +37,6 @@ let timeout;
let getMessageReceiver;
let logger;
const _activeAttachmentDownloadJobs = {};
const _messageCache = {};

async function start(options = {}) {
({ getMessageReceiver, logger } = options);
Expand Down Expand Up @@ -149,12 +148,15 @@ async function _runJob(job) {
);
}

message = await _getMessage(messageId);
if (!message) {
const found = await getMessageById(messageId, {
Message: Whisper.Message,
});
if (!found) {
logger.error('_runJob: Source message not found, deleting job');
await _finishJob(message, id);
await _finishJob(null, id);
return;
}
message = MessageController.register(found.id, found);

const pending = true;
await setAttachmentDownloadJobPending(id, pending);
Expand Down Expand Up @@ -232,46 +234,6 @@ async function _runJob(job) {
}
}

async function _getMessage(id) {
let item = _messageCache[id];
if (item) {
const fiveMinutesAgo = Date.now() - 5 * MINUTE;
if (item.timestamp >= fiveMinutesAgo) {
return item.message;
}

delete _messageCache[id];
}

let message = await getMessageById(id, {
Message: Whisper.Message,
});
if (!message) {
return message;
}

// Once more, checking for race conditions
item = _messageCache[id];
if (item) {
const fiveMinutesAgo = Date.now() - 5 * MINUTE;
if (item.timestamp >= fiveMinutesAgo) {
return item.message;
}
}

const conversation = message.getConversation();
if (conversation && conversation.messageCollection.get(id)) {
message = conversation.get(id);
}

_messageCache[id] = {
timestamp: Date.now(),
message,
};

return message;
}

async function _finishJob(message, id) {
if (message) {
await saveMessage(message.attributes, {
Expand Down
16 changes: 14 additions & 2 deletions js/read_receipts.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
/* global Whisper, Backbone, _, ConversationController, window */
/* global
Whisper,
Backbone,
_,
ConversationController,
MessageController,
window
*/

/* eslint-disable more/no-then */

Expand Down Expand Up @@ -46,9 +53,14 @@
const ids = groups.pluck('id');
ids.push(reader);

return messages.find(
const target = messages.find(
item => item.isOutgoing() && _.contains(ids, item.get('conversationId'))
);
if (!target) {
return null;
}

return MessageController.register(target.id, target);
},
async onReceipt(receipt) {
try {
Expand Down

0 comments on commit 74cb808

Please sign in to comment.