pass memberchanges around instead of members
so we can easily tell how their membership changes, (e.g. join -> left) which we'll need for device tracking. Not adding this to RoomMember because RoomMember also needs to be able to represent a member loaded from storage which doesn't contain this error. A MemberChange exists only within a sync.
This commit is contained in:
parent
164384f312
commit
8482bc95ec
5 changed files with 64 additions and 35 deletions
|
@ -51,7 +51,7 @@ export class Room extends EventEmitter {
|
|||
membership,
|
||||
isInitialSync, isTimelineOpen,
|
||||
txn);
|
||||
const {entries, newLiveKey, changedMembers} = await this._syncWriter.writeSync(roomResponse, txn);
|
||||
const {entries, newLiveKey, memberChanges} = await this._syncWriter.writeSync(roomResponse, txn);
|
||||
// fetch new members while we have txn open,
|
||||
// but don't make any in-memory changes yet
|
||||
let heroChanges;
|
||||
|
@ -60,7 +60,7 @@ export class Room extends EventEmitter {
|
|||
if (!this._heroes) {
|
||||
this._heroes = new Heroes(this._roomId);
|
||||
}
|
||||
heroChanges = await this._heroes.calculateChanges(summaryChanges.heroes, changedMembers, txn);
|
||||
heroChanges = await this._heroes.calculateChanges(summaryChanges.heroes, memberChanges, txn);
|
||||
}
|
||||
let removedPendingEvents;
|
||||
if (roomResponse.timeline && roomResponse.timeline.events) {
|
||||
|
@ -71,22 +71,22 @@ export class Room extends EventEmitter {
|
|||
newTimelineEntries: entries,
|
||||
newLiveKey,
|
||||
removedPendingEvents,
|
||||
changedMembers,
|
||||
memberChanges,
|
||||
heroChanges
|
||||
};
|
||||
}
|
||||
|
||||
/** @package */
|
||||
afterSync({summaryChanges, newTimelineEntries, newLiveKey, removedPendingEvents, changedMembers, heroChanges}) {
|
||||
afterSync({summaryChanges, newTimelineEntries, newLiveKey, removedPendingEvents, memberChanges, heroChanges}) {
|
||||
this._syncWriter.afterSync(newLiveKey);
|
||||
if (changedMembers.length) {
|
||||
if (memberChanges.size) {
|
||||
if (this._changedMembersDuringSync) {
|
||||
for (const member of changedMembers) {
|
||||
this._changedMembersDuringSync.set(member.userId, member);
|
||||
for (const [userId, memberChange] of memberChanges.entries()) {
|
||||
this._changedMembersDuringSync.set(userId, memberChange.member);
|
||||
}
|
||||
}
|
||||
if (this._memberList) {
|
||||
this._memberList.afterSync(changedMembers);
|
||||
this._memberList.afterSync(memberChanges);
|
||||
}
|
||||
}
|
||||
let emitChange = false;
|
||||
|
|
|
@ -42,11 +42,11 @@ export class Heroes {
|
|||
|
||||
/**
|
||||
* @param {string[]} newHeroes array of user ids
|
||||
* @param {RoomMember[]} changedMembers array of changed members in this sync
|
||||
* @param {Map<string, MemberChange>} memberChanges map of changed memberships
|
||||
* @param {Transaction} txn
|
||||
* @return {Promise}
|
||||
*/
|
||||
async calculateChanges(newHeroes, changedMembers, txn) {
|
||||
async calculateChanges(newHeroes, memberChanges, txn) {
|
||||
const updatedHeroMembers = new Map();
|
||||
const removedUserIds = [];
|
||||
// remove non-present members
|
||||
|
@ -56,9 +56,9 @@ export class Heroes {
|
|||
}
|
||||
}
|
||||
// update heroes with synced member changes
|
||||
for (const member of changedMembers) {
|
||||
if (this._members.has(member.userId) || newHeroes.indexOf(member.userId) !== -1) {
|
||||
updatedHeroMembers.set(member.userId, member);
|
||||
for (const [userId, memberChange] of memberChanges.entries()) {
|
||||
if (this._members.has(userId) || newHeroes.indexOf(userId) !== -1) {
|
||||
updatedHeroMembers.set(userId, memberChange.member);
|
||||
}
|
||||
}
|
||||
// load member for new heroes from storage
|
||||
|
|
|
@ -26,9 +26,9 @@ export class MemberList {
|
|||
this._retentionCount = 1;
|
||||
}
|
||||
|
||||
afterSync(updatedMembers) {
|
||||
for (const member of updatedMembers) {
|
||||
this._members.add(member.userId, member);
|
||||
afterSync(memberChanges) {
|
||||
for (const [userId, memberChange] of memberChanges.entries()) {
|
||||
this._members.add(userId, memberChange.member);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -99,3 +99,30 @@ export class RoomMember {
|
|||
return this._data;
|
||||
}
|
||||
}
|
||||
|
||||
export class MemberChange {
|
||||
constructor(roomId, memberEvent) {
|
||||
this._roomId = roomId;
|
||||
this._memberEvent = memberEvent;
|
||||
this._member = null;
|
||||
}
|
||||
|
||||
get member() {
|
||||
if (!this._member) {
|
||||
this._member = RoomMember.fromMemberEvent(this._roomId, this._memberEvent);
|
||||
}
|
||||
return this._member;
|
||||
}
|
||||
|
||||
userId() {
|
||||
return this._memberEvent.state_key;
|
||||
}
|
||||
|
||||
previousMembership() {
|
||||
return this._memberEvent.unsigned?.prev_content?.membership;
|
||||
}
|
||||
|
||||
membership() {
|
||||
return this._memberEvent.content?.membership;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import {EventKey} from "../EventKey.js";
|
|||
import {EventEntry} from "../entries/EventEntry.js";
|
||||
import {FragmentBoundaryEntry} from "../entries/FragmentBoundaryEntry.js";
|
||||
import {createEventEntry} from "./common.js";
|
||||
import {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../members/RoomMember.js";
|
||||
import {MemberChange, RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../members/RoomMember.js";
|
||||
|
||||
// Synapse bug? where the m.room.create event appears twice in sync response
|
||||
// when first syncing the room
|
||||
|
@ -102,13 +102,13 @@ export class SyncWriter {
|
|||
if (event.type === MEMBER_EVENT_TYPE) {
|
||||
const userId = event.state_key;
|
||||
if (userId) {
|
||||
const member = RoomMember.fromMemberEvent(this._roomId, event);
|
||||
if (member) {
|
||||
const memberChange = new MemberChange(this._roomId, event);
|
||||
if (memberChange.member) {
|
||||
// as this is sync, we can just replace the member
|
||||
// if it is there already
|
||||
txn.roomMembers.set(member.serialize());
|
||||
txn.roomMembers.set(memberChange.member.serialize());
|
||||
return memberChange;
|
||||
}
|
||||
return member;
|
||||
}
|
||||
} else {
|
||||
txn.roomState.set(this._roomId, event);
|
||||
|
@ -116,22 +116,22 @@ export class SyncWriter {
|
|||
}
|
||||
|
||||
_writeStateEvents(roomResponse, txn) {
|
||||
const changedMembers = [];
|
||||
const memberChanges = new Map();
|
||||
// persist state
|
||||
const {state} = roomResponse;
|
||||
if (Array.isArray(state?.events)) {
|
||||
for (const event of state.events) {
|
||||
const member = this._writeStateEvent(event, txn);
|
||||
if (member) {
|
||||
changedMembers.push(member);
|
||||
const memberChange = this._writeStateEvent(event, txn);
|
||||
if (memberChange) {
|
||||
memberChanges.set(memberChange.userId, memberChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
return changedMembers;
|
||||
return memberChanges;
|
||||
}
|
||||
|
||||
async _writeTimeline(entries, timeline, currentKey, txn) {
|
||||
const changedMembers = [];
|
||||
const memberChanges = new Map();
|
||||
if (timeline.events) {
|
||||
const events = deduplicateEvents(timeline.events);
|
||||
for(const event of events) {
|
||||
|
@ -148,14 +148,14 @@ export class SyncWriter {
|
|||
|
||||
// process live state events first, so new member info is available
|
||||
if (typeof event.state_key === "string") {
|
||||
const member = this._writeStateEvent(event, txn);
|
||||
if (member) {
|
||||
changedMembers.push(member);
|
||||
const memberChange = this._writeStateEvent(event, txn);
|
||||
if (memberChange) {
|
||||
memberChanges.set(memberChange.userId, memberChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {currentKey, changedMembers};
|
||||
return {currentKey, memberChanges};
|
||||
}
|
||||
|
||||
async _findMemberData(userId, events, txn) {
|
||||
|
@ -198,12 +198,14 @@ export class SyncWriter {
|
|||
}
|
||||
// important this happens before _writeTimeline so
|
||||
// members are available in the transaction
|
||||
const changedMembers = this._writeStateEvents(roomResponse, txn);
|
||||
const memberChanges = this._writeStateEvents(roomResponse, txn);
|
||||
const timelineResult = await this._writeTimeline(entries, timeline, currentKey, txn);
|
||||
currentKey = timelineResult.currentKey;
|
||||
changedMembers.push(...timelineResult.changedMembers);
|
||||
|
||||
return {entries, newLiveKey: currentKey, changedMembers};
|
||||
// merge member changes from state and timeline, giving precedence to the latter
|
||||
for (const [userId, memberChange] of timelineResult.memberChanges.entries()) {
|
||||
memberChanges.set(userId, memberChange);
|
||||
}
|
||||
return {entries, newLiveKey: currentKey, memberChanges};
|
||||
}
|
||||
|
||||
afterSync(newLiveKey) {
|
||||
|
|
Reference in a new issue