From ea738e31badc647f517bd55cb0641b51f516e7e3 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 2 Jul 2015 11:03:50 +0100 Subject: [PATCH] Add storeEvents impl. --- lib/client.js | 19 ++++++++--- lib/store/memory.js | 11 +++++++ lib/store/stub.js | 10 ++++++ lib/store/webstorage.js | 70 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 99 insertions(+), 11 deletions(-) diff --git a/lib/client.js b/lib/client.js index 40ed527ee..e2bf7d919 100644 --- a/lib/client.js +++ b/lib/client.js @@ -830,27 +830,38 @@ MatrixClient.prototype.roomState = function(roomId, callback) { */ MatrixClient.prototype.scrollback = function(room, limit, callback) { if (utils.isFunction(limit)) { callback = limit; limit = undefined; } + limit = limit || 30; + if (room.oldState.paginationToken === null) { return q(room); // already at the start. } + // attempt to grab more events from the store first + var numAdded = this.store.scrollback(room, limit).length; + if (numAdded === limit) { + // store contained everything we needed. + return q(room); + } + // reduce the required number of events appropriately + limit = limit - numAdded; + var path = utils.encodeUri( "/rooms/$roomId/messages", {$roomId: room.roomId} ); - limit = limit || 30; var params = { from: room.oldState.paginationToken, limit: limit, dir: 'b' }; var defer = q.defer(); + var self = this; this._http.authedRequest(callback, "GET", path, params).done(function(res) { - room.addEventsToTimeline( - utils.map(res.chunk, _PojoToMatrixEventMapper), true - ); + var matrixEvents = utils.map(res.chunk, _PojoToMatrixEventMapper); + room.addEventsToTimeline(matrixEvents, true); room.oldState.paginationToken = res.end; if (res.chunk.length < limit) { room.oldState.paginationToken = null; } + self.store.storeEvents(room, matrixEvents, res.end, true); _resolve(callback, defer, room); }, function(err) { _reject(callback, defer, err); diff --git a/lib/store/memory.js b/lib/store/memory.js index 9db8f9e06..72fda7b0e 100644 --- a/lib/store/memory.js +++ b/lib/store/memory.js @@ -98,6 +98,17 @@ module.exports.MatrixInMemoryStore.prototype = { */ scrollback: function(room, limit) { return []; + }, + + /** + * Store events for a room. The events have already been added to the timeline + * @param {Room} room The room to store events for. + * @param {Array} events The events to store. + * @param {string} token The token associated with these events. + * @param {boolean} toStart True if these are paginated results. + */ + storeEvents: function(room, events, token, toStart) { + // no-op because they've already been added to the room instance. } // TODO diff --git a/lib/store/stub.js b/lib/store/stub.js index 0d06082d2..2bfea4f07 100644 --- a/lib/store/stub.js +++ b/lib/store/stub.js @@ -86,6 +86,16 @@ StubStore.prototype = { */ scrollback: function(room, limit) { return []; + }, + + /** + * Store events for a room. + * @param {Room} room The room to store events for. + * @param {Array} events The events to store. + * @param {string} token The token associated with these events. + * @param {boolean} toStart True if these are paginated results. + */ + storeEvents: function(room, events, token, toStart) { } // TODO diff --git a/lib/store/webstorage.js b/lib/store/webstorage.js index 4ed81329d..d23cc756c 100644 --- a/lib/store/webstorage.js +++ b/lib/store/webstorage.js @@ -364,6 +364,59 @@ WebStorageStore.prototype.scrollback = function(room, limit) { return scrollback; }; +/** + * Store events for a room. The events have already been added to the timeline. + * @param {Room} room The room to store events for. + * @param {Array} events The events to store. + * @param {string} token The token associated with these events. + * @param {boolean} toStart True if these are paginated results. The last element + * is the 'oldest' (for parity with homeserver scrollback APIs). + */ +WebStorageStore.prototype.storeEvents = function(room, events, token, toStart) { + if (toStart) { + // add paginated events to lowest batch indexes (can go -ve) + var lowIndex = getIndexExtremity( + getTimelineIndices(this.store, room.roomId), true + ); + var i, key, batch; + for (i = 0; i < events.length; i++) { // loop events to be stored + key = keyName(room.roomId, "timeline", lowIndex); + batch = this.store.getItem(key) || []; + while (batch.length < this.batchSize && i < events.length) { + batch.unshift(events[i]); + i++; // increment to insert next event into this batch + } + i--; // decrement to avoid skipping one (for loop ++s) + this.store.setItem(key, batch); + lowIndex--; // decrement index to get a new batch. + } + } + else { + // dump as live events + var liveEvents = this.store.getItem( + keyName(room.roomId, "timeline", "live") + ) || []; + debuglog( + "Adding %s events to %s live list (which has %s already)", + events.length, room.roomId, liveEvents.length + ); + var updateState = false; + liveEvents.concat(utils.map(events, function(me) { + // cheeky check to avoid looping twice + if (me.isState()) { + updateState = true; + } + return me.event; + })); + if (updateState) { + debuglog("Storing state for %s as new events updated state", room.roomId); + // use 0 batch size; we don't care about batching right now. + var serRoom = SerialisedRoom.fromRoom(room, 0); + this.store.setItem(keyName(serRoom.roomId, "state"), serRoom.state); + } + } +}; + /** * Sync the 'live' timeline, batching live events according to 'batchSize'. * @param {string} roomId The room to sync the timeline. @@ -375,7 +428,7 @@ WebStorageStore.prototype._syncTimeline = function(roomId, timelineIndices) { var liveEvents = this.store.getItem(keyName(roomId, "timeline", "live")) || []; // get the highest numbered $INDEX batch - var highestIndex = getHighestIndex(timelineIndices); + var highestIndex = getIndexExtremity(timelineIndices); var hiKey = keyName(roomId, "timeline", highestIndex); var hiBatch = this.store.getItem(hiKey) || []; // fill up the existing batch first. @@ -481,7 +534,7 @@ function loadRoom(store, roomId, numEvents, tokenArray) { // add most recent numEvents var recentEvents = []; - var index = getHighestIndex(getTimelineIndices(store, roomId)); + var index = getIndexExtremity(getTimelineIndices(store, roomId)); var eventIndex = index; var i, key, batch; while (recentEvents.length < numEvents) { @@ -534,15 +587,18 @@ function getTimelineIndices(store, roomId) { return keys; } -function getHighestIndex(timelineIndices) { - var highestIndex, index; +function getIndexExtremity(timelineIndices, getLowest) { + var extremity, index; for (var i = 0; i < timelineIndices.length; i++) { index = parseInt(timelineIndices[i]); - if (!isNaN(index) && (highestIndex === undefined || index > highestIndex)) { - highestIndex = index; + if (!isNaN(index) && ( + extremity === undefined || + !getLowest && index > extremity || + getLowest && index < extremity)) { + extremity = index; } } - return highestIndex; + return extremity; } function keyName(roomId, key, index) {