diff --git a/lib/client.js b/lib/client.js index f99ec8ccc..699d0bcda 100644 --- a/lib/client.js +++ b/lib/client.js @@ -543,7 +543,9 @@ module.exports.MatrixClient.prototype = { utils.map(data.rooms[i].messages.chunk, eventMapper) ); - room.recalculate(); + // cache the name/summary/etc prior to storage since we don't + // know how the store will serialise the Room. + room.recalculate(self.credentials.userId); self.store.storeRoom(room); } @@ -604,6 +606,10 @@ module.exports.MatrixClient.prototype = { room = new Room(roomId); } room.addEventsToTimeline([events[i]]); + if (events[i].isState()) { + room.currentState.setStateEvents([events[i]]); + room.recalculate(self.credentials.userId); + } } } } @@ -628,6 +634,7 @@ module.exports.MatrixClient.prototype = { */ stopClient: function() { this.clientRunning = false; + // TODO: f.e. Room => self.store.storeRoom(room) ? } }; diff --git a/lib/models/room-member.js b/lib/models/room-member.js index 6d81c21f6..70ce31e00 100644 --- a/lib/models/room-member.js +++ b/lib/models/room-member.js @@ -23,7 +23,7 @@ function RoomMember(event) { throw new Error("Invalid event type: " + event.getType()); } this.roomId = event.getRoomId(); - this.userId = event.getSender(); + this.userId = event.getStateKey(); this.event = event; this.typing = false; this.name = this.calculateDisplayName(); @@ -40,8 +40,9 @@ RoomMember.prototype = { */ calculateDisplayName: function(roomState) { var displayName = this.event.getContent().displayname; + var selfUserId = this.userId; if (!displayName) { - return this.userId; + return selfUserId; } if (!roomState) { return displayName; @@ -50,11 +51,12 @@ RoomMember.prototype = { var stateEvents = utils.filter( roomState.getStateEvents("m.room.member"), function(event) { - return event.getContent().displayname === displayName; + return event.getContent().displayname === displayName && + event.getSender() !== selfUserId; } ); if (stateEvents.length > 1) { - return displayName + " (" + this.userId + ")"; + return displayName + " (" + selfUserId + ")"; } return displayName; diff --git a/lib/models/room-state.js b/lib/models/room-state.js index feead6339..c7e1e2e63 100644 --- a/lib/models/room-state.js +++ b/lib/models/room-state.js @@ -3,6 +3,7 @@ * @module models/room-state */ var utils = require("../utils"); +var RoomMember = require("./room-member"); /** * Construct room state. @@ -45,9 +46,9 @@ RoomState.prototype = { getStateEvents: function(eventType, stateKey) { if (!this.stateEvents[eventType]) { // no match - return stateKey ? null : []; + return stateKey === undefined ? [] : null; } - if (!stateKey) { // return all values + if (stateKey === undefined) { // return all values return utils.values(this.stateEvents[eventType]); } var event = this.stateEvents[eventType][stateKey]; @@ -61,14 +62,20 @@ RoomState.prototype = { */ setStateEvents: function(stateEvents) { for (var i = 0; i < stateEvents.length; i++) { - var event = stateEvents[i].event; + var event = stateEvents[i]; if (event.getRoomId() !== this.roomId) { continue; } if (!event.isState()) { continue; } if (this.stateEvents[event.getType()] === undefined) { this.stateEvents[event.getType()] = {}; } - this.stateEvents[event.getType()][event.getStateKey()] = stateEvents[i]; + this.stateEvents[event.getType()][event.getStateKey()] = event; + + if (event.getType() === "m.room.member") { + var member = new RoomMember(event); + member.calculateDisplayName(this); + this.members[event.getStateKey()] = member; + } } } }; diff --git a/lib/models/room.js b/lib/models/room.js index c5f2ea865..e89260e70 100644 --- a/lib/models/room.js +++ b/lib/models/room.js @@ -82,15 +82,18 @@ Room.prototype = { return alias; } else { - var members = this.currentState.getMembers(); + // get members that are NOT ourselves. + var members = utils.filter(this.currentState.getMembers(), function(m) { + return m.userId !== userId; + }); if (members.length === 0) { return "Unknown"; } - else if (members.length == 1) { + else if (members.length === 1) { return members[0].name; } - else if (members.length == 2) { + else if (members.length === 2) { return ( members[0].name + " and " + members[1].name ); diff --git a/lib/utils.js b/lib/utils.js index 645f35e78..b048f6581 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -65,7 +65,7 @@ module.exports.filter = function(array, fn) { var results = []; for (var i = 0; i < array.length; i++) { if (fn(array[i], i, array)) { - results.push(results); + results.push(array[i]); } } return results; diff --git a/spec/integ/matrix-client.spec.js b/spec/integ/matrix-client.spec.js index 648e2fedc..89c2f958b 100644 --- a/spec/integ/matrix-client.spec.js +++ b/spec/integ/matrix-client.spec.js @@ -6,12 +6,19 @@ var utils = require("../test-utils"); describe("MatrixClient", function() { var baseUrl = "http://localhost.or.something"; var client, httpBackend; + var selfUserId = "@alice:localhost"; + var selfAccessToken = "aseukfgwef"; + var otherUserId = "@bob:localhost"; beforeEach(function() { utils.beforeEach(this); httpBackend = new HttpBackend(); sdk.request(httpBackend.requestFn); - client = sdk.createClient(baseUrl); + client = sdk.createClient({ + baseUrl: baseUrl, + userId: selfUserId, + accessToken: selfAccessToken + }); }); afterEach(function() { @@ -56,4 +63,119 @@ describe("MatrixClient", function() { }); }); + describe("room state", function() { + var roomOne = "!foo:localhost"; + var roomTwo = "!bar:localhost"; + var msgText = "some text here"; + var otherDisplayName = "Bob Smith"; + var initialSync = { + end: "s_5_3", + presence: [], + rooms: [ + { + membership: "join", + room_id: roomOne, + messages: { + start: "f_1_1", + end: "f_2_2", + chunk: [ + utils.mkMessage(roomOne, otherUserId, "hello") + ] + }, + state: [ + utils.mkEvent( + "m.room.name", roomOne, otherUserId, + { + name: "Old room name" + } + ), + utils.mkMembership(roomOne, "join", otherUserId), + utils.mkMembership(roomOne, "join", selfUserId), + utils.mkEvent( + "m.room.create", roomOne, selfUserId, + { + creator: selfUserId + } + ) + ] + }, + { + membership: "join", + room_id: roomTwo, + messages: { + start: "f_1_1", + end: "f_2_2", + chunk: [ + utils.mkMessage(roomTwo, otherUserId, "hiii") + ] + }, + state: [ + utils.mkMembership( + roomTwo, "join", otherUserId, null, otherDisplayName + ), + utils.mkMembership(roomTwo, "join", selfUserId), + utils.mkEvent( + "m.room.create", roomTwo, selfUserId, + { + creator: selfUserId + } + ) + ] + } + ] + }; + var eventData = { + start: "s_5_3", + end: "e_6_7", + chunk: [ + utils.mkEvent("m.room.name", roomOne, selfUserId, { + name: "A new room name" + }), + utils.mkMessage(roomTwo, otherUserId, msgText) + ] + }; + + it("should continually recalculate the right room name.", function(done) { + httpBackend.when("GET", "/initialSync").respond(200, initialSync); + httpBackend.when("GET", "/events").respond(200, eventData); + + client.startClient(function(err, data, isLive) {}); + + httpBackend.flush().done(function() { + var room = client.getStore().getRoom(roomOne); + // should have clobbered the name to the one from /events + expect(room.name).toEqual(eventData.chunk[0].content.name); + done(); + }); + }); + + it("should store the right events in the timeline.", function(done) { + httpBackend.when("GET", "/initialSync").respond(200, initialSync); + httpBackend.when("GET", "/events").respond(200, eventData); + + client.startClient(function(err, data, isLive) {}); + + httpBackend.flush().done(function() { + var room = client.getStore().getRoom(roomTwo); + // should have added the message from /events + expect(room.timeline.length).toEqual(2); + expect(room.timeline[1].getContent().body).toEqual(msgText); + done(); + }); + }); + + it("should set the right room name.", function(done) { + httpBackend.when("GET", "/initialSync").respond(200, initialSync); + httpBackend.when("GET", "/events").respond(200, eventData); + + client.startClient(function(err, data, isLive) {}); + httpBackend.flush().done(function() { + var room = client.getStore().getRoom(roomTwo); + // should use the display name of the other person. + expect(room.name).toEqual(otherDisplayName); + done(); + }); + }); + }); + }); diff --git a/spec/test-utils.js b/spec/test-utils.js index ef150ee8f..5637d98b3 100644 --- a/spec/test-utils.js +++ b/spec/test-utils.js @@ -10,3 +10,50 @@ module.exports.beforeEach = function(testCase) { console.log(desc); console.log(new Array(1 + desc.length).join("=")); }; + +/** + * Create a JSON object representing an Event. + * @param {string} type The event.type + * @param {string} room The event.room_id + * @param {string} userId The event.user_id + * @param {Object} content The event.content + */ +module.exports.mkEvent = function(type, room, userId, content) { + var event = { + type: type, + room_id: room, + user_id: userId, + content: content, + event_id: "$" + Math.random() + "-" + Math.random() + }; + if (["m.room.name", "m.room.topic", "m.room.create", "m.room.join_rules", + "m.room.power_levels", "m.room.topic", "com.example.state"].indexOf(type) + !== -1) { + event.state_key = ""; + } + return event; +}; + +module.exports.mkMembership = function(room, membership, userId, otherUserId, + displayName, avatarUrl) { + var event = module.exports.mkEvent("m.room.member", room, userId, { + membership: membership, + displayname: displayName, + avatar_url: avatarUrl + }); + event.state_key = userId; + if (["invite", "ban"].indexOf(membership) !== -1) { + event.state_key = otherUserId; + } + return event; +}; + +module.exports.mkMessage = function(room, userId, msg) { + if (!msg) { + msg = "Random->"+Math.random(); + } + return module.exports.mkEvent("m.room.message", room, userId, { + msgtype: "m.text", + body: msg + }); +}; \ No newline at end of file