Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 51b4555

Browse files
authored
Merge pull request #9934 from matrix-org/kegan/lists-as-keys
refactor: sliding sync: convert to lists-as-keys rather than indexes
2 parents a0a419a + 7df07fa commit 51b4555

File tree

10 files changed

+329
-178
lines changed

10 files changed

+329
-178
lines changed

cypress.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ export default defineConfig({
3030
specPattern: "cypress/e2e/**/*.{js,jsx,ts,tsx}",
3131
},
3232
env: {
33-
// Docker tag to use for `ghcr.io/matrix-org/sliding-sync-proxy` image.
34-
SLIDING_SYNC_PROXY_TAG: "v0.6.0",
33+
// Docker tag to use for `ghcr.io/matrix-org/sliding-sync` image.
34+
SLIDING_SYNC_PROXY_TAG: "v0.99.0-rc1",
3535
HOMESERVER: "synapse",
3636
},
3737
retries: {

cypress/e2e/sliding-sync/sliding-sync.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ import { MatrixClient } from "matrix-js-sdk/src/matrix";
2121
import { Interception } from "cypress/types/net-stubbing";
2222

2323
import { HomeserverInstance } from "../../plugins/utils/homeserver";
24-
import { SettingLevel } from "../../../src/settings/SettingLevel";
25-
import { Layout } from "../../../src/settings/enums/Layout";
2624
import { ProxyInstance } from "../../plugins/sliding-sync";
2725

2826
describe("Sliding Sync", () => {
@@ -102,21 +100,6 @@ describe("Sliding Sync", () => {
102100
});
103101
};
104102

105-
// sanity check everything works
106-
it("should correctly render expected messages", () => {
107-
cy.get<string>("@roomId").then((roomId) => cy.visit("/#/room/" + roomId));
108-
cy.setSettingValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
109-
110-
// Wait until configuration is finished
111-
cy.contains(
112-
".mx_RoomView_body .mx_GenericEventListSummary .mx_GenericEventListSummary_summary",
113-
"created and configured the room.",
114-
);
115-
116-
// Click "expand" link button
117-
cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=false]").click();
118-
});
119-
120103
it("should render the Rooms list in reverse chronological order by default and allowing sorting A-Z", () => {
121104
// create rooms and check room names are correct
122105
cy.createRoom({ name: "Apple" }).then(() => cy.contains(".mx_RoomSublist", "Apple"));

cypress/plugins/sliding-sync/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { getFreePort } from "../utils/port";
2323
import { HomeserverInstance } from "../utils/homeserver";
2424

2525
// A cypress plugin to add command to start & stop https://github.com/matrix-org/sliding-sync
26-
// SLIDING_SYNC_PROXY_TAG env used as the docker tag to use for `ghcr.io/matrix-org/sliding-sync-proxy` image.
26+
// SLIDING_SYNC_PROXY_TAG env used as the docker tag to use for `ghcr.io/matrix-org/sliding-sync` image.
2727

2828
export interface ProxyInstance {
2929
containerId: string;
@@ -72,7 +72,7 @@ async function proxyStart(dockerTag: string, homeserver: HomeserverInstance): Pr
7272
const port = await getFreePort();
7373
console.log(new Date(), "starting proxy container...", dockerTag);
7474
const containerId = await dockerRun({
75-
image: "ghcr.io/matrix-org/sliding-sync-proxy:" + dockerTag,
75+
image: "ghcr.io/matrix-org/sliding-sync:" + dockerTag,
7676
containerName: "react-sdk-cypress-sliding-sync-proxy",
7777
params: [
7878
"--rm",

src/SlidingSyncManager.ts

Lines changed: 22 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,10 @@ export class SlidingSyncManager {
119119

120120
public slidingSync: SlidingSync;
121121
private client: MatrixClient;
122-
private listIdToIndex: Record<string, number>;
123122

124123
private configureDefer: IDeferred<void>;
125124

126125
public constructor() {
127-
this.listIdToIndex = {};
128126
this.configureDefer = defer<void>();
129127
}
130128

@@ -134,13 +132,18 @@ export class SlidingSyncManager {
134132

135133
public configure(client: MatrixClient, proxyUrl: string): SlidingSync {
136134
this.client = client;
137-
this.listIdToIndex = {};
138135
// by default use the encrypted subscription as that gets everything, which is a safer
139136
// default than potentially missing member events.
140-
this.slidingSync = new SlidingSync(proxyUrl, [], ENCRYPTED_SUBSCRIPTION, client, SLIDING_SYNC_TIMEOUT_MS);
137+
this.slidingSync = new SlidingSync(
138+
proxyUrl,
139+
new Map(),
140+
ENCRYPTED_SUBSCRIPTION,
141+
client,
142+
SLIDING_SYNC_TIMEOUT_MS,
143+
);
141144
this.slidingSync.addCustomSubscription(UNENCRYPTED_SUBSCRIPTION_NAME, UNENCRYPTED_SUBSCRIPTION);
142145
// set the space list
143-
this.slidingSync.setList(this.getOrAllocateListIndex(SlidingSyncManager.ListSpaces), {
146+
this.slidingSync.setList(SlidingSyncManager.ListSpaces, {
144147
ranges: [[0, 20]],
145148
sort: ["by_name"],
146149
slow_get_all_rooms: true,
@@ -173,47 +176,16 @@ export class SlidingSyncManager {
173176
return this.slidingSync;
174177
}
175178

176-
public listIdForIndex(index: number): string | null {
177-
for (const listId in this.listIdToIndex) {
178-
if (this.listIdToIndex[listId] === index) {
179-
return listId;
180-
}
181-
}
182-
return null;
183-
}
184-
185-
/**
186-
* Allocate or retrieve the list index for an arbitrary list ID. For example SlidingSyncManager.ListSpaces
187-
* @param listId A string which represents the list.
188-
* @returns The index to use when registering lists or listening for callbacks.
189-
*/
190-
public getOrAllocateListIndex(listId: string): number {
191-
let index = this.listIdToIndex[listId];
192-
if (index === undefined) {
193-
// assign next highest index
194-
index = -1;
195-
for (const id in this.listIdToIndex) {
196-
const listIndex = this.listIdToIndex[id];
197-
if (listIndex > index) {
198-
index = listIndex;
199-
}
200-
}
201-
index++;
202-
this.listIdToIndex[listId] = index;
203-
}
204-
return index;
205-
}
206-
207179
/**
208180
* Ensure that this list is registered.
209-
* @param listIndex The list index to register
181+
* @param listKey The list key to register
210182
* @param updateArgs The fields to update on the list.
211183
* @returns The complete list request params
212184
*/
213-
public async ensureListRegistered(listIndex: number, updateArgs: PartialSlidingSyncRequest): Promise<MSC3575List> {
214-
logger.debug("ensureListRegistered:::", listIndex, updateArgs);
185+
public async ensureListRegistered(listKey: string, updateArgs: PartialSlidingSyncRequest): Promise<MSC3575List> {
186+
logger.debug("ensureListRegistered:::", listKey, updateArgs);
215187
await this.configureDefer.promise;
216-
let list = this.slidingSync.getList(listIndex);
188+
let list = this.slidingSync.getListParams(listKey);
217189
if (!list) {
218190
list = {
219191
ranges: [[0, 20]],
@@ -252,14 +224,14 @@ export class SlidingSyncManager {
252224
try {
253225
// if we only have range changes then call a different function so we don't nuke the list from before
254226
if (updateArgs.ranges && Object.keys(updateArgs).length === 1) {
255-
await this.slidingSync.setListRanges(listIndex, updateArgs.ranges);
227+
await this.slidingSync.setListRanges(listKey, updateArgs.ranges);
256228
} else {
257-
await this.slidingSync.setList(listIndex, list);
229+
await this.slidingSync.setList(listKey, list);
258230
}
259231
} catch (err) {
260232
logger.debug("ensureListRegistered: update failed txn_id=", err);
261233
}
262-
return this.slidingSync.getList(listIndex);
234+
return this.slidingSync.getListParams(listKey)!;
263235
}
264236

265237
public async setRoomVisible(roomId: string, visible: boolean): Promise<string> {
@@ -304,7 +276,6 @@ export class SlidingSyncManager {
304276
*/
305277
public async startSpidering(batchSize: number, gapBetweenRequestsMs: number): Promise<void> {
306278
await sleep(gapBetweenRequestsMs); // wait a bit as this is called on first render so let's let things load
307-
const listIndex = this.getOrAllocateListIndex(SlidingSyncManager.ListSearch);
308279
let startIndex = batchSize;
309280
let hasMore = true;
310281
let firstTime = true;
@@ -316,7 +287,7 @@ export class SlidingSyncManager {
316287
[startIndex, endIndex],
317288
];
318289
if (firstTime) {
319-
await this.slidingSync.setList(listIndex, {
290+
await this.slidingSync.setList(SlidingSyncManager.ListSearch, {
320291
// e.g [0,19] [20,39] then [0,19] [40,59]. We keep [0,20] constantly to ensure
321292
// any changes to the list whilst spidering are caught.
322293
ranges: ranges,
@@ -342,15 +313,17 @@ export class SlidingSyncManager {
342313
},
343314
});
344315
} else {
345-
await this.slidingSync.setListRanges(listIndex, ranges);
316+
await this.slidingSync.setListRanges(SlidingSyncManager.ListSearch, ranges);
346317
}
347-
// gradually request more over time
348-
await sleep(gapBetweenRequestsMs);
349318
} catch (err) {
350319
// do nothing, as we reject only when we get interrupted but that's fine as the next
351320
// request will include our data
321+
} finally {
322+
// gradually request more over time, even on errors.
323+
await sleep(gapBetweenRequestsMs);
352324
}
353-
hasMore = endIndex + 1 < this.slidingSync.getListData(listIndex)?.joinedCount;
325+
const listData = this.slidingSync.getListData(SlidingSyncManager.ListSearch)!;
326+
hasMore = endIndex + 1 < listData.joinedCount;
354327
startIndex += batchSize;
355328
firstTime = false;
356329
}

src/components/views/rooms/RoomSublist.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,8 @@ export default class RoomSublist extends React.Component<IProps, IState> {
340340

341341
private onShowAllClick = async (): Promise<void> => {
342342
if (this.slidingSyncMode) {
343-
const slidingSyncIndex = SlidingSyncManager.instance.getOrAllocateListIndex(this.props.tagId);
344343
const count = RoomListStore.instance.getCount(this.props.tagId);
345-
await SlidingSyncManager.instance.ensureListRegistered(slidingSyncIndex, {
344+
await SlidingSyncManager.instance.ensureListRegistered(this.props.tagId, {
346345
ranges: [[0, count]],
347346
});
348347
}
@@ -566,10 +565,9 @@ export default class RoomSublist extends React.Component<IProps, IState> {
566565
let isAlphabetical = RoomListStore.instance.getTagSorting(this.props.tagId) === SortAlgorithm.Alphabetic;
567566
let isUnreadFirst = RoomListStore.instance.getListOrder(this.props.tagId) === ListAlgorithm.Importance;
568567
if (this.slidingSyncMode) {
569-
const slidingSyncIndex = SlidingSyncManager.instance.getOrAllocateListIndex(this.props.tagId);
570-
const slidingList = SlidingSyncManager.instance.slidingSync.getList(slidingSyncIndex);
571-
isAlphabetical = slidingList.sort[0] === "by_name";
572-
isUnreadFirst = slidingList.sort[0] === "by_notification_level";
568+
const slidingList = SlidingSyncManager.instance.slidingSync.getListParams(this.props.tagId);
569+
isAlphabetical = (slidingList?.sort || [])[0] === "by_name";
570+
isUnreadFirst = (slidingList?.sort || [])[0] === "by_notification_level";
573571
}
574572

575573
// Invites don't get some nonsense options, so only add them if we have to.

src/hooks/useSlidingSyncRoomSearch.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ export const useSlidingSyncRoomSearch = (): {
3434
const [rooms, setRooms] = useState<Room[]>([]);
3535

3636
const [loading, setLoading] = useState(false);
37-
const listIndex = SlidingSyncManager.instance.getOrAllocateListIndex(SlidingSyncManager.ListSearch);
3837

3938
const [updateQuery, updateResult] = useLatestResult<{ term: string; limit?: number }, Room[]>(setRooms);
4039

@@ -50,14 +49,16 @@ export const useSlidingSyncRoomSearch = (): {
5049

5150
try {
5251
setLoading(true);
53-
await SlidingSyncManager.instance.ensureListRegistered(listIndex, {
52+
await SlidingSyncManager.instance.ensureListRegistered(SlidingSyncManager.ListSearch, {
5453
ranges: [[0, limit]],
5554
filters: {
5655
room_name_like: term,
5756
},
5857
});
5958
const rooms = [];
60-
const { roomIndexToRoomId } = SlidingSyncManager.instance.slidingSync.getListData(listIndex);
59+
const { roomIndexToRoomId } = SlidingSyncManager.instance.slidingSync.getListData(
60+
SlidingSyncManager.ListSearch,
61+
)!;
6162
let i = 0;
6263
while (roomIndexToRoomId[i]) {
6364
const roomId = roomIndexToRoomId[i];
@@ -78,7 +79,7 @@ export const useSlidingSyncRoomSearch = (): {
7879
// TODO: delete the list?
7980
}
8081
},
81-
[updateQuery, updateResult, listIndex],
82+
[updateQuery, updateResult],
8283
);
8384

8485
return {

src/stores/room-list/SlidingRoomListStore.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,20 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
8484
public constructor(dis: MatrixDispatcher, private readonly context: SdkContextClass) {
8585
super(dis);
8686
this.setMaxListeners(20); // RoomList + LeftPanel + 8xRoomSubList + spares
87+
this.stickyRoomId = null;
8788
}
8889

8990
public async setTagSorting(tagId: TagID, sort: SortAlgorithm): Promise<void> {
9091
logger.info("SlidingRoomListStore.setTagSorting ", tagId, sort);
9192
this.tagIdToSortAlgo[tagId] = sort;
92-
const slidingSyncIndex = this.context.slidingSyncManager.getOrAllocateListIndex(tagId);
9393
switch (sort) {
9494
case SortAlgorithm.Alphabetic:
95-
await this.context.slidingSyncManager.ensureListRegistered(slidingSyncIndex, {
95+
await this.context.slidingSyncManager.ensureListRegistered(tagId, {
9696
sort: SlidingSyncSortToFilter[SortAlgorithm.Alphabetic],
9797
});
9898
break;
9999
case SortAlgorithm.Recent:
100-
await this.context.slidingSyncManager.ensureListRegistered(slidingSyncIndex, {
100+
await this.context.slidingSyncManager.ensureListRegistered(tagId, {
101101
sort: SlidingSyncSortToFilter[SortAlgorithm.Recent],
102102
});
103103
break;
@@ -164,8 +164,7 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
164164
// check all lists for each tag we know about and see if the room is there
165165
const tags: TagID[] = [];
166166
for (const tagId in this.tagIdToSortAlgo) {
167-
const index = this.context.slidingSyncManager.getOrAllocateListIndex(tagId);
168-
const listData = this.context.slidingSyncManager.slidingSync.getListData(index);
167+
const listData = this.context.slidingSyncManager.slidingSync.getListData(tagId);
169168
if (!listData) {
170169
continue;
171170
}
@@ -251,19 +250,19 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
251250
}
252251

253252
// now set the rooms
254-
const rooms = orderedRoomIds.map((roomId) => {
255-
return this.matrixClient.getRoom(roomId);
253+
const rooms: Room[] = [];
254+
orderedRoomIds.forEach((roomId) => {
255+
const room = this.matrixClient.getRoom(roomId);
256+
if (!room) {
257+
return;
258+
}
259+
rooms.push(room);
256260
});
257261
tagMap[tagId] = rooms;
258262
this.tagMap = tagMap;
259263
}
260264

261-
private onSlidingSyncListUpdate(
262-
listIndex: number,
263-
joinCount: number,
264-
roomIndexToRoomId: Record<number, string>,
265-
): void {
266-
const tagId = this.context.slidingSyncManager.listIdForIndex(listIndex);
265+
private onSlidingSyncListUpdate(tagId: string, joinCount: number, roomIndexToRoomId: Record<number, string>): void {
267266
this.counts[tagId] = joinCount;
268267
this.refreshOrderedLists(tagId, roomIndexToRoomId);
269268
// let the UI update
@@ -295,8 +294,7 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
295294
if (room) {
296295
// resort it based on the slidingSync view of the list. This may cause this old sticky
297296
// room to cease to exist.
298-
const index = this.context.slidingSyncManager.getOrAllocateListIndex(tagId);
299-
const listData = this.context.slidingSyncManager.slidingSync.getListData(index);
297+
const listData = this.context.slidingSyncManager.slidingSync.getListData(tagId);
300298
if (!listData) {
301299
continue;
302300
}
@@ -334,9 +332,8 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
334332
const sort = SortAlgorithm.Recent; // default to recency sort, TODO: read from config
335333
this.tagIdToSortAlgo[tagId] = sort;
336334
this.emit(LISTS_LOADING_EVENT, tagId, true);
337-
const index = this.context.slidingSyncManager.getOrAllocateListIndex(tagId);
338335
this.context.slidingSyncManager
339-
.ensureListRegistered(index, {
336+
.ensureListRegistered(tagId, {
340337
filters: filter,
341338
sort: SlidingSyncSortToFilter[sort],
342339
})
@@ -361,15 +358,17 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
361358
if (roomId === activeSpace) {
362359
return;
363360
}
361+
if (!filters.spaces) {
362+
filters.spaces = [];
363+
}
364364
filters.spaces.push(roomId); // add subspace
365365
},
366366
false,
367367
);
368368

369369
this.emit(LISTS_LOADING_EVENT, tagId, true);
370-
const index = this.context.slidingSyncManager.getOrAllocateListIndex(tagId);
371370
this.context.slidingSyncManager
372-
.ensureListRegistered(index, {
371+
.ensureListRegistered(tagId, {
373372
filters: filters,
374373
})
375374
.then(() => {

0 commit comments

Comments
 (0)