Improve types of sticky rooms (#10078)

pull/28217/head
Andy Balaam 2023-02-09 10:45:11 +00:00 committed by GitHub
parent 47d9b63cfe
commit a068b1e940
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 36 additions and 18 deletions

View File

@ -62,12 +62,12 @@ interface IStickyRoom {
*/ */
export class Algorithm extends EventEmitter { export class Algorithm extends EventEmitter {
private _cachedRooms: ITagMap = {}; private _cachedRooms: ITagMap = {};
private _cachedStickyRooms: ITagMap = {}; // a clone of the _cachedRooms, with the sticky room private _cachedStickyRooms: ITagMap | null = {}; // a clone of the _cachedRooms, with the sticky room
private _stickyRoom: IStickyRoom = null; private _stickyRoom: IStickyRoom | null = null;
private _lastStickyRoom: IStickyRoom = null; // only not-null when changing the sticky room private _lastStickyRoom: IStickyRoom | null = null; // only not-null when changing the sticky room
private sortAlgorithms: ITagSortingMap; private sortAlgorithms: ITagSortingMap | null = null;
private listAlgorithms: IListOrderingMap; private listAlgorithms: IListOrderingMap | null = null;
private algorithms: IOrderingAlgorithmMap; private algorithms: IOrderingAlgorithmMap | null = null;
private rooms: Room[] = []; private rooms: Room[] = [];
private roomIdsToTags: { private roomIdsToTags: {
[roomId: string]: TagID[]; [roomId: string]: TagID[];
@ -86,7 +86,7 @@ export class Algorithm extends EventEmitter {
CallStore.instance.off(CallStoreEvent.ActiveCalls, this.onActiveCalls); CallStore.instance.off(CallStoreEvent.ActiveCalls, this.onActiveCalls);
} }
public get stickyRoom(): Room { public get stickyRoom(): Room | null {
return this._stickyRoom ? this._stickyRoom.room : null; return this._stickyRoom ? this._stickyRoom.room : null;
} }
@ -124,7 +124,7 @@ export class Algorithm extends EventEmitter {
} }
} }
public getTagSorting(tagId: TagID): SortAlgorithm { public getTagSorting(tagId: TagID): SortAlgorithm | null {
if (!this.sortAlgorithms) return null; if (!this.sortAlgorithms) return null;
return this.sortAlgorithms[tagId]; return this.sortAlgorithms[tagId];
} }
@ -132,6 +132,8 @@ export class Algorithm extends EventEmitter {
public setTagSorting(tagId: TagID, sort: SortAlgorithm): void { public setTagSorting(tagId: TagID, sort: SortAlgorithm): void {
if (!tagId) throw new Error("Tag ID must be defined"); if (!tagId) throw new Error("Tag ID must be defined");
if (!sort) throw new Error("Algorithm must be defined"); if (!sort) throw new Error("Algorithm must be defined");
if (!this.sortAlgorithms) throw new Error("this.sortAlgorithms must be defined before calling setTagSorting");
if (!this.algorithms) throw new Error("this.algorithms must be defined before calling setTagSorting");
this.sortAlgorithms[tagId] = sort; this.sortAlgorithms[tagId] = sort;
const algorithm: OrderingAlgorithm = this.algorithms[tagId]; const algorithm: OrderingAlgorithm = this.algorithms[tagId];
@ -141,7 +143,7 @@ export class Algorithm extends EventEmitter {
this.recalculateActiveCallRooms(tagId); this.recalculateActiveCallRooms(tagId);
} }
public getListOrdering(tagId: TagID): ListAlgorithm { public getListOrdering(tagId: TagID): ListAlgorithm | null {
if (!this.listAlgorithms) return null; if (!this.listAlgorithms) return null;
return this.listAlgorithms[tagId]; return this.listAlgorithms[tagId];
} }
@ -149,6 +151,9 @@ export class Algorithm extends EventEmitter {
public setListOrdering(tagId: TagID, order: ListAlgorithm): void { public setListOrdering(tagId: TagID, order: ListAlgorithm): void {
if (!tagId) throw new Error("Tag ID must be defined"); if (!tagId) throw new Error("Tag ID must be defined");
if (!order) throw new Error("Algorithm must be defined"); if (!order) throw new Error("Algorithm must be defined");
if (!this.sortAlgorithms) throw new Error("this.sortAlgorithms must be defined before calling setListOrdering");
if (!this.listAlgorithms) throw new Error("this.listAlgorithms must be defined before calling setListOrdering");
if (!this.algorithms) throw new Error("this.algorithms must be defined before calling setListOrdering");
this.listAlgorithms[tagId] = order; this.listAlgorithms[tagId] = order;
const algorithm = getListAlgorithmInstance(order, tagId, this.sortAlgorithms[tagId]); const algorithm = getListAlgorithmInstance(order, tagId, this.sortAlgorithms[tagId]);
@ -160,12 +165,12 @@ export class Algorithm extends EventEmitter {
this.recalculateActiveCallRooms(tagId); this.recalculateActiveCallRooms(tagId);
} }
private updateStickyRoom(val: Room): void { private updateStickyRoom(val: Room | null): void {
this.doUpdateStickyRoom(val); this.doUpdateStickyRoom(val);
this._lastStickyRoom = null; // clear to indicate we're done changing this._lastStickyRoom = null; // clear to indicate we're done changing
} }
private doUpdateStickyRoom(val: Room): void { private doUpdateStickyRoom(val: Room | null): void {
if (val?.isSpaceRoom() && val.getMyMembership() !== "invite") { if (val?.isSpaceRoom() && val.getMyMembership() !== "invite") {
// no-op sticky rooms for spaces - they're effectively virtual rooms // no-op sticky rooms for spaces - they're effectively virtual rooms
val = null; val = null;
@ -237,6 +242,10 @@ export class Algorithm extends EventEmitter {
// Lie to the algorithm and remove the room from it's field of view // Lie to the algorithm and remove the room from it's field of view
this.handleRoomUpdate(val, RoomUpdateCause.RoomRemoved); this.handleRoomUpdate(val, RoomUpdateCause.RoomRemoved);
// handleRoomUpdate may have modified this._stickyRoom. Convince the
// compiler of this fact.
this._stickyRoom = this.stickyRoomMightBeModified();
// Check for tag & position changes while we're here. We also check the room to ensure // Check for tag & position changes while we're here. We also check the room to ensure
// it is still the same room. // it is still the same room.
if (this._stickyRoom) { if (this._stickyRoom) {
@ -284,6 +293,13 @@ export class Algorithm extends EventEmitter {
this.emit(LIST_UPDATED_EVENT); this.emit(LIST_UPDATED_EVENT);
} }
/**
* Hack to prevent Typescript claiming this._stickyRoom is always null.
*/
private stickyRoomMightBeModified(): IStickyRoom | null {
return this._stickyRoom;
}
private onActiveCalls = (): void => { private onActiveCalls = (): void => {
// In case we're unsticking a room, sort it back into natural order // In case we're unsticking a room, sort it back into natural order
this.recalculateStickyRoom(); this.recalculateStickyRoom();
@ -310,7 +326,7 @@ export class Algorithm extends EventEmitter {
* the call. * the call.
* @param updatedTag The tag that was updated, if possible. * @param updatedTag The tag that was updated, if possible.
*/ */
protected recalculateStickyRoom(updatedTag: TagID = null): void { protected recalculateStickyRoom(updatedTag: TagID | null = null): void {
// 🐉 Here be dragons. // 🐉 Here be dragons.
// This function does far too much for what it should, and is called by many places. // This function does far too much for what it should, and is called by many places.
// Not only is this responsible for ensuring the sticky room is held in place at all // Not only is this responsible for ensuring the sticky room is held in place at all
@ -336,14 +352,16 @@ export class Algorithm extends EventEmitter {
if (updatedTag) { if (updatedTag) {
// Update the tag indicated by the caller, if possible. This is mostly to ensure // Update the tag indicated by the caller, if possible. This is mostly to ensure
// our cache is up to date. // our cache is up to date.
this._cachedStickyRooms[updatedTag] = [...this.cachedRooms[updatedTag]]; // shallow clone if (this._cachedStickyRooms) {
this._cachedStickyRooms[updatedTag] = [...this.cachedRooms[updatedTag]]; // shallow clone
}
} }
// Now try to insert the sticky room, if we need to. // Now try to insert the sticky room, if we need to.
// We need to if there's no updated tag (we regenned the whole cache) or if the tag // We need to if there's no updated tag (we regenned the whole cache) or if the tag
// we might have updated from the cache is also our sticky room. // we might have updated from the cache is also our sticky room.
const sticky = this._stickyRoom; const sticky = this._stickyRoom;
if (!updatedTag || updatedTag === sticky.tag) { if (sticky && (!updatedTag || updatedTag === sticky.tag) && this._cachedStickyRooms) {
this._cachedStickyRooms[sticky.tag].splice(sticky.position, 0, sticky.room); this._cachedStickyRooms[sticky.tag].splice(sticky.position, 0, sticky.room);
} }
@ -362,7 +380,7 @@ export class Algorithm extends EventEmitter {
* *
* @param updatedTag The tag that was updated, if possible. * @param updatedTag The tag that was updated, if possible.
*/ */
protected recalculateActiveCallRooms(updatedTag: TagID = null): void { protected recalculateActiveCallRooms(updatedTag: TagID | null = null): void {
if (!updatedTag) { if (!updatedTag) {
// Assume all tags need updating // Assume all tags need updating
// We're not modifying the map here, so can safely rely on the cached values // We're not modifying the map here, so can safely rely on the cached values
@ -379,7 +397,7 @@ export class Algorithm extends EventEmitter {
if (CallStore.instance.activeCalls.size) { if (CallStore.instance.activeCalls.size) {
// We operate on the sticky rooms map // We operate on the sticky rooms map
if (!this._cachedStickyRooms) this.initCachedStickyRooms(); if (!this._cachedStickyRooms) this.initCachedStickyRooms();
const rooms = this._cachedStickyRooms[updatedTag]; const rooms = this._cachedStickyRooms![updatedTag];
const activeRoomIds = new Set([...CallStore.instance.activeCalls].map((call) => call.roomId)); const activeRoomIds = new Set([...CallStore.instance.activeCalls].map((call) => call.roomId));
const activeRooms: Room[] = []; const activeRooms: Room[] = [];
@ -390,7 +408,7 @@ export class Algorithm extends EventEmitter {
} }
// Stick rooms with active calls to the top // Stick rooms with active calls to the top
this._cachedStickyRooms[updatedTag] = [...activeRooms, ...inactiveRooms]; this._cachedStickyRooms![updatedTag] = [...activeRooms, ...inactiveRooms];
} }
} }
@ -638,7 +656,7 @@ export class Algorithm extends EventEmitter {
} }
// Like above, update the reference to the sticky room if we need to // Like above, update the reference to the sticky room if we need to
if (hasTags && isSticky) { if (hasTags && isSticky && this._stickyRoom) {
// Go directly in and set the sticky room's new reference, being careful not // Go directly in and set the sticky room's new reference, being careful not
// to trigger a sticky room update ourselves. // to trigger a sticky room update ourselves.
this._stickyRoom.room = room; this._stickyRoom.room = room;