diff --git a/lib/client.js b/lib/client.js index 0ce1576f5..106dfea26 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1943,13 +1943,15 @@ MatrixClient.prototype.registerGuest = function(opts, callback) { * Peek into a room and receive updates about the room. This only works if the * history visibility for the room is world_readable. * @param {String} roomId The room to attempt to peek into. + * @return {module:client.Promise} Resolves: Room object + * @return {module:http-api.MatrixError} Rejects: with an error response. */ MatrixClient.prototype.peekInRoom = function(roomId) { if (this._peekSync) { this._peekSync.stopPeeking(); } this._peekSync = new SyncApi(this); - this._peekSync.peek(roomId); + return this._peekSync.peek(roomId); }; /** diff --git a/lib/sync.js b/lib/sync.js index 1bc7335ac..e7299a39b 100644 --- a/lib/sync.js +++ b/lib/sync.js @@ -44,6 +44,7 @@ function SyncApi(client, opts) { opts.pollTimeout = opts.pollTimeout || (30 * 1000); opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological"; this.opts = opts; + this._peekRoomId = null; } /** @@ -143,9 +144,53 @@ SyncApi.prototype.syncLeftRooms = function() { * Peek into a room. This will result in the room in question being synced so it * is accessible via getRooms(). Live updates for the room will be provided. * @param {string} roomId The room ID to peek into. + * @return {Promise} A promise which resolves once the room has been added to the + * store. */ SyncApi.prototype.peek = function(roomId) { + var self = this; + var client = this.client; + this._peekRoomId = roomId; + return this.client.roomInitialSync(roomId, 20).then(function(response) { + // make sure things are init'd + response.messages = response.messages || {}; + response.messages.chunk = response.messages.chunk || []; + response.state = response.state || []; + var peekRoom = self.createRoom(roomId); + + // FIXME: Mostly duplicated from _processRoomEvents but not entirely + // because "state" in this API is at the BEGINNING of the chunk + var oldStateEvents = utils.map( + utils.deepCopy(response.state), client.getEventMapper() + ); + var stateEvents = utils.map( + response.state, client.getEventMapper() + ); + var messages = utils.map( + response.messages.chunk, client.getEventMapper() + ); + + if (response.messages.start) { + peekRoom.oldState.paginationToken = response.messages.start; + } + + // set the state of the room to as it was after the timeline executes + peekRoom.oldState.setStateEvents(oldStateEvents); + peekRoom.currentState.setStateEvents(stateEvents); + + self._resolveInvites(peekRoom); + peekRoom.recalculate(self.client.credentials.userId); + + // roll backwards to diverge old state: + peekRoom.addEventsToTimeline(messages.reverse(), true); + + client.store.storeRoom(peekRoom); + client.emit("Room", peekRoom); + + self._peekPoll(roomId); + return peekRoom; + }); }; /** @@ -153,7 +198,40 @@ SyncApi.prototype.peek = function(roomId) { * peeked. */ SyncApi.prototype.stopPeeking = function() { + this._peekRoomId = null; +}; +/** + * Do a peek room poll. + * @param {string} roomId + * @param {string} token from= token + */ +SyncApi.prototype._peekPoll = function(roomId, token) { + if (this._peekRoomId !== roomId) { + console.log("Stopped peeking in room %s", roomId); + return; + } + + var self = this; + // FIXME: gut wrenching; hard-coded timeout values + this.client._http.authedRequestWithPrefix(undefined, "GET", "/events", { + room_id: roomId, + timeout: 30 * 1000, + from: token + }, undefined, httpApi.PREFIX_V1, 50 * 1000).done(function(res) { + // strip out events which aren't for the given room_id (e.g presence) + var events = res.chunk.filter(function(e) { + return e.room_id === roomId; + }).map(self.client.getEventMapper()); + var room = self.client.getRoom(roomId); + room.addEvents(events); + self._peekPoll(roomId, res.end); + }, function(err) { + console.error("[%s] Peek poll failed: %s", roomId, err); + setTimeout(function() { + self._peekPoll(roomId, token); + }, 30 * 1000); + }); }; /**