You've already forked matrix-js-sdk
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:
@@ -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") {
|
||||
|
@@ -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
|
||||
|
17
lib/utils.js
17
lib/utils.js
@@ -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.
|
||||
*/
|
||||
|
@@ -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; }
|
||||
|
Reference in New Issue
Block a user