From e4b9c51f88f222846a7d0886368322e1da4d0819 Mon Sep 17 00:00:00 2001 From: lilia Date: Tue, 21 Feb 2017 15:32:40 -0800 Subject: [PATCH] Rework expiring messages management // FREEBIE --- js/background.js | 1 + js/database.js | 10 +++++++++ js/expiring_messages.js | 40 +++++++++++++++++++++++++++++------ js/models/messages.js | 32 +++++++++++++++------------- js/views/conversation_view.js | 1 + js/views/message_view.js | 3 +++ 6 files changed, 66 insertions(+), 21 deletions(-) diff --git a/js/background.js b/js/background.js index 5479265a..da3ca449 100644 --- a/js/background.js +++ b/js/background.js @@ -86,6 +86,7 @@ } RotateSignedPreKeyListener.init(); + ExpiringMessagesListener.update(); }); window.getSyncRequest = function() { diff --git a/js/database.js b/js/database.js index 700069b9..ff4c3af3 100644 --- a/js/database.js +++ b/js/database.js @@ -189,6 +189,16 @@ messages.createIndex('expire', 'expireTimer', { unique: false }); next(); } + }, + { + version: "11.0", + migrate: function(transaction, next) { + console.log('migration 11.0'); + console.log('creating expires_at message index'); + var messages = transaction.objectStore('messages'); + messages.createIndex('expires_at', 'expires_at', { unique: false }); + next(); + } } ]; }()); diff --git a/js/expiring_messages.js b/js/expiring_messages.js index 4989ee12..096d0dfb 100644 --- a/js/expiring_messages.js +++ b/js/expiring_messages.js @@ -5,12 +5,40 @@ ;(function() { 'use strict'; window.Whisper = window.Whisper || {}; - Whisper.ExpiringMessages = new (Whisper.MessageCollection.extend({ - initialize: function() { - this.on('expired', this.remove); - this.fetchExpiring(); - } - }))(); + + function destroyExpiredMessages() { + // Load messages that have expired and destroy them + var expired = new Whisper.MessageCollection(); + expired.on('add', function(message) { + console.log('message', message.get('sent_at'), 'expired'); + message.destroy(); + message.getConversation().trigger('expired', message); + }); + expired.on('reset', checkExpiringMessages); + + expired.fetchExpired(); + } + + var timeout; + function checkExpiringMessages() { + // Look up the next expiring message and set a timer to destroy it + var expiring = new Whisper.MessageCollection(); + expiring.once('add', function(next) { + var expires_at = next.get('expires_at'); + console.log('next message expires', new Date(expires_at)); + + var wait = expires_at - Date.now(); + if (wait < 0) { wait = 0; } + + clearTimeout(timeout); + timeout = setTimeout(destroyExpiredMessages, wait); + }); + expiring.fetchNextExpiring(); + } + + window.ExpiringMessagesListener = { + update: checkExpiringMessages + }; var TimerOption = Backbone.Model.extend({ getName: function() { diff --git a/js/models/messages.js b/js/models/messages.js index 6b0e8216..431fa4a7 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -459,15 +459,6 @@ })); return this.save(); }, - markExpired: function() { - console.log('message', this.get('sent_at'), 'expired'); - clearInterval(this.expirationTimeout); - this.expirationTimeout = null; - this.trigger('expired', this); - this.destroy(); - - this.getConversation().trigger('expired', this); - }, isExpiring: function() { return this.get('expireTimer') && this.get('expirationStartTimestamp'); }, @@ -485,10 +476,13 @@ return ms_from_now; }, setToExpire: function() { - if (this.isExpiring() && !this.expirationTimeout) { - var ms_from_now = this.msTilExpire(); - console.log('message', this.get('sent_at'), 'expires in', ms_from_now, 'ms'); - this.expirationTimeout = setTimeout(this.markExpired.bind(this), ms_from_now); + if (this.isExpiring() && !this.get('expires_at')) { + var start = this.get('expirationStartTimestamp'); + var delta = this.get('expireTimer') * 1000; + var expires_at = start + delta; + this.save('expires_at', expires_at); + ExpiringMessagesListener.update(); + console.log('message', this.get('sent_at'), 'expires at', expires_at); } } @@ -551,8 +545,16 @@ }.bind(this)); }, - fetchExpiring: function() { - this.fetch({conditions: {expireTimer: {$gt: 0}}}); + fetchNextExpiring: function() { + this.fetch({ index: { name: 'expires_at' }, limit: 1 }); + }, + + fetchExpired: function() { + console.log('loading expired messages'); + this.fetch({ + conditions: { expires_at: { $lte: Date.now() } }, + addIndividually: true + }); }, hasKeyConflicts: function() { diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index 33d5604e..b19c9a0c 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -227,6 +227,7 @@ } }, onExpiredCollection: function(message) { + console.log('removing message', message.get('sent_at'), 'from collection'); this.model.messageCollection.remove(message.id); }, diff --git a/js/views/message_view.js b/js/views/message_view.js index bd49b6d4..cc579d61 100644 --- a/js/views/message_view.js +++ b/js/views/message_view.js @@ -144,6 +144,9 @@ this.remove(); } }.bind(this)); + + // Failsafe: if in the background, animation events don't fire + setTimeout(this.remove.bind(this), 1000); }, onDestroy: function() { if (this.$el.hasClass('expired')) {