diff --git a/src/SlidingSyncManager.ts b/src/SlidingSyncManager.ts index 1489f636d4..4f21a69260 100644 --- a/src/SlidingSyncManager.ts +++ b/src/SlidingSyncManager.ts @@ -231,7 +231,7 @@ export class SlidingSyncManager { } catch (err) { logger.debug("ensureListRegistered: update failed txn_id=", err); } - return this.slidingSync.getListParams(listKey); + return this.slidingSync.getListParams(listKey)!; } public async setRoomVisible(roomId: string, visible: boolean): Promise { @@ -315,13 +315,19 @@ export class SlidingSyncManager { } else { await this.slidingSync.setListRanges(SlidingSyncManager.ListSearch, ranges); } - // gradually request more over time - await sleep(gapBetweenRequestsMs); } catch (err) { // do nothing, as we reject only when we get interrupted but that's fine as the next // request will include our data + } finally { + // gradually request more over time, even on errors. + await sleep(gapBetweenRequestsMs); } - hasMore = endIndex + 1 < this.slidingSync.getListData(SlidingSyncManager.ListSearch)?.joinedCount; + const listData = this.slidingSync.getListData(SlidingSyncManager.ListSearch); + if (!listData) { + // we failed to do the first request, keep trying + continue; + } + hasMore = endIndex + 1 < listData.joinedCount; startIndex += batchSize; firstTime = false; } diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index 7674916d70..1dd25cbff5 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -566,8 +566,8 @@ export default class RoomSublist extends React.Component { let isUnreadFirst = RoomListStore.instance.getListOrder(this.props.tagId) === ListAlgorithm.Importance; if (this.slidingSyncMode) { const slidingList = SlidingSyncManager.instance.slidingSync.getListParams(this.props.tagId); - isAlphabetical = slidingList.sort[0] === "by_name"; - isUnreadFirst = slidingList.sort[0] === "by_notification_level"; + isAlphabetical = (slidingList?.sort || [])[0] === "by_name"; + isUnreadFirst = (slidingList?.sort || [])[0] === "by_notification_level"; } // Invites don't get some nonsense options, so only add them if we have to. diff --git a/src/hooks/useSlidingSyncRoomSearch.ts b/src/hooks/useSlidingSyncRoomSearch.ts index 00f26c198b..7d5d1d92aa 100644 --- a/src/hooks/useSlidingSyncRoomSearch.ts +++ b/src/hooks/useSlidingSyncRoomSearch.ts @@ -58,7 +58,7 @@ export const useSlidingSyncRoomSearch = (): { const rooms = []; const { roomIndexToRoomId } = SlidingSyncManager.instance.slidingSync.getListData( SlidingSyncManager.ListSearch, - ); + )!; let i = 0; while (roomIndexToRoomId[i]) { const roomId = roomIndexToRoomId[i]; diff --git a/src/stores/room-list/SlidingRoomListStore.ts b/src/stores/room-list/SlidingRoomListStore.ts index eeacab35f6..884f588bd2 100644 --- a/src/stores/room-list/SlidingRoomListStore.ts +++ b/src/stores/room-list/SlidingRoomListStore.ts @@ -29,6 +29,7 @@ import { MetaSpace, SpaceKey, UPDATE_SELECTED_SPACE } from "../spaces"; import { LISTS_LOADING_EVENT } from "./RoomListStore"; import { UPDATE_EVENT } from "../AsyncStore"; import { SdkContextClass } from "../../contexts/SDKContext"; +import { filter } from "lodash"; interface IState { // state is tracked in underlying classes @@ -84,6 +85,7 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient impl public constructor(dis: MatrixDispatcher, private readonly context: SdkContextClass) { super(dis); this.setMaxListeners(20); // RoomList + LeftPanel + 8xRoomSubList + spares + this.stickyRoomId = null; } public async setTagSorting(tagId: TagID, sort: SortAlgorithm): Promise { @@ -249,9 +251,14 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient impl } // now set the rooms - const rooms = orderedRoomIds.map((roomId) => { - return this.matrixClient.getRoom(roomId); - }); + const rooms: Room[] = []; + orderedRoomIds.forEach((roomId) => { + const room = this.matrixClient.getRoom(roomId); + if (!room) { + return; + } + rooms.push(room); + }) tagMap[tagId] = rooms; this.tagMap = tagMap; } @@ -352,6 +359,9 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient impl if (roomId === activeSpace) { return; } + if (!filters.spaces) { + filters.spaces = []; + } filters.spaces.push(roomId); // add subspace }, false, diff --git a/test/stores/room-list/SlidingRoomListStore-test.ts b/test/stores/room-list/SlidingRoomListStore-test.ts index 2e83a53a8a..5d322daafb 100644 --- a/test/stores/room-list/SlidingRoomListStore-test.ts +++ b/test/stores/room-list/SlidingRoomListStore-test.ts @@ -82,10 +82,10 @@ describe("SlidingRoomListStore", () => { // change the active space activeSpace = spaceRoomId; - context._SpaceStore.emit(UPDATE_SELECTED_SPACE, spaceRoomId, false); + context._SpaceStore!.emit(UPDATE_SELECTED_SPACE, spaceRoomId, false); await p; - expect(context._SlidingSyncManager.ensureListRegistered).toHaveBeenCalledWith(DefaultTagID.Untagged, { + expect(context._SlidingSyncManager!.ensureListRegistered).toHaveBeenCalledWith(DefaultTagID.Untagged, { filters: expect.objectContaining({ spaces: [spaceRoomId], }), @@ -101,7 +101,7 @@ describe("SlidingRoomListStore", () => { }); await store.start(); // call onReady await p; - expect(context._SlidingSyncManager.ensureListRegistered).toHaveBeenCalledWith( + expect(context._SlidingSyncManager!.ensureListRegistered).toHaveBeenCalledWith( DefaultTagID.Untagged, expect.objectContaining({ filters: expect.objectContaining({ @@ -121,7 +121,7 @@ describe("SlidingRoomListStore", () => { return listName === DefaultTagID.Untagged && !isLoading; }); - mocked(context._SpaceStore.traverseSpace).mockImplementation( + mocked(context._SpaceStore!.traverseSpace).mockImplementation( (spaceId: string, fn: (roomId: string) => void) => { if (spaceId === spaceRoomId) { fn(subSpace1); @@ -132,10 +132,10 @@ describe("SlidingRoomListStore", () => { // change the active space activeSpace = spaceRoomId; - context._SpaceStore.emit(UPDATE_SELECTED_SPACE, spaceRoomId, false); + context._SpaceStore!.emit(UPDATE_SELECTED_SPACE, spaceRoomId, false); await p; - expect(context._SlidingSyncManager.ensureListRegistered).toHaveBeenCalledWith(DefaultTagID.Untagged, { + expect(context._SlidingSyncManager!.ensureListRegistered).toHaveBeenCalledWith(DefaultTagID.Untagged, { filters: expect.objectContaining({ spaces: [spaceRoomId, subSpace1, subSpace2], }), @@ -146,13 +146,13 @@ describe("SlidingRoomListStore", () => { it("setTagSorting alters the 'sort' option in the list", async () => { const tagId: TagID = "foo"; await store.setTagSorting(tagId, SortAlgorithm.Alphabetic); - expect(context._SlidingSyncManager.ensureListRegistered).toBeCalledWith(tagId, { + expect(context._SlidingSyncManager!.ensureListRegistered).toBeCalledWith(tagId, { sort: SlidingSyncSortToFilter[SortAlgorithm.Alphabetic], }); expect(store.getTagSorting(tagId)).toEqual(SortAlgorithm.Alphabetic); await store.setTagSorting(tagId, SortAlgorithm.Recent); - expect(context._SlidingSyncManager.ensureListRegistered).toBeCalledWith(tagId, { + expect(context._SlidingSyncManager!.ensureListRegistered).toBeCalledWith(tagId, { sort: SlidingSyncSortToFilter[SortAlgorithm.Recent], }); expect(store.getTagSorting(tagId)).toEqual(SortAlgorithm.Recent); @@ -177,14 +177,14 @@ describe("SlidingRoomListStore", () => { }, }, }; - mocked(context._SlidingSyncManager.slidingSync.getListData).mockImplementation((key: string) => { + mocked(context._SlidingSyncManager!.slidingSync.getListData).mockImplementation((key: string) => { return keyToListData[key] || null; }); - expect(store.getTagsForRoom(new Room(roomA, context.client, context.client.getUserId()))).toEqual([ + expect(store.getTagsForRoom(new Room(roomA, context.client!, context.client!.getUserId()))).toEqual([ DefaultTagID.Untagged, ]); - expect(store.getTagsForRoom(new Room(roomB, context.client, context.client.getUserId()))).toEqual([ + expect(store.getTagsForRoom(new Room(roomB, context.client!, context.client!.getUserId()))).toEqual([ DefaultTagID.Favourite, DefaultTagID.Untagged, ]); @@ -204,11 +204,11 @@ describe("SlidingRoomListStore", () => { 0: roomA, }; const rooms = [ - new Room(roomA, context.client, context.client.getUserId()), - new Room(roomB, context.client, context.client.getUserId()), - new Room(roomC, context.client, context.client.getUserId()), + new Room(roomA, context.client!, context.client!.getUserId()), + new Room(roomB, context.client!, context.client!.getUserId()), + new Room(roomC, context.client!, context.client!.getUserId()), ]; - mocked(context.client.getRoom).mockImplementation((roomId: string) => { + mocked(context.client!.getRoom).mockImplementation((roomId: string) => { switch (roomId) { case roomA: return rooms[0]; @@ -240,10 +240,10 @@ describe("SlidingRoomListStore", () => { 2: roomIdC, 0: roomIdA, }; - const roomA = new Room(roomIdA, context.client, context.client.getUserId()); - const roomB = new Room(roomIdB, context.client, context.client.getUserId()); - const roomC = new Room(roomIdC, context.client, context.client.getUserId()); - mocked(context.client.getRoom).mockImplementation((roomId: string) => { + const roomA = new Room(roomIdA, context.client!, context.client!.getUserId()); + const roomB = new Room(roomIdB, context.client!, context.client!.getUserId()); + const roomC = new Room(roomIdC, context.client!, context.client!.getUserId()); + mocked(context.client!.getRoom).mockImplementation((roomId: string) => { switch (roomId) { case roomIdA: return roomA; @@ -254,7 +254,7 @@ describe("SlidingRoomListStore", () => { } return null; }); - mocked(context._SlidingSyncManager.slidingSync.getListData).mockImplementation((key: string) => { + mocked(context._SlidingSyncManager!.slidingSync.getListData).mockImplementation((key: string) => { if (key !== tagId) { return null; }