mirror of https://github.com/vector-im/riot-web
				
				
				
			Fix issue with room duplication caused by filtering and selecting room using keyboard
Wrap sticky room updates in lock to prevent setStickyRoom running in middle of setKnownRoomspull/21833/head
							parent
							
								
									376533e709
								
							
						
					
					
						commit
						32cc48ff7a
					
				|  | @ -16,8 +16,10 @@ limitations under the License. | |||
| 
 | ||||
| import { Room } from "matrix-js-sdk/src/models/room"; | ||||
| import { isNullOrUndefined } from "matrix-js-sdk/src/utils"; | ||||
| import DMRoomMap from "../../../utils/DMRoomMap"; | ||||
| import { EventEmitter } from "events"; | ||||
| import AwaitLock from "await-lock"; | ||||
| 
 | ||||
| import DMRoomMap from "../../../utils/DMRoomMap"; | ||||
| import { arrayDiff, arrayHasDiff } from "../../../utils/arrays"; | ||||
| import { DefaultTagID, RoomUpdateCause, TagID } from "../models"; | ||||
| import { | ||||
|  | @ -78,6 +80,7 @@ export class Algorithm extends EventEmitter { | |||
|     } = {}; | ||||
|     private allowedByFilter: Map<IFilterCondition, Room[]> = new Map<IFilterCondition, Room[]>(); | ||||
|     private allowedRoomsByFilters: Set<Room> = new Set<Room>(); | ||||
|     private stickyLock = new AwaitLock(); | ||||
| 
 | ||||
|     /** | ||||
|      * Set to true to suspend emissions of algorithm updates. | ||||
|  | @ -123,7 +126,12 @@ export class Algorithm extends EventEmitter { | |||
|      * @param val The new room to sticky. | ||||
|      */ | ||||
|     public async setStickyRoom(val: Room) { | ||||
|         await this.updateStickyRoom(val); | ||||
|         await this.stickyLock.acquireAsync(); | ||||
|         try { | ||||
|             await this.updateStickyRoom(val); | ||||
|         } finally { | ||||
|             this.stickyLock.release(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public getTagSorting(tagId: TagID): SortAlgorithm { | ||||
|  | @ -519,82 +527,87 @@ export class Algorithm extends EventEmitter { | |||
|         if (isNullOrUndefined(rooms)) throw new Error(`Array of rooms cannot be null`); | ||||
|         if (!this.sortAlgorithms) throw new Error(`Cannot set known rooms without a tag sorting map`); | ||||
| 
 | ||||
|         if (!this.updatesInhibited) { | ||||
|             // We only log this if we're expecting to be publishing updates, which means that
 | ||||
|             // this could be an unexpected invocation. If we're inhibited, then this is probably
 | ||||
|             // an intentional invocation.
 | ||||
|             console.warn("Resetting known rooms, initiating regeneration"); | ||||
|         } | ||||
|         await this.stickyLock.acquireAsync(); | ||||
|         try { | ||||
|             if (!this.updatesInhibited) { | ||||
|                 // We only log this if we're expecting to be publishing updates, which means that
 | ||||
|                 // this could be an unexpected invocation. If we're inhibited, then this is probably
 | ||||
|                 // an intentional invocation.
 | ||||
|                 console.warn("Resetting known rooms, initiating regeneration"); | ||||
|             } | ||||
| 
 | ||||
|         // Before we go any further we need to clear (but remember) the sticky room to
 | ||||
|         // avoid accidentally duplicating it in the list.
 | ||||
|         const oldStickyRoom = this._stickyRoom; | ||||
|         await this.updateStickyRoom(null); | ||||
|             // Before we go any further we need to clear (but remember) the sticky room to
 | ||||
|             // avoid accidentally duplicating it in the list.
 | ||||
|             const oldStickyRoom = this._stickyRoom; | ||||
|             if (oldStickyRoom) await this.updateStickyRoom(null); | ||||
| 
 | ||||
|         this.rooms = rooms; | ||||
|             this.rooms = rooms; | ||||
| 
 | ||||
|         const newTags: ITagMap = {}; | ||||
|         for (const tagId in this.sortAlgorithms) { | ||||
|             // noinspection JSUnfilteredForInLoop
 | ||||
|             newTags[tagId] = []; | ||||
|         } | ||||
|             const newTags: ITagMap = {}; | ||||
|             for (const tagId in this.sortAlgorithms) { | ||||
|                 // noinspection JSUnfilteredForInLoop
 | ||||
|                 newTags[tagId] = []; | ||||
|             } | ||||
| 
 | ||||
|         // If we can avoid doing work, do so.
 | ||||
|         if (!rooms.length) { | ||||
|             await this.generateFreshTags(newTags); // just in case it wants to do something
 | ||||
|             this.cachedRooms = newTags; | ||||
|             return; | ||||
|         } | ||||
|             // If we can avoid doing work, do so.
 | ||||
|             if (!rooms.length) { | ||||
|                 await this.generateFreshTags(newTags); // just in case it wants to do something
 | ||||
|                 this.cachedRooms = newTags; | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|         // Split out the easy rooms first (leave and invite)
 | ||||
|         const memberships = splitRoomsByMembership(rooms); | ||||
|         for (const room of memberships[EffectiveMembership.Invite]) { | ||||
|             newTags[DefaultTagID.Invite].push(room); | ||||
|         } | ||||
|         for (const room of memberships[EffectiveMembership.Leave]) { | ||||
|             newTags[DefaultTagID.Archived].push(room); | ||||
|         } | ||||
|             // Split out the easy rooms first (leave and invite)
 | ||||
|             const memberships = splitRoomsByMembership(rooms); | ||||
|             for (const room of memberships[EffectiveMembership.Invite]) { | ||||
|                 newTags[DefaultTagID.Invite].push(room); | ||||
|             } | ||||
|             for (const room of memberships[EffectiveMembership.Leave]) { | ||||
|                 newTags[DefaultTagID.Archived].push(room); | ||||
|             } | ||||
| 
 | ||||
|         // Now process all the joined rooms. This is a bit more complicated
 | ||||
|         for (const room of memberships[EffectiveMembership.Join]) { | ||||
|             const tags = this.getTagsOfJoinedRoom(room); | ||||
|             // Now process all the joined rooms. This is a bit more complicated
 | ||||
|             for (const room of memberships[EffectiveMembership.Join]) { | ||||
|                 const tags = this.getTagsOfJoinedRoom(room); | ||||
| 
 | ||||
|             let inTag = false; | ||||
|             if (tags.length > 0) { | ||||
|                 for (const tag of tags) { | ||||
|                     if (!isNullOrUndefined(newTags[tag])) { | ||||
|                         newTags[tag].push(room); | ||||
|                         inTag = true; | ||||
|                 let inTag = false; | ||||
|                 if (tags.length > 0) { | ||||
|                     for (const tag of tags) { | ||||
|                         if (!isNullOrUndefined(newTags[tag])) { | ||||
|                             newTags[tag].push(room); | ||||
|                             inTag = true; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if (!inTag) { | ||||
|                     if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) { | ||||
|                         newTags[DefaultTagID.DM].push(room); | ||||
|                     } else { | ||||
|                         newTags[DefaultTagID.Untagged].push(room); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (!inTag) { | ||||
|                 if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) { | ||||
|                     newTags[DefaultTagID.DM].push(room); | ||||
|                 } else { | ||||
|                     newTags[DefaultTagID.Untagged].push(room); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         await this.generateFreshTags(newTags); | ||||
| 
 | ||||
|         this.cachedRooms = newTags; // this recalculates the filtered rooms for us
 | ||||
|         this.updateTagsFromCache(); | ||||
| 
 | ||||
|         // Now that we've finished generation, we need to update the sticky room to what
 | ||||
|         // it was. It's entirely possible that it changed lists though, so if it did then
 | ||||
|         // we also have to update the position of it.
 | ||||
|         if (oldStickyRoom && oldStickyRoom.room) { | ||||
|             await this.updateStickyRoom(oldStickyRoom.room); | ||||
|             if (this._stickyRoom && this._stickyRoom.room) { // just in case the update doesn't go according to plan
 | ||||
|                 if (this._stickyRoom.tag !== oldStickyRoom.tag) { | ||||
|                     // We put the sticky room at the top of the list to treat it as an obvious tag change.
 | ||||
|                     this._stickyRoom.position = 0; | ||||
|                     this.recalculateStickyRoom(this._stickyRoom.tag); | ||||
|             await this.generateFreshTags(newTags); | ||||
| 
 | ||||
|             this.cachedRooms = newTags; // this recalculates the filtered rooms for us
 | ||||
|             this.updateTagsFromCache(); | ||||
| 
 | ||||
|             // Now that we've finished generation, we need to update the sticky room to what
 | ||||
|             // it was. It's entirely possible that it changed lists though, so if it did then
 | ||||
|             // we also have to update the position of it.
 | ||||
|             if (oldStickyRoom && oldStickyRoom.room) { | ||||
|                 await this.updateStickyRoom(oldStickyRoom.room); | ||||
|                 if (this._stickyRoom && this._stickyRoom.room) { // just in case the update doesn't go according to plan
 | ||||
|                     if (this._stickyRoom.tag !== oldStickyRoom.tag) { | ||||
|                         // We put the sticky room at the top of the list to treat it as an obvious tag change.
 | ||||
|                         this._stickyRoom.position = 0; | ||||
|                         this.recalculateStickyRoom(this._stickyRoom.tag); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } finally { | ||||
|             this.stickyLock.release(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -685,9 +698,9 @@ export class Algorithm extends EventEmitter { | |||
|         if (!this.algorithms) throw new Error("Not ready: no algorithms to determine tags from"); | ||||
| 
 | ||||
|         // Note: check the isSticky against the room ID just in case the reference is wrong
 | ||||
|         const isSticky = this._stickyRoom && this._stickyRoom.room && this._stickyRoom.room.roomId === room.roomId; | ||||
|         const isSticky = this._stickyRoom?.room?.roomId === room.roomId; | ||||
|         if (cause === RoomUpdateCause.NewRoom) { | ||||
|             const isForLastSticky = this._lastStickyRoom && this._lastStickyRoom.room === room; | ||||
|             const isForLastSticky = this._lastStickyRoom?.room === room; | ||||
|             const roomTags = this.roomIdsToTags[room.roomId]; | ||||
|             const hasTags = roomTags && roomTags.length > 0; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Michael Telatynski
						Michael Telatynski