diff --git a/src/client.ts b/src/client.ts index 47c21127648..0a82be50158 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1321,6 +1321,7 @@ export class MatrixClient extends TypedEventEmitter User.withReEmitterOnClient(userId, this)); this.deviceId = opts.deviceId || null; this.sessionId = randomString(10); diff --git a/src/models/user.ts b/src/models/user.ts index 054a17438f2..33a953f63df 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { MatrixClient } from "../matrix"; import { MatrixEvent } from "./event"; import { TypedEventEmitter } from "./typed-event-emitter"; @@ -160,6 +161,25 @@ export class User extends TypedEventEmitter { this.updateModifiedTime(); } + /** + * Construct a new User whose events will also emit on MatrixClient. + * A User must have an ID and can optionally have extra information associated with it. + * @param userId - Required. The ID of this user. + * @param client - An instance of MatrixClient object + * @returns User object with reEmitter setup on client + */ + public static withReEmitterOnClient(userId: string, client: MatrixClient): User { + const user = new User(userId); + client.reEmitter.reEmit(user, [ + UserEvent.AvatarUrl, + UserEvent.DisplayName, + UserEvent.Presence, + UserEvent.CurrentlyActive, + UserEvent.LastPresenceTs, + ]); + return user; + } + /** * Update this User with the given presence event. May fire "User.presence", * "User.avatarUrl" and/or "User.displayName" if this event updates this user's diff --git a/src/store/index.ts b/src/store/index.ts index ac0a344e852..fbb66b006ec 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -32,6 +32,8 @@ export interface ISavedSync { accountData: IMinimalEvent[]; } +export type UserCreator = (userId: string) => User; + /** * A store for most of the data js-sdk needs to store, apart from crypto data */ @@ -62,6 +64,12 @@ export interface IStore { */ storeRoom(room: Room): void; + /** + * Set the user creator which is used for creating User objects + * @param creator - A callback that accepts an user-id and returns an User object + */ + setUserCreator(creator: UserCreator): void; + /** * Retrieve a room by its' room ID. * @param roomId - The room ID. diff --git a/src/store/indexeddb.ts b/src/store/indexeddb.ts index 3ca9ea2dca4..33453b25db4 100644 --- a/src/store/indexeddb.ts +++ b/src/store/indexeddb.ts @@ -19,7 +19,6 @@ limitations under the License. import { MemoryStore, IOpts as IBaseOpts } from "./memory"; import { LocalIndexedDBStoreBackend } from "./indexeddb-local-backend"; import { RemoteIndexedDBStoreBackend } from "./indexeddb-remote-backend"; -import { User } from "../models/user"; import { IEvent, MatrixEvent } from "../models/event"; import { logger } from "../logger"; import { ISavedSync } from "./index"; @@ -140,7 +139,10 @@ export class IndexedDBStore extends MemoryStore { .then((userPresenceEvents) => { logger.log(`IndexedDBStore.startup: processing presence events`); userPresenceEvents.forEach(([userId, rawEvent]) => { - const u = new User(userId); + if (!this.createUser) { + throw new Error("createUser is undefined, it should be set with setUserCreator()!"); + } + const u = this.createUser(userId); if (rawEvent) { u.setPresenceEvent(new MatrixEvent(rawEvent)); } diff --git a/src/store/memory.ts b/src/store/memory.ts index 8b560784622..8eeb53b85b2 100644 --- a/src/store/memory.ts +++ b/src/store/memory.ts @@ -25,7 +25,7 @@ import { IEvent, MatrixEvent } from "../models/event"; import { RoomState, RoomStateEvent } from "../models/room-state"; import { RoomMember } from "../models/room-member"; import { Filter } from "../filter"; -import { ISavedSync, IStore } from "./index"; +import { ISavedSync, IStore, UserCreator } from "./index"; import { RoomSummary } from "../models/room-summary"; import { ISyncResponse } from "../sync-accumulator"; import { IStateEventWithRoomId } from "../@types/search"; @@ -63,6 +63,7 @@ export class MemoryStore implements IStore { private clientOptions?: IStoredClientOpts; private pendingToDeviceBatches: IndexedToDeviceBatch[] = []; private nextToDeviceBatchId = 0; + protected createUser?: UserCreator; /** * Construct a new in-memory data store for the Matrix Client. @@ -108,6 +109,10 @@ export class MemoryStore implements IStore { }); } + public setUserCreator(creator: UserCreator): void { + this.createUser = creator; + } + /** * Called when a room member in a room being tracked by this store has been * updated. @@ -119,7 +124,7 @@ export class MemoryStore implements IStore { return; } - const user = this.users[member.userId] || new User(member.userId); + const user = this.users[member.userId] || this.createUser?.(member.userId); if (member.name) { user.setDisplayName(member.name); if (member.events.member) { diff --git a/src/store/stub.ts b/src/store/stub.ts index 5ea91cf98cc..c320d78b153 100644 --- a/src/store/stub.ts +++ b/src/store/stub.ts @@ -23,7 +23,7 @@ import { Room } from "../models/room"; import { User } from "../models/user"; import { IEvent, MatrixEvent } from "../models/event"; import { Filter } from "../filter"; -import { ISavedSync, IStore } from "./index"; +import { ISavedSync, IStore, UserCreator } from "./index"; import { RoomSummary } from "../models/room-summary"; import { ISyncResponse } from "../sync-accumulator"; import { IStateEventWithRoomId } from "../@types/search"; @@ -117,6 +117,13 @@ export class StubStore implements IStore { return []; } + /** + * No-op. + */ + public setUserCreator(creator: UserCreator): void { + return; + } + /** * Store events for a room. * @param room - The room to store events for. diff --git a/src/sync.ts b/src/sync.ts index 84b6255188b..06b8b3a3b93 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -26,7 +26,7 @@ limitations under the License. import { Optional } from "matrix-events-sdk"; import type { SyncCryptoCallbacks } from "./common-crypto/CryptoBackend"; -import { User, UserEvent } from "./models/user"; +import { User } from "./models/user"; import { NotificationCountType, Room, RoomEvent } from "./models/room"; import { deepCopy, defer, IDeferred, noUnsafeEventProps, promiseMapSeries, unsafeProp } from "./utils"; import { Filter } from "./filter"; @@ -431,7 +431,7 @@ export class SyncApi { if (user) { user.setPresenceEvent(presenceEvent); } else { - user = createNewUser(client, presenceEvent.getContent().user_id); + user = User.withReEmitterOnClient(presenceEvent.getContent().user_id, client); user.setPresenceEvent(presenceEvent); client.store.storeUser(user); } @@ -530,7 +530,7 @@ export class SyncApi { if (user) { user.setPresenceEvent(presenceEvent); } else { - user = createNewUser(this.client, presenceEvent.getContent().user_id); + user = User.withReEmitterOnClient(presenceEvent.getContent().user_id, this.client); user.setPresenceEvent(presenceEvent); this.client.store.storeUser(user); } @@ -1150,7 +1150,7 @@ export class SyncApi { if (user) { user.setPresenceEvent(presenceEvent); } else { - user = createNewUser(client, presenceEvent.getSender()!); + user = User.withReEmitterOnClient(presenceEvent.getSender()!, client); user.setPresenceEvent(presenceEvent); client.store.storeUser(user); } @@ -1893,18 +1893,6 @@ export class SyncApi { }; } -function createNewUser(client: MatrixClient, userId: string): User { - const user = new User(userId); - client.reEmitter.reEmit(user, [ - UserEvent.AvatarUrl, - UserEvent.DisplayName, - UserEvent.Presence, - UserEvent.CurrentlyActive, - UserEvent.LastPresenceTs, - ]); - return user; -} - // /!\ This function is not intended for public use! It's only exported from // here in order to share some common logic with sliding-sync-sdk.ts. export function _createAndReEmitRoom(client: MatrixClient, roomId: string, opts: Partial): Room {