extract function to iterate over room response state events
This commit is contained in:
parent
d727dfd843
commit
db05338596
4 changed files with 91 additions and 43 deletions
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {reduceStateEvents} from "./RoomSummary.js";
|
||||
import {iterateResponseStateEvents} from "./common";
|
||||
import {BaseRoom} from "./BaseRoom.js";
|
||||
import {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "./members/RoomMember.js";
|
||||
|
||||
|
@ -173,15 +173,15 @@ export class ArchivedRoom extends BaseRoom {
|
|||
}
|
||||
|
||||
function findKickDetails(roomResponse, ownUserId) {
|
||||
const kickEvent = reduceStateEvents(roomResponse, (kickEvent, event) => {
|
||||
let kickEvent;
|
||||
iterateResponseStateEvents(roomResponse, event => {
|
||||
if (event.type === MEMBER_EVENT_TYPE) {
|
||||
// did we get kicked?
|
||||
if (event.state_key === ownUserId && event.sender !== event.state_key) {
|
||||
kickEvent = event;
|
||||
}
|
||||
}
|
||||
return kickEvent;
|
||||
}, null);
|
||||
});
|
||||
if (kickEvent) {
|
||||
return {
|
||||
// this is different from the room membership in the sync section, which can only be leave
|
||||
|
|
|
@ -23,6 +23,7 @@ import {WrappedError} from "../error.js"
|
|||
import {Heroes} from "./members/Heroes.js";
|
||||
import {AttachmentUpload} from "./AttachmentUpload.js";
|
||||
import {DecryptionSource} from "../e2ee/common.js";
|
||||
import {iterateResponseStateEvents} from "./common.js";
|
||||
import {PowerLevels, EVENT_TYPE as POWERLEVELS_EVENT_TYPE } from "./PowerLevels.js";
|
||||
|
||||
const EVENT_ENCRYPTED_TYPE = "m.room.encrypted";
|
||||
|
@ -179,7 +180,7 @@ export class Room extends BaseRoom {
|
|||
removedPendingEvents = await this._sendQueue.removeRemoteEchos(roomResponse.timeline.events, txn, log);
|
||||
}
|
||||
const powerLevelsEvent = this._getPowerLevelsEvent(roomResponse);
|
||||
this._updateRoomStateHandler(roomResponse, txn, log);
|
||||
this._runRoomStateHandlers(roomResponse, txn, log);
|
||||
return {
|
||||
summaryChanges,
|
||||
roomEncryption,
|
||||
|
@ -275,8 +276,13 @@ export class Room extends BaseRoom {
|
|||
}
|
||||
|
||||
_getPowerLevelsEvent(roomResponse) {
|
||||
const isPowerlevelEvent = event => event.state_key === "" && event.type === POWERLEVELS_EVENT_TYPE;
|
||||
const powerLevelEvent = roomResponse.timeline?.events.find(isPowerlevelEvent) ?? roomResponse.state?.events.find(isPowerlevelEvent);
|
||||
let powerLevelEvent;
|
||||
iterateResponseStateEvents(roomResponse, event => {
|
||||
if(event.state_key === "" && event.type === POWERLEVELS_EVENT_TYPE) {
|
||||
powerLevelEvent = event;
|
||||
}
|
||||
|
||||
});
|
||||
return powerLevelEvent;
|
||||
}
|
||||
|
||||
|
@ -445,19 +451,11 @@ export class Room extends BaseRoom {
|
|||
return this._sendQueue.pendingEvents;
|
||||
}
|
||||
|
||||
_updateRoomStateHandler(roomResponse, txn, log) {
|
||||
const stateEvents = roomResponse.state?.events;
|
||||
if (stateEvents) {
|
||||
for (let i = 0; i < stateEvents.length; i++) {
|
||||
this._roomStateHandler.handleRoomState(this, stateEvents[i], txn, log);
|
||||
}
|
||||
}
|
||||
let timelineEvents = roomResponse.timeline?.events;
|
||||
if (timelineEvents) {
|
||||
for (let i = 0; i < timelineEvents.length; i++) {
|
||||
const event = timelineEvents[i];
|
||||
if (typeof event.state_key === "string") {
|
||||
/** global room state handlers, run during write sync step */
|
||||
_runRoomStateHandlers(roomResponse, txn, log) {
|
||||
iterateResponseStateEvents(roomResponse, event => {
|
||||
this._roomStateHandler.handleRoomState(this, event, txn, log);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import {MEGOLM_ALGORITHM} from "../e2ee/common.js";
|
||||
|
||||
import {iterateResponseStateEvents} from "./common";
|
||||
|
||||
function applyTimelineEntries(data, timelineEntries, isInitialSync, canMarkUnread, ownUserId) {
|
||||
if (timelineEntries.length) {
|
||||
|
@ -27,25 +27,6 @@ function applyTimelineEntries(data, timelineEntries, isInitialSync, canMarkUnrea
|
|||
return data;
|
||||
}
|
||||
|
||||
export function reduceStateEvents(roomResponse, callback, value) {
|
||||
const stateEvents = roomResponse?.state?.events;
|
||||
// state comes before timeline
|
||||
if (Array.isArray(stateEvents)) {
|
||||
value = stateEvents.reduce(callback, value);
|
||||
}
|
||||
const timelineEvents = roomResponse?.timeline?.events;
|
||||
// and after that state events in the timeline
|
||||
if (Array.isArray(timelineEvents)) {
|
||||
value = timelineEvents.reduce((data, event) => {
|
||||
if (typeof event.state_key === "string") {
|
||||
value = callback(value, event);
|
||||
}
|
||||
return value;
|
||||
}, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function applySyncResponse(data, roomResponse, membership, ownUserId) {
|
||||
if (roomResponse.summary) {
|
||||
data = updateSummary(data, roomResponse.summary);
|
||||
|
@ -60,7 +41,9 @@ function applySyncResponse(data, roomResponse, membership, ownUserId) {
|
|||
// process state events in state and in timeline.
|
||||
// non-state events are handled by applyTimelineEntries
|
||||
// so decryption is handled properly
|
||||
data = reduceStateEvents(roomResponse, (data, event) => processStateEvent(data, event, ownUserId), data);
|
||||
iterateResponseStateEvents(roomResponse, event => {
|
||||
data = processStateEvent(data, event, ownUserId);
|
||||
});
|
||||
const unreadNotifications = roomResponse.unread_notifications;
|
||||
if (unreadNotifications) {
|
||||
data = processNotificationCounts(data, unreadNotifications);
|
||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import type {Room} from "./Room";
|
||||
import type {StateEvent} from "../storage/types";
|
||||
import type {StateEvent, TimelineEvent} from "../storage/types";
|
||||
import type {Transaction} from "../storage/idb/Transaction";
|
||||
import type {ILogItem} from "../../logging/types";
|
||||
import type {MemberChange} from "./members/RoomMember";
|
||||
|
@ -50,4 +50,71 @@ export enum RoomType {
|
|||
export interface RoomStateHandler {
|
||||
handleRoomState(room: Room, stateEvent: StateEvent, txn: Transaction, log: ILogItem);
|
||||
updateRoomMembers(room: Room, memberChanges: Map<string, MemberChange>);
|
||||
type RoomResponse = {
|
||||
state?: {
|
||||
events?: Array<StateEvent>
|
||||
},
|
||||
timeline?: {
|
||||
events?: Array<StateEvent>
|
||||
}
|
||||
}
|
||||
|
||||
/** iterates over any state events in a sync room response, in the order that they should be applied (from older to younger events) */
|
||||
export function iterateResponseStateEvents(roomResponse: RoomResponse, callback: (StateEvent) => void) {
|
||||
// first iterate over state events, they precede the timeline
|
||||
const stateEvents = roomResponse.state?.events;
|
||||
if (stateEvents) {
|
||||
for (let i = 0; i < stateEvents.length; i++) {
|
||||
callback(stateEvents[i]);
|
||||
}
|
||||
}
|
||||
// now see if there are any state events within the timeline
|
||||
let timelineEvents = roomResponse.timeline?.events;
|
||||
if (timelineEvents) {
|
||||
for (let i = 0; i < timelineEvents.length; i++) {
|
||||
const event = timelineEvents[i];
|
||||
if (typeof event.state_key === "string") {
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function tests() {
|
||||
return {
|
||||
"test iterateResponseStateEvents with both state and timeline sections": assert => {
|
||||
const roomResponse = {
|
||||
state: {
|
||||
events: [
|
||||
{type: "m.room.member", state_key: "1"},
|
||||
{type: "m.room.member", state_key: "2", content: {a: 1}},
|
||||
]
|
||||
},
|
||||
timeline: {
|
||||
events: [
|
||||
{type: "m.room.message"},
|
||||
{type: "m.room.member", state_key: "3"},
|
||||
{type: "m.room.message"},
|
||||
{type: "m.room.member", state_key: "2", content: {a: 2}},
|
||||
]
|
||||
}
|
||||
} as unknown as RoomResponse;
|
||||
const expectedStateKeys = ["1", "2", "3", "2"];
|
||||
const expectedAForMember2 = [1, 2];
|
||||
iterateResponseStateEvents(roomResponse, event => {
|
||||
assert.strictEqual(event.type, "m.room.member");
|
||||
assert.strictEqual(expectedStateKeys.shift(), event.state_key);
|
||||
if (event.state_key === "2") {
|
||||
assert.strictEqual(expectedAForMember2.shift(), event.content.a);
|
||||
}
|
||||
});
|
||||
assert.strictEqual(expectedStateKeys.length, 0);
|
||||
assert.strictEqual(expectedAForMember2.length, 0);
|
||||
},
|
||||
"test iterateResponseStateEvents with empty response": assert => {
|
||||
iterateResponseStateEvents({}, () => {
|
||||
assert.fail("no events expected");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue