Add Sync3ObservableList
This commit is contained in:
parent
104d98d4a4
commit
32c62641fd
3 changed files with 189 additions and 0 deletions
|
@ -58,6 +58,7 @@
|
||||||
"@rollup/plugin-commonjs": "^15.0.0",
|
"@rollup/plugin-commonjs": "^15.0.0",
|
||||||
"@rollup/plugin-json": "^4.1.0",
|
"@rollup/plugin-json": "^4.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||||
|
"@types/assert": "^1.5.6",
|
||||||
"aes-js": "^3.1.2",
|
"aes-js": "^3.1.2",
|
||||||
"another-json": "^0.2.0",
|
"another-json": "^0.2.0",
|
||||||
"base64-arraybuffer": "^0.2.0",
|
"base64-arraybuffer": "^0.2.0",
|
||||||
|
|
183
src/matrix/Sync3ObservableList.ts
Normal file
183
src/matrix/Sync3ObservableList.ts
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as assert from "assert";
|
||||||
|
import { BaseObservableList } from "../observable/list/BaseObservableList";
|
||||||
|
import { ObservableMap } from "../observable/map/ObservableMap.js";
|
||||||
|
import { Room } from "./room/Room.js";
|
||||||
|
|
||||||
|
// subset of Sync3 functions used in this list; interfaced out for testing
|
||||||
|
interface ISync {
|
||||||
|
count(): number;
|
||||||
|
roomAtIndex(i: number): string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An observable list that produces a subset of rooms based on Sync3 responses.
|
||||||
|
*/
|
||||||
|
export class Sync3ObservableList extends BaseObservableList<Room | null> {
|
||||||
|
sync: ISync;
|
||||||
|
rooms: ObservableMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an observable list that produces Rooms within the sliding window of Sync3 responses
|
||||||
|
* or `null` to indicate that a placeholder room should be used.
|
||||||
|
* @param sync3 The Sync3 class, which tracks up-to-date information on the sliding window / room counts.
|
||||||
|
* @param rooms The entire set of rooms known to the client (e.g from Session.rooms).
|
||||||
|
*/
|
||||||
|
constructor(sync3: ISync, rooms: ObservableMap) {
|
||||||
|
super();
|
||||||
|
this.sync = sync3;
|
||||||
|
this.rooms = rooms;
|
||||||
|
}
|
||||||
|
|
||||||
|
get length(): number {
|
||||||
|
return this.sync.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.iterator](): Iterator<Room | null, any, undefined> {
|
||||||
|
let i = 0;
|
||||||
|
return {
|
||||||
|
next: (): any => {
|
||||||
|
// base case
|
||||||
|
if (i >= this.length) {
|
||||||
|
return {
|
||||||
|
done: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let roomId = this.sync.roomAtIndex(i);
|
||||||
|
i += 1;
|
||||||
|
if (!roomId) {
|
||||||
|
return {
|
||||||
|
value: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
value: this.rooms.get(roomId) || null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tests() {
|
||||||
|
const makeRooms = function (len) {
|
||||||
|
let rooms: any[] = [];
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
const roomId = "!room-" + i;
|
||||||
|
rooms.push({
|
||||||
|
id: roomId,
|
||||||
|
data: {
|
||||||
|
some_key: i,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return rooms;
|
||||||
|
}
|
||||||
|
|
||||||
|
const assertList = function (assert, rooms, gotList, wantListRoomIds) {
|
||||||
|
if (wantListRoomIds.length === 0) {
|
||||||
|
assert.equal(gotList.length, 0);
|
||||||
|
for (const room of gotList) {
|
||||||
|
assert.equal(0, 1); // fail
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let i = 0;
|
||||||
|
for (const room of gotList) {
|
||||||
|
const wantRoomId = wantListRoomIds[i];
|
||||||
|
const gotRoomId = room ? room.id : null;
|
||||||
|
assert.equal(wantRoomId, gotRoomId);
|
||||||
|
if (wantRoomId !== null && gotRoomId !== null) {
|
||||||
|
assert.deepEqual(room, rooms.get(wantRoomId));
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"iterator": (assert) => {
|
||||||
|
const rooms = new ObservableMap();
|
||||||
|
let indexToRoomId = {};
|
||||||
|
let roomCount = 0;
|
||||||
|
const sync3 = {
|
||||||
|
count: (): number => {
|
||||||
|
return roomCount;
|
||||||
|
},
|
||||||
|
roomAtIndex: (i: number): string => {
|
||||||
|
return indexToRoomId[i];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
makeRooms(100).forEach((r) => {
|
||||||
|
rooms.add(r.id, r);
|
||||||
|
});
|
||||||
|
const list = new Sync3ObservableList(sync3, rooms);
|
||||||
|
|
||||||
|
// iterate over the list (0 items) as sync3 has no indexes for the rooms
|
||||||
|
assertList(assert, rooms, list, []);
|
||||||
|
|
||||||
|
|
||||||
|
// 'load' 5 rooms from sync v3 and set the total count to 5 so we load the entire room list in one go!
|
||||||
|
// [R,R,R,R,R]
|
||||||
|
let slidingWindow: any[] = [
|
||||||
|
"!room-50", "!room-53", "!room-1", "!room-52", "!room-97"
|
||||||
|
]
|
||||||
|
roomCount = 5;
|
||||||
|
for (let i = 0; i < slidingWindow.length; i++) {
|
||||||
|
indexToRoomId[i] = slidingWindow[i];
|
||||||
|
}
|
||||||
|
assertList(assert, rooms, list, slidingWindow);
|
||||||
|
|
||||||
|
// now add 5 more rooms which we don't know about, we should iterate through them with `null` entries
|
||||||
|
// [R,R,R,R,R,P,P,P,P,P] (R=room, P=placeholder)
|
||||||
|
roomCount = 10;
|
||||||
|
assertList(assert, rooms, list, slidingWindow.concat([null, null, null, null, null]));
|
||||||
|
|
||||||
|
|
||||||
|
// now track a window in the middle of the list (5-10)
|
||||||
|
indexToRoomId = {};
|
||||||
|
for (let i = 0; i < slidingWindow.length; i++) {
|
||||||
|
indexToRoomId[i + 5] = slidingWindow[i];
|
||||||
|
}
|
||||||
|
roomCount = 15;
|
||||||
|
// [P,P,P,P,P,R,R,R,R,R,P,P,P,P,P]
|
||||||
|
assertList(assert, rooms, list, [null, null, null, null, null].concat(slidingWindow.concat([null, null, null, null, null])));
|
||||||
|
|
||||||
|
|
||||||
|
// now track multiple ranges
|
||||||
|
const anotherSlidingWindow: any[] = [
|
||||||
|
"!room-30", "!room-33", "!room-36", "!room-29", "!room-21"
|
||||||
|
]
|
||||||
|
for (let i = 0; i < anotherSlidingWindow.length; i++) {
|
||||||
|
indexToRoomId[i + 15] = anotherSlidingWindow[i];
|
||||||
|
}
|
||||||
|
roomCount = 20;
|
||||||
|
// [P,P,P,P,P,R,R,R,R,R,P,P,P,P,P,R,R,R,R,R]
|
||||||
|
assertList(
|
||||||
|
assert, rooms, list,
|
||||||
|
[null, null, null, null, null].concat(
|
||||||
|
slidingWindow.concat(
|
||||||
|
[null, null, null, null, null].concat(
|
||||||
|
anotherSlidingWindow
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
|
@ -1164,6 +1164,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
||||||
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
|
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
|
||||||
|
|
||||||
|
"@types/assert@^1.5.6":
|
||||||
|
version "1.5.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/assert/-/assert-1.5.6.tgz#a8b5a94ce5fb8f4ba65fdc37fc9507609114189e"
|
||||||
|
integrity sha512-Y7gDJiIqb9qKUHfBQYOWGngUpLORtirAVPuj/CWJrU2C6ZM4/y3XLwuwfGMF8s7QzW746LQZx23m0+1FSgjfug==
|
||||||
|
|
||||||
"@types/cacheable-request@^6.0.1":
|
"@types/cacheable-request@^6.0.1":
|
||||||
version "6.0.2"
|
version "6.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9"
|
resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9"
|
||||||
|
|
Reference in a new issue