1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-09-03 08:42:03 +03:00

Add concept of 'sentinel' RoomMembers which watch state at a particular point in time.

New sentinels are only created when the RoomMember state changes, so we don't
needlessly deep copy RoomMembers f.e. MatrixEvent. Sentinels co-exist with
RoomState.members which are single instances to which listeners can be attached.
This gets the best of both worlds (don't have to keep re-attaching listeners on
member changes, don't have needless memory consumption).
This commit is contained in:
Kegan Dougal
2015-06-12 15:38:02 +01:00
parent 8a844d59ec
commit 7a02c5d167
4 changed files with 62 additions and 17 deletions

View File

@@ -26,6 +26,10 @@ function RoomState(roomId) {
// eventType: { stateKey: MatrixEvent }
};
this.paginationToken = null;
this._sentinels = {
// userId: RoomMember
};
}
utils.inherits(RoomState, EventEmitter);
@@ -46,6 +50,19 @@ RoomState.prototype.getMember = function(userId) {
return this.members[userId] || null;
};
/**
* Get a room member whose properties will not change with this room state. You
* typically want this if you want to attach a RoomMember to a MatrixEvent which
* may no longer be represented correctly by Room.currentState or Room.oldState.
* The term 'sentinel' refers to the fact that this RoomMember is an unchanging
* guardian for state at this particular point in time.
* @param {string} userId The room member's user ID.
* @return {RoomMember} The member or null if they do not exist.
*/
RoomState.prototype.getSentinelMember = function(userId) {
return this._sentinels[userId] || null;
};
/**
* Get state events from the state of the room.
* @param {string} eventType The event type of the state event.
@@ -89,19 +106,29 @@ RoomState.prototype.setStateEvents = function(stateEvents) {
self.emit("RoomState.events", event, self);
if (event.getType() === "m.room.member") {
// we *always* need to create a new RoomMember because there may be
// MatrixEvents with their .sender property set to a RoomMember which
// was right for that point in time. We are now diverging from that
// point so in order to not corrupt the existing messages we need to
// make a new RoomMember.
var member = new RoomMember(event.getRoomId(), event.getStateKey());
member.setMembershipEvent(event, self);
var userId = event.getStateKey();
var member = self.members[userId];
if (!member) {
member = new RoomMember(event.getRoomId(), userId);
self.emit("RoomState.newMember", event, self, member);
}
// Add a new sentinel for this change. We apply the same
// operations to both sentinel and member rather than deep copying
// so we don't make assumptions about the properties of RoomMember
// (e.g. and manage to break it because deep copying doesn't do
// everything).
var sentinel = new RoomMember(event.getRoomId(), userId);
utils.forEach([member, sentinel], function(roomMember) {
roomMember.setMembershipEvent(event, self);
// this member may have a power level already, so set it.
var pwrLvlEvent = self.getStateEvents("m.room.power_levels", "");
if (pwrLvlEvent) {
member.setPowerLevelEvent(pwrLvlEvent);
roomMember.setPowerLevelEvent(pwrLvlEvent);
}
self.members[event.getStateKey()] = member;
});
self._sentinels[userId] = sentinel;
self.members[userId] = member;
self.emit("RoomState.members", event, self, member);
}
else if (event.getType() === "m.room.power_levels") {

View File

@@ -68,9 +68,13 @@ Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline) {
var stateContext = toStartOfTimeline ? this.oldState : this.currentState;
for (var i = 0; i < events.length; i++) {
// set sender and target properties
events[i].sender = stateContext.getMember(events[i].getSender());
events[i].sender = stateContext.getSentinelMember(
events[i].getSender()
);
if (events[i].getType() === "m.room.member") {
events[i].target = stateContext.getMember(events[i].getStateKey());
events[i].target = stateContext.getSentinelMember(
events[i].getStateKey()
);
}
// modify state

View File

@@ -159,7 +159,22 @@ module.exports.checkObjectHasNoAdditionalKeys = function(obj, allowedKeys) {
};
/**
* Deep copy the given object. The object MUST NOT have circular references.
* Assigns all the properties in src to dst. If these properties are Objects,
* then both src and dst will refer to the same thing.
* @param {Object} src The object to copy properties from.
* @param {Object} dst The object to write properties to.
*/
module.exports.shallowCopy = function(src, dst) {
for (var i in src) {
if (src.hasOwnProperty(i)) {
dst[i] = src[i];
}
}
};
/**
* Deep copy the given object. The object MUST NOT have circular references and
* MUST NOT have functions.
* @param {Object} obj The object to deep copy.
* @return {Object} A copy of the object without any references to the original.
*/

View File

@@ -156,7 +156,6 @@ describe("MatrixClient", function() {
var fired = false;
client.on("User.presence", function(event, user) {
fired = true;
console.log("handle");
expect(user).toBeDefined();
expect(event).toBeDefined();
if (!user || !event) { return; }