add session.observeRoomState to observe state changes in all rooms

and use it for calls
this won't be called for state already received and stored in storage,
that you need to still do yourself
This commit is contained in:
Bruno Windels 2022-05-12 11:58:28 +02:00
parent ec1568cf1c
commit d727dfd843
6 changed files with 86 additions and 28 deletions

View file

@ -15,11 +15,19 @@ limitations under the License.
*/
export {Logger} from "./logging/Logger";
export type {ILogItem} from "./logging/types";
export {IDBLogPersister} from "./logging/IDBLogPersister";
export {ConsoleReporter} from "./logging/ConsoleReporter";
export {Platform} from "./platform/web/Platform.js";
export {Client, LoadStatus} from "./matrix/Client.js";
export {RoomStatus} from "./matrix/room/common";
// export everything needed to observe state events on all rooms using session.observeRoomState
export type {RoomStateHandler} from "./matrix/room/common";
export type {MemberChange} from "./matrix/room/members/RoomMember";
export type {Transaction} from "./matrix/storage/idb/Transaction";
export type {Room} from "./matrix/room/Room";
export type {StateEvent} from "./matrix/storage/types";
// export main view & view models
export {createNavigation, createRouter} from "./domain/navigation/index.js";
export {RootViewModel} from "./domain/RootViewModel.js";

View file

@ -0,0 +1,37 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import type {ILogItem} from "../logging/types";
import type {StateEvent} from "./storage/types";
import type {Transaction} from "./storage/idb/Transaction";
import type {Room} from "./room/Room";
import type {MemberChange} from "./room/members/RoomMember";
import type {RoomStateHandler} from "./room/common";
import {BaseObservable} from "../observable/BaseObservable";
/** keeps track of all handlers registered with Session.observeRoomState */
export class RoomStateHandlerSet extends BaseObservable<RoomStateHandler> implements RoomStateHandler {
handleRoomState(room: Room, stateEvent: StateEvent, txn: Transaction, log: ILogItem) {
for(let h of this._handlers) {
h.handleRoomState(room, stateEvent, txn, log);
}
}
updateRoomMembers(room: Room, memberChanges: Map<string, MemberChange>) {
for(let h of this._handlers) {
h.updateRoomMembers(room, memberChanges);
}
}
}

View file

@ -48,6 +48,7 @@ import {SecretStorage} from "./ssss/SecretStorage";
import {ObservableValue} from "../observable/value/ObservableValue";
import {RetainedObservableValue} from "../observable/value/RetainedObservableValue";
import {CallHandler} from "./calls/CallHandler";
import {RoomStateHandlerSet} from "./RoomStateHandlerSet";
const PICKLE_KEY = "DEFAULT_KEY";
const PUSHER_KEY = "pusher";
@ -101,6 +102,8 @@ export class Session {
}],
forceTURN: false,
});
this._roomStateHandler = new RoomStateHandlerSet();
this.observeRoomState(this._callHandler);
this._deviceMessageHandler = new DeviceMessageHandler({storage, callHandler: this._callHandler});
this._olm = olm;
this._olmUtil = null;
@ -595,7 +598,7 @@ export class Session {
user: this._user,
createRoomEncryption: this._createRoomEncryption,
platform: this._platform,
callHandler: this._callHandler
roomStateHandler: this._roomStateHandler
});
}
@ -937,6 +940,10 @@ export class Session {
return observable;
}
observeRoomState(roomStateHandler) {
return this._roomStateHandler.subscribe(roomStateHandler);
}
/**
Creates an empty (summary isn't loaded) the archived room if it isn't
loaded already, assuming sync will either remove it (when rejoining) or

View file

@ -35,6 +35,7 @@ import type {Options as GroupCallOptions} from "./group/GroupCall";
import type {Transaction} from "../storage/idb/Transaction";
import type {CallEntry} from "../storage/idb/stores/CallStore";
import type {Clock} from "../../platform/web/dom/Clock";
import type {RoomStateHandler} from "../room/common";
export type Options = Omit<GroupCallOptions, "emitUpdate" | "createTimeout"> & {
clock: Clock
@ -44,7 +45,7 @@ function getRoomMemberKey(roomId: string, userId: string): string {
return JSON.stringify(roomId)+`,`+JSON.stringify(userId);
}
export class CallHandler {
export class CallHandler implements RoomStateHandler {
// group calls by call id
private readonly _calls: ObservableMap<string, GroupCall> = new ObservableMap<string, GroupCall>();
// map of `"roomId","userId"` to set of conf_id's they are in
@ -143,18 +144,12 @@ export class CallHandler {
// TODO: check and poll turn server credentials here
/** @internal */
handleRoomState(room: Room, events: StateEvent[], txn: Transaction, log: ILogItem) {
// first update call events
for (const event of events) {
if (event.type === EventType.GroupCall) {
this.handleCallEvent(event, room.id, txn, log);
}
handleRoomState(room: Room, event: StateEvent, txn: Transaction, log: ILogItem) {
if (event.type === EventType.GroupCall) {
this.handleCallEvent(event, room.id, txn, log);
}
// then update members
for (const event of events) {
if (event.type === EventType.GroupCallMember) {
this.handleCallMemberEvent(event, room.id, log);
}
if (event.type === EventType.GroupCallMember) {
this.handleCallMemberEvent(event, room.id, log);
}
}

View file

@ -30,7 +30,7 @@ const EVENT_ENCRYPTED_TYPE = "m.room.encrypted";
export class Room extends BaseRoom {
constructor(options) {
super(options);
this._callHandler = options.callHandler;
this._roomStateHandler = options.roomStateHandler;
// TODO: pass pendingEvents to start like pendingOperations?
const {pendingEvents} = options;
const relationWriter = new RelationWriter({
@ -179,7 +179,7 @@ export class Room extends BaseRoom {
removedPendingEvents = await this._sendQueue.removeRemoteEchos(roomResponse.timeline.events, txn, log);
}
const powerLevelsEvent = this._getPowerLevelsEvent(roomResponse);
this._updateCallHandler(roomResponse, txn, log);
this._updateRoomStateHandler(roomResponse, txn, log);
return {
summaryChanges,
roomEncryption,
@ -217,9 +217,7 @@ export class Room extends BaseRoom {
if (this._memberList) {
this._memberList.afterSync(memberChanges);
}
if (this._callHandler) {
this._callHandler.updateRoomMembers(this, memberChanges);
}
this._roomStateHandler.updateRoomMembers(this, memberChanges);
if (this._observedMembers) {
this._updateObservedMembers(memberChanges);
}
@ -447,17 +445,19 @@ export class Room extends BaseRoom {
return this._sendQueue.pendingEvents;
}
_updateCallHandler(roomResponse, txn, log) {
if (this._callHandler) {
const stateEvents = roomResponse.state?.events;
if (stateEvents?.length) {
this._callHandler.handleRoomState(this, stateEvents, txn, log);
_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) {
const timelineStateEvents = timelineEvents.filter(e => typeof e.state_key === "string");
if (timelineEvents.length !== 0) {
this._callHandler.handleRoomState(this, timelineStateEvents, 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") {
this._roomStateHandler.handleRoomState(this, event, txn, log);
}
}
}

View file

@ -14,6 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import type {Room} from "./Room";
import type {StateEvent} from "../storage/types";
import type {Transaction} from "../storage/idb/Transaction";
import type {ILogItem} from "../../logging/types";
import type {MemberChange} from "./members/RoomMember";
export function getPrevContentFromStateEvent(event) {
// where to look for prev_content is a bit of a mess,
// see https://matrix.to/#/!NasysSDfxKxZBzJJoE:matrix.org/$DvrAbZJiILkOmOIuRsNoHmh2v7UO5CWp_rYhlGk34fQ?via=matrix.org&via=pixie.town&via=amorgan.xyz
@ -40,3 +46,8 @@ export enum RoomType {
Private,
Public
}
export interface RoomStateHandler {
handleRoomState(room: Room, stateEvent: StateEvent, txn: Transaction, log: ILogItem);
updateRoomMembers(room: Room, memberChanges: Map<string, MemberChange>);
}