You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-09-01 21:21:58 +03:00
Merge pull request #32 from matrix-org/member-info-for-invites
Retrieving profile info for invites
This commit is contained in:
@@ -141,6 +141,7 @@ function MatrixClient(opts) {
|
|||||||
this.callList = {
|
this.callList = {
|
||||||
// callId: MatrixCall
|
// callId: MatrixCall
|
||||||
};
|
};
|
||||||
|
this._config = {}; // see startClient()
|
||||||
|
|
||||||
// try constructing a MatrixCall to see if we are running in an environment
|
// try constructing a MatrixCall to see if we are running in an environment
|
||||||
// which has WebRTC. If we are, listen for and handle m.call.* events.
|
// which has WebRTC. If we are, listen for and handle m.call.* events.
|
||||||
@@ -1986,6 +1987,9 @@ function doInitialSync(client, historyLen, includeArchived) {
|
|||||||
* @param {Number} opts.initialSyncLimit The event <code>limit=</code> to apply
|
* @param {Number} opts.initialSyncLimit The event <code>limit=</code> to apply
|
||||||
* to initial sync. Default: 8.
|
* to initial sync. Default: 8.
|
||||||
* @param {Boolean} opts.includeArchivedRooms True to put <code>archived=true</code>
|
* @param {Boolean} opts.includeArchivedRooms True to put <code>archived=true</code>
|
||||||
|
* @param {Boolean} opts.resolveInvitesToProfiles True to do /profile requests
|
||||||
|
* on every invite event if the displayname/avatar_url is not known for this user ID.
|
||||||
|
* Default: false.
|
||||||
* on the <code>/initialSync</code> request. Default: false.
|
* on the <code>/initialSync</code> request. Default: false.
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.startClient = function(opts) {
|
MatrixClient.prototype.startClient = function(opts) {
|
||||||
@@ -2003,6 +2007,8 @@ MatrixClient.prototype.startClient = function(opts) {
|
|||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
opts.initialSyncLimit = opts.initialSyncLimit || 8;
|
opts.initialSyncLimit = opts.initialSyncLimit || 8;
|
||||||
opts.includeArchivedRooms = opts.includeArchivedRooms || false;
|
opts.includeArchivedRooms = opts.includeArchivedRooms || false;
|
||||||
|
opts.resolveInvitesToProfiles = opts.resolveInvitesToProfiles || false;
|
||||||
|
this._config = opts;
|
||||||
|
|
||||||
if (CRYPTO_ENABLED && this.sessionStore !== null) {
|
if (CRYPTO_ENABLED && this.sessionStore !== null) {
|
||||||
this.uploadKeys(5);
|
this.uploadKeys(5);
|
||||||
@@ -2057,6 +2063,7 @@ function _pollForEvents(client) {
|
|||||||
events = utils.map(data.chunk, _PojoToMatrixEventMapper(self));
|
events = utils.map(data.chunk, _PojoToMatrixEventMapper(self));
|
||||||
}
|
}
|
||||||
if (!(self.store instanceof StubStore)) {
|
if (!(self.store instanceof StubStore)) {
|
||||||
|
var roomIdsWithNewInvites = {};
|
||||||
// bucket events based on room.
|
// bucket events based on room.
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var roomIdToEvents = {};
|
var roomIdToEvents = {};
|
||||||
@@ -2068,6 +2075,10 @@ function _pollForEvents(client) {
|
|||||||
roomIdToEvents[roomId] = [];
|
roomIdToEvents[roomId] = [];
|
||||||
}
|
}
|
||||||
roomIdToEvents[roomId].push(events[i]);
|
roomIdToEvents[roomId].push(events[i]);
|
||||||
|
if (events[i].getType() === "m.room.member" &&
|
||||||
|
events[i].getContent().membership === "invite") {
|
||||||
|
roomIdsWithNewInvites[roomId] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (events[i].getType() === "m.presence") {
|
else if (events[i].getType() === "m.presence") {
|
||||||
var usr = self.store.getUser(events[i].getContent().user_id);
|
var usr = self.store.getUser(events[i].getContent().user_id);
|
||||||
@@ -2081,6 +2092,7 @@ function _pollForEvents(client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add events to room
|
// add events to room
|
||||||
var roomIds = utils.keys(roomIdToEvents);
|
var roomIds = utils.keys(roomIdToEvents);
|
||||||
utils.forEach(roomIds, function(roomId) {
|
utils.forEach(roomIds, function(roomId) {
|
||||||
@@ -2115,6 +2127,10 @@ function _pollForEvents(client) {
|
|||||||
_syncRoom(self, room);
|
_syncRoom(self, room);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.keys(roomIdsWithNewInvites).forEach(function(inviteRoomId) {
|
||||||
|
_resolveInvites(self, self.store.getRoom(inviteRoomId));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
self.store.setSyncToken(data.end);
|
self.store.setSyncToken(data.end);
|
||||||
@@ -2173,6 +2189,8 @@ function _processRoomEvents(client, room, stateEventList, messageChunk) {
|
|||||||
room.oldState.setStateEvents(oldStateEvents);
|
room.oldState.setStateEvents(oldStateEvents);
|
||||||
room.currentState.setStateEvents(stateEvents);
|
room.currentState.setStateEvents(stateEvents);
|
||||||
|
|
||||||
|
_resolveInvites(client, room);
|
||||||
|
|
||||||
// add events to the timeline *after* setting the state
|
// add events to the timeline *after* setting the state
|
||||||
// events so messages use the right display names. Initial sync
|
// events so messages use the right display names. Initial sync
|
||||||
// returns messages in chronological order, so we need to reverse
|
// returns messages in chronological order, so we need to reverse
|
||||||
@@ -2217,6 +2235,47 @@ function reEmit(reEmitEntity, emittableEntity, eventNames) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _resolveInvites(client, room) {
|
||||||
|
if (!room || !client._config.resolveInvitesToProfiles) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// For each invited room member we want to give them a displayname/avatar url
|
||||||
|
// if they have one (the m.room.member invites don't contain this).
|
||||||
|
room.getMembersWithMembership("invite").forEach(function(member) {
|
||||||
|
if (member._requestedProfileInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
member._requestedProfileInfo = true;
|
||||||
|
// try to get a cached copy first.
|
||||||
|
var user = client.getUser(member.userId);
|
||||||
|
var promise;
|
||||||
|
if (user) {
|
||||||
|
promise = q({
|
||||||
|
avatar_url: user.avatarUrl,
|
||||||
|
displayname: user.displayName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
promise = client.getProfileInfo(member.userId);
|
||||||
|
}
|
||||||
|
promise.done(function(info) {
|
||||||
|
// slightly naughty by doctoring the invite event but this means all
|
||||||
|
// the code paths remain the same between invite/join display name stuff
|
||||||
|
// which is a worthy trade-off for some minor pollution.
|
||||||
|
var inviteEvent = member.events.member;
|
||||||
|
if (inviteEvent.getContent().membership !== "invite") {
|
||||||
|
// between resolving and now they have since joined, so don't clobber
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inviteEvent.getContent().avatar_url = info.avatar_url;
|
||||||
|
inviteEvent.getContent().displayname = info.displayname;
|
||||||
|
member.setMembershipEvent(inviteEvent, room.currentState); // fire listeners
|
||||||
|
}, function(err) {
|
||||||
|
// OH WELL.
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function setupCallEventHandler(client) {
|
function setupCallEventHandler(client) {
|
||||||
var candidatesByCall = {
|
var candidatesByCall = {
|
||||||
// callId: [Candidate]
|
// callId: [Candidate]
|
||||||
|
@@ -10,6 +10,11 @@ describe("MatrixClient syncing", function() {
|
|||||||
var selfUserId = "@alice:localhost";
|
var selfUserId = "@alice:localhost";
|
||||||
var selfAccessToken = "aseukfgwef";
|
var selfAccessToken = "aseukfgwef";
|
||||||
var otherUserId = "@bob:localhost";
|
var otherUserId = "@bob:localhost";
|
||||||
|
var userA = "@alice:bar";
|
||||||
|
var userB = "@bob:bar";
|
||||||
|
var userC = "@claire:bar";
|
||||||
|
var roomOne = "!foo:localhost";
|
||||||
|
var roomTwo = "!bar:localhost";
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
utils.beforeEach(this);
|
utils.beforeEach(this);
|
||||||
@@ -65,10 +70,156 @@ describe("MatrixClient syncing", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("resolving invites to profile info", function() {
|
||||||
|
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({
|
||||||
|
room: roomOne, user: otherUserId, msg: "hello"
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
state: [
|
||||||
|
utils.mkMembership({
|
||||||
|
room: roomOne, mship: "join", user: otherUserId
|
||||||
|
}),
|
||||||
|
utils.mkMembership({
|
||||||
|
room: roomOne, mship: "join", user: selfUserId
|
||||||
|
}),
|
||||||
|
utils.mkEvent({
|
||||||
|
type: "m.room.create", room: roomOne, user: selfUserId,
|
||||||
|
content: {
|
||||||
|
creator: selfUserId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
var eventData = {
|
||||||
|
start: "s_5_3",
|
||||||
|
end: "e_6_7",
|
||||||
|
chunk: []
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
eventData.chunk = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve incoming invites from /events", function(done) {
|
||||||
|
eventData.chunk = [
|
||||||
|
utils.mkMembership({
|
||||||
|
room: roomOne, mship: "invite", user: userC
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
httpBackend.when("GET", "/initialSync").respond(200, initialSync);
|
||||||
|
httpBackend.when("GET", "/events").respond(200, eventData);
|
||||||
|
httpBackend.when("GET", "/profile/" + encodeURIComponent(userC)).respond(
|
||||||
|
200, {
|
||||||
|
avatar_url: "mxc://flibble/wibble",
|
||||||
|
displayname: "The Boss"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
client.startClient({
|
||||||
|
resolveInvitesToProfiles: true
|
||||||
|
});
|
||||||
|
|
||||||
|
httpBackend.flush().done(function() {
|
||||||
|
var member = client.getRoom(roomOne).getMember(userC);
|
||||||
|
expect(member.name).toEqual("The Boss");
|
||||||
|
expect(
|
||||||
|
member.getAvatarUrl("home.server.url", null, null, null, false)
|
||||||
|
).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use cached values from m.presence wherever possible", function(done) {
|
||||||
|
eventData.chunk = [
|
||||||
|
utils.mkPresence({
|
||||||
|
user: userC, presence: "online", name: "The Ghost"
|
||||||
|
}),
|
||||||
|
utils.mkMembership({
|
||||||
|
room: roomOne, mship: "invite", user: userC
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
httpBackend.when("GET", "/initialSync").respond(200, initialSync);
|
||||||
|
httpBackend.when("GET", "/events").respond(200, eventData);
|
||||||
|
|
||||||
|
client.startClient({
|
||||||
|
resolveInvitesToProfiles: true
|
||||||
|
});
|
||||||
|
|
||||||
|
httpBackend.flush().done(function() {
|
||||||
|
var member = client.getRoom(roomOne).getMember(userC);
|
||||||
|
expect(member.name).toEqual("The Ghost");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should result in events on the room member firing", function(done) {
|
||||||
|
eventData.chunk = [
|
||||||
|
utils.mkPresence({
|
||||||
|
user: userC, presence: "online", name: "The Ghost"
|
||||||
|
}),
|
||||||
|
utils.mkMembership({
|
||||||
|
room: roomOne, mship: "invite", user: userC
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
httpBackend.when("GET", "/initialSync").respond(200, initialSync);
|
||||||
|
httpBackend.when("GET", "/events").respond(200, eventData);
|
||||||
|
|
||||||
|
var latestFiredName = null;
|
||||||
|
client.on("RoomMember.name", function(event, m) {
|
||||||
|
if (m.userId === userC && m.roomId === roomOne) {
|
||||||
|
latestFiredName = m.name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.startClient({
|
||||||
|
resolveInvitesToProfiles: true
|
||||||
|
});
|
||||||
|
|
||||||
|
httpBackend.flush().done(function() {
|
||||||
|
expect(latestFiredName).toEqual("The Ghost");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should no-op if resolveInvitesToProfiles is not set", function(done) {
|
||||||
|
eventData.chunk = [
|
||||||
|
utils.mkMembership({
|
||||||
|
room: roomOne, mship: "invite", user: userC
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
httpBackend.when("GET", "/initialSync").respond(200, initialSync);
|
||||||
|
httpBackend.when("GET", "/events").respond(200, eventData);
|
||||||
|
|
||||||
|
client.startClient();
|
||||||
|
|
||||||
|
httpBackend.flush().done(function() {
|
||||||
|
var member = client.getRoom(roomOne).getMember(userC);
|
||||||
|
expect(member.name).toEqual(userC);
|
||||||
|
expect(
|
||||||
|
member.getAvatarUrl("home.server.url", null, null, null, false)
|
||||||
|
).toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("users", function() {
|
describe("users", function() {
|
||||||
var userA = "@alice:bar";
|
|
||||||
var userB = "@bob:bar";
|
|
||||||
var userC = "@claire:bar";
|
|
||||||
var initialSync = {
|
var initialSync = {
|
||||||
end: "s_5_3",
|
end: "s_5_3",
|
||||||
presence: [
|
presence: [
|
||||||
@@ -113,8 +264,6 @@ describe("MatrixClient syncing", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("room state", function() {
|
describe("room state", function() {
|
||||||
var roomOne = "!foo:localhost";
|
|
||||||
var roomTwo = "!bar:localhost";
|
|
||||||
var msgText = "some text here";
|
var msgText = "some text here";
|
||||||
var otherDisplayName = "Bob Smith";
|
var otherDisplayName = "Bob Smith";
|
||||||
var initialSync = {
|
var initialSync = {
|
||||||
@@ -272,7 +421,6 @@ describe("MatrixClient syncing", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("receipts", function() {
|
describe("receipts", function() {
|
||||||
var roomOne = "!foo:localhost";
|
|
||||||
var initialSync = {
|
var initialSync = {
|
||||||
end: "s_5_3",
|
end: "s_5_3",
|
||||||
presence: [],
|
presence: [],
|
||||||
|
@@ -13,6 +13,7 @@ function HttpBackend() {
|
|||||||
this.requestFn = function(opts, callback) {
|
this.requestFn = function(opts, callback) {
|
||||||
var realReq = new Request(opts.method, opts.uri, opts.body, opts.qs);
|
var realReq = new Request(opts.method, opts.uri, opts.body, opts.qs);
|
||||||
realReq.callback = callback;
|
realReq.callback = callback;
|
||||||
|
console.log("HTTP backend received request: %s %s", opts.method, opts.uri);
|
||||||
self.requests.push(realReq);
|
self.requests.push(realReq);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user