diff --git a/examples/node/app.js b/examples/node/app.js
index a7533be6f..ed512b563 100644
--- a/examples/node/app.js
+++ b/examples/node/app.js
@@ -28,7 +28,9 @@ rl.on('line', function(line) {
viewingRoom = roomList[roomIndex];
if (viewingRoom.getMember(myUserId).membership === "invite") {
// join the room first
- matrixClient.joinRoom(viewingRoom.roomId).done(function() {
+ matrixClient.joinRoom(viewingRoom.roomId).done(function(room) {
+ roomList = matrixClient.getRooms();
+ viewingRoom = room;
printMessages();
}, function(err) {
console.log("Error: %s", err);
@@ -69,6 +71,13 @@ matrixClient.on("syncComplete", function() {
printHelp();
});
+matrixClient.on("Room", function() {
+ roomList = matrixClient.getRooms();
+ if (!viewingRoom) {
+ printRoomList();
+ }
+});
+
// print incoming messages.
matrixClient.on("Room.timeline", function(event, room, toStartOfTimeline) {
if (toStartOfTimeline) {
diff --git a/lib/client.js b/lib/client.js
index 956be29a3..08e53cc40 100644
--- a/lib/client.js
+++ b/lib/client.js
@@ -121,12 +121,26 @@ MatrixClient.prototype.createRoom = function(options, callback) {
* Join a room.
* @param {string} roomIdOrAlias The room ID or room alias to join.
* @param {module:client.callback} callback Optional.
- * @return {module:client.Promise} Resolves: {room_id: {string}}
+ * @return {module:client.Promise} Resolves: Room object.
* @return {module:http-api.MatrixError} Rejects: with an error response.
*/
MatrixClient.prototype.joinRoom = function(roomIdOrAlias, callback) {
var path = utils.encodeUri("/join/$roomid", { $roomid: roomIdOrAlias});
- return this._http.authedRequest(callback, "POST", path, undefined, {});
+ var defer = q.defer();
+ var self = this;
+ this._http.authedRequest(undefined, "POST", path, undefined, {}).then(
+ function(res) {
+ var roomId = res.room_id;
+ var room = createNewRoom(self, roomId);
+ return _syncRoom(self, room);
+ }, function(err) {
+ _reject(callback, defer, err);
+ }).done(function(room) {
+ _resolve(callback, defer, room);
+ }, function(err) {
+ _reject(callback, defer, err);
+ });
+ return defer.promise;
};
/**
@@ -915,10 +929,6 @@ MatrixClient.prototype.isLoggedIn = function() {
};
-
-
-
-
// Higher level APIs
// =================
@@ -959,10 +969,8 @@ MatrixClient.prototype.startClient = function(historyLen) {
var i, j;
// intercept the results and put them into our store
if (self.store) {
- var eventMapper = function(event) {
- return new MatrixEvent(event);
- };
- utils.forEach(utils.map(data.presence, eventMapper), function(e) {
+ utils.forEach(utils.map(data.presence, _PojoToMatrixEventMapper),
+ function(e) {
var user = createNewUser(self, e.getContent().user_id);
user.setPresenceEvent(e);
self.store.storeUser(user);
@@ -986,27 +994,8 @@ MatrixClient.prototype.startClient = function(historyLen) {
});
}
- // "old" and "current" state are the same initially; they
- // start diverging if the user paginates.
- // We must deep copy otherwise membership changes in old state
- // will leak through to current state!
- var oldStateEvents = utils.map(
- utils.deepCopy(data.rooms[i].state), eventMapper
- );
- var stateEvents = utils.map(data.rooms[i].state, eventMapper);
- room.oldState.setStateEvents(oldStateEvents);
- room.currentState.setStateEvents(stateEvents);
-
- // add events to the timeline *after* setting the state
- // events so messages use the right display names. Initial sync
- // returns messages in chronological order, so we need to reverse
- // it to get most recent -> oldest. We need it in that order in
- // order to diverge old/current state correctly.
- room.addEventsToTimeline(
- utils.map(
- data.rooms[i].messages ? data.rooms[i].messages.chunk : [],
- eventMapper
- ).reverse(), true
+ _processRoomEvents(
+ room, data.rooms[i].state, data.rooms[i].messages
);
// cache the name/summary/etc prior to storage since we don't
@@ -1087,23 +1076,38 @@ function _pollForEvents(client) {
}
// add events to room
var roomIds = utils.keys(roomIdToEvents);
- for (i = 0; i < roomIds.length; i++) {
- var room = self.store.getRoom(roomIds[i]);
- var shouldStoreRoom = false;
+ utils.forEach(roomIds, function(roomId) {
+ var room = self.store.getRoom(roomId);
+ var isBrandNewRoom = false;
if (!room) {
- room = createNewRoom(self, roomIds[i]);
- shouldStoreRoom = true;
+ room = createNewRoom(self, roomId);
+ isBrandNewRoom = true;
}
- room.addEvents(roomIdToEvents[roomIds[i]], "replace");
+
+ //var wasJoined = room.hasMembershipState(
+ // self.credentials.userId, "join"
+ //);
+
+ room.addEvents(roomIdToEvents[roomId], "replace");
room.recalculate(self.credentials.userId);
- if (shouldStoreRoom) {
+ // store the Room for things like invite events so developers
+ // can update the UI
+ if (isBrandNewRoom) {
self.store.storeRoom(room);
- // TODO: Should be doing roomInitialSync here to pull in
- // all state before emitting this(!)
self.emit("Room", room);
}
- }
+ /* TODO invite->join trigger roominitialsync
+ var justJoined = room.hasMembershipState(
+ self.credentials.userId, "join"
+ );
+
+ if (!wasJoined && justJoined) {
+ // we've just transitioned into a join state for this room,
+ // so sync state.
+ _syncRoom(self, room);
+ } */
+ });
}
if (data) {
self.fromToken = data.end;
@@ -1122,6 +1126,45 @@ function _pollForEvents(client) {
});
}
+function _syncRoom(client, room) {
+ var defer = q.defer();
+ client.roomInitialSync(room.roomId, 8).done(function(res) {
+ _processRoomEvents(room, res.state, res.messages);
+ room.recalculate(client.credentials.userId);
+ client.store.storeRoom(room);
+ client.emit("Room", room);
+ defer.resolve(room);
+ }, function(err) {
+ defer.reject(err);
+ });
+ return defer.promise;
+}
+
+function _processRoomEvents(room, stateEventList, messageChunk) {
+ // "old" and "current" state are the same initially; they
+ // start diverging if the user paginates.
+ // We must deep copy otherwise membership changes in old state
+ // will leak through to current state!
+ var oldStateEvents = utils.map(
+ utils.deepCopy(stateEventList), _PojoToMatrixEventMapper
+ );
+ var stateEvents = utils.map(stateEventList, _PojoToMatrixEventMapper);
+ room.oldState.setStateEvents(oldStateEvents);
+ room.currentState.setStateEvents(stateEvents);
+
+ // add events to the timeline *after* setting the state
+ // events so messages use the right display names. Initial sync
+ // returns messages in chronological order, so we need to reverse
+ // it to get most recent -> oldest. We need it in that order in
+ // order to diverge old/current state correctly.
+ room.addEventsToTimeline(
+ utils.map(
+ messageChunk ? messageChunk.chunk : [],
+ _PojoToMatrixEventMapper
+ ).reverse(), true
+ );
+}
+
/**
* High level helper method to stop the client from polling and allow a
* clean shutdown.
@@ -1178,6 +1221,24 @@ function createNewRoom(client, roomId) {
return room;
}
+function _reject(callback, defer, err) {
+ if (callback) {
+ callback(err);
+ }
+ defer.reject(err);
+}
+
+function _resolve(callback, defer, res) {
+ if (callback) {
+ callback(null, res);
+ }
+ defer.resolve(res);
+}
+
+function _PojoToMatrixEventMapper(plainOldJsObject) {
+ return new MatrixEvent(plainOldJsObject);
+}
+
/** */
module.exports.MatrixClient = MatrixClient;
@@ -1215,7 +1276,8 @@ module.exports.MatrixClient = MatrixClient;
*/
/**
- * Fires whenever a new Room is added. This event is experimental and
+ * Fires whenever a new Room is added. This will fire when you are invited to a
+ * room, as well as when you join a room. This event is experimental and
* may change.
* @event module:client~MatrixClient#"Room"
* @param {Room} room The newly created, fully populated room.
diff --git a/lib/models/room.js b/lib/models/room.js
index 24550ad76..64afa001c 100644
--- a/lib/models/room.js
+++ b/lib/models/room.js
@@ -55,6 +55,18 @@ utils.inherits(Room, EventEmitter);
});
};
+ /**
+ * Check if the given user_id has the given membership state.
+ * @param {string} userId The user ID to check.
+ * @param {string} membership The membership e.g. 'join'
+ * @return {boolean} True if this user_id has the given membership state.
+ */
+ Room.prototype.hasMembershipState = function(userId, membership) {
+ return utils.filter(this.currentState.getMembers(), function(m) {
+ return m.membership === membership && m.userId === userId;
+ }).length > 0;
+ };
+
/**
* Add some events to this room's timeline. Will fire "Room.timeline" for
* each event added.
diff --git a/lib/scheduler.js b/lib/scheduler.js
index 899c556b9..8f7d4adcc 100644
--- a/lib/scheduler.js
+++ b/lib/scheduler.js
@@ -4,7 +4,6 @@
* of requests.
* @module scheduler
*/
-var EventStatus = require("./models/event").EventStatus;
var utils = require("./utils");
var q = require("q");