mirror of
https://github.com/matrix-org/sliding-sync.git
synced 2025-03-10 13:37:11 +00:00
client: swap to lists-as-keys
This commit is contained in:
parent
3c964de004
commit
613cbd9a67
@ -113,7 +113,10 @@ export function svgify(domNode, activeLists, resp) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// add insertions, deletions and updates
|
// add insertions, deletions and updates
|
||||||
resp.lists.forEach((list, listIndex) => {
|
let listIndex = 0;
|
||||||
|
const listKeys = Object.keys(resp.lists).sort();
|
||||||
|
listKeys.forEach((listKey) => {
|
||||||
|
const list = resp.lists[listKey];
|
||||||
list.ops.forEach((op) => {
|
list.ops.forEach((op) => {
|
||||||
if (op.op === "DELETE") {
|
if (op.op === "DELETE") {
|
||||||
addLine(listIndex, op.index, colorDelete);
|
addLine(listIndex, op.index, colorDelete);
|
||||||
@ -135,6 +138,7 @@ export function svgify(domNode, activeLists, resp) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
listIndex++;
|
||||||
});
|
});
|
||||||
|
|
||||||
// this is expensive so only do it on smaller accounts
|
// this is expensive so only do it on smaller accounts
|
||||||
|
@ -241,7 +241,11 @@ const doSyncLoop = async (accessToken) => {
|
|||||||
slidingSync.stop();
|
slidingSync.stop();
|
||||||
}
|
}
|
||||||
console.log("Starting sync loop");
|
console.log("Starting sync loop");
|
||||||
slidingSync = new SlidingSync(activeLists, syncConnection);
|
const lists = {};
|
||||||
|
activeLists.forEach((al, i) => {
|
||||||
|
lists[""+i] = al;
|
||||||
|
})
|
||||||
|
slidingSync = new SlidingSync(lists, syncConnection);
|
||||||
slidingSync.addLifecycleListener((state, resp, err) => {
|
slidingSync.addLifecycleListener((state, resp, err) => {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
// The sync has been processed and we can now re-render the UI.
|
// The sync has been processed and we can now re-render the UI.
|
||||||
|
127
client/sync.js
127
client/sync.js
@ -147,11 +147,11 @@ export class SlidingList {
|
|||||||
export class SlidingSync {
|
export class SlidingSync {
|
||||||
/**
|
/**
|
||||||
* Create a new sliding sync instance
|
* Create a new sliding sync instance
|
||||||
* @param {[]SlidingList} lists The lists to use for sliding sync.
|
* @param {Record<string, SlidingList>} lists The lists to use for sliding sync.
|
||||||
* @param {SlidingSyncConnection} conn The connection to use for /sync calls.
|
* @param {SlidingSyncConnection} conn The connection to use for /sync calls.
|
||||||
*/
|
*/
|
||||||
constructor(lists, conn) {
|
constructor(lists, conn) {
|
||||||
this.lists = lists;
|
this.lists = lists || {};
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
this.terminated = false;
|
this.terminated = false;
|
||||||
this.roomSubscription = "";
|
this.roomSubscription = "";
|
||||||
@ -217,24 +217,27 @@ export class SlidingSync {
|
|||||||
let resp;
|
let resp;
|
||||||
try {
|
try {
|
||||||
// these fields are always required
|
// these fields are always required
|
||||||
|
const reqLists = {};
|
||||||
|
Object.keys(this.lists).forEach((listKey) => {
|
||||||
|
const al = this.lists[listKey];
|
||||||
|
let l = {
|
||||||
|
ranges: al.activeRanges,
|
||||||
|
filters: al.getFilters(),
|
||||||
|
};
|
||||||
|
// if this is the first request on this session, send sticky request data which never changes
|
||||||
|
if (!currentPos) {
|
||||||
|
l.required_state = REQUIRED_STATE_EVENTS_IN_LIST;
|
||||||
|
l.timeline_limit = 1;
|
||||||
|
l.sort = [
|
||||||
|
"by_highlight_count",
|
||||||
|
"by_notification_count",
|
||||||
|
"by_recency",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
reqLists[listKey] = l;
|
||||||
|
});
|
||||||
let reqBody = {
|
let reqBody = {
|
||||||
lists: this.lists.map((al) => {
|
lists: reqLists,
|
||||||
let l = {
|
|
||||||
ranges: al.activeRanges,
|
|
||||||
filters: al.getFilters(),
|
|
||||||
};
|
|
||||||
// if this is the first request on this session, send sticky request data which never changes
|
|
||||||
if (!currentPos) {
|
|
||||||
l.required_state = REQUIRED_STATE_EVENTS_IN_LIST;
|
|
||||||
l.timeline_limit = 1;
|
|
||||||
l.sort = [
|
|
||||||
"by_highlight_count",
|
|
||||||
"by_notification_count",
|
|
||||||
"by_recency",
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return l;
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
// check if we are (un)subscribing to a room and modify request this one time for it
|
// check if we are (un)subscribing to a room and modify request this one time for it
|
||||||
let subscribingToRoom;
|
let subscribingToRoom;
|
||||||
@ -265,10 +268,14 @@ export class SlidingSync {
|
|||||||
currentSub = subscribingToRoom;
|
currentSub = subscribingToRoom;
|
||||||
}
|
}
|
||||||
if (!resp.lists) {
|
if (!resp.lists) {
|
||||||
resp.lists = [];
|
resp.lists = {};
|
||||||
}
|
}
|
||||||
resp.lists.forEach((l, index) => {
|
Object.keys(resp.lists).forEach((key) => {
|
||||||
this.lists[index].joinedCount = l.count;
|
const list = this.lists[key];
|
||||||
|
if (!list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
list.joinedCount = resp.lists[key].count;
|
||||||
});
|
});
|
||||||
this._invokeLifecycleListeners(
|
this._invokeLifecycleListeners(
|
||||||
LifecycleSyncRequestFinished,
|
LifecycleSyncRequestFinished,
|
||||||
@ -292,17 +299,18 @@ export class SlidingSync {
|
|||||||
this._invokeRoomDataListeners(roomId, resp.rooms[roomId]);
|
this._invokeRoomDataListeners(roomId, resp.rooms[roomId]);
|
||||||
});
|
});
|
||||||
|
|
||||||
resp.lists.forEach((list, listIndex) => {
|
Object.keys(resp.lists).forEach((listKey) => {
|
||||||
|
const list = resp.lists[listKey];
|
||||||
let gapIndex = -1;
|
let gapIndex = -1;
|
||||||
list.ops = list.ops || [];
|
list.ops = list.ops || [];
|
||||||
list.ops.forEach((op) => {
|
list.ops.forEach((op) => {
|
||||||
switch (op.op) {
|
switch (op.op) {
|
||||||
case "DELETE": {
|
case "DELETE": {
|
||||||
console.log("DELETE", listIndex, op.index, ";");
|
console.log("DELETE", listKey, op.index, ";");
|
||||||
delete this.lists[listIndex].roomIndexToRoomId[op.index];
|
delete this.lists[listKey].roomIndexToRoomId[op.index];
|
||||||
if (gapIndex !== -1) {
|
if (gapIndex !== -1) {
|
||||||
// we already have a DELETE operation to process, so process it.
|
// we already have a DELETE operation to process, so process it.
|
||||||
this.removeEntry(listIndex, gapIndex);
|
this.removeEntry(listKey, gapIndex);
|
||||||
}
|
}
|
||||||
gapIndex = op.index;
|
gapIndex = op.index;
|
||||||
break;
|
break;
|
||||||
@ -310,16 +318,16 @@ export class SlidingSync {
|
|||||||
case "INSERT": {
|
case "INSERT": {
|
||||||
console.log(
|
console.log(
|
||||||
"INSERT",
|
"INSERT",
|
||||||
listIndex,
|
listKey,
|
||||||
op.index,
|
op.index,
|
||||||
op.room_id,
|
op.room_id,
|
||||||
";",
|
";",
|
||||||
);
|
);
|
||||||
if (this.lists[listIndex].roomIndexToRoomId[op.index]) {
|
if (this.lists[listKey].roomIndexToRoomId[op.index]) {
|
||||||
// something is in this space, shift items out of the way
|
// something is in this space, shift items out of the way
|
||||||
if (gapIndex < 0) {
|
if (gapIndex < 0) {
|
||||||
// we haven't been told where to shift from, so make way for a new room entry.
|
// we haven't been told where to shift from, so make way for a new room entry.
|
||||||
this.addEntry(listIndex, op.index);
|
this.addEntry(listKey, op.index);
|
||||||
} else if (gapIndex > op.index) {
|
} else if (gapIndex > op.index) {
|
||||||
// the gap is further down the list, shift every element to the right
|
// the gap is further down the list, shift every element to the right
|
||||||
// starting at the gap so we can just shift each element in turn:
|
// starting at the gap so we can just shift each element in turn:
|
||||||
@ -328,11 +336,11 @@ export class SlidingSync {
|
|||||||
// [A,B,B,C] i=2
|
// [A,B,B,C] i=2
|
||||||
// [A,A,B,C] i=1
|
// [A,A,B,C] i=1
|
||||||
// Terminate. We'll assign into op.index next.
|
// Terminate. We'll assign into op.index next.
|
||||||
this.shiftRight(listIndex, gapIndex, op.index);
|
this.shiftRight(listKey, gapIndex, op.index);
|
||||||
} else if (gapIndex < op.index) {
|
} else if (gapIndex < op.index) {
|
||||||
// the gap is further up the list, shift every element to the left
|
// the gap is further up the list, shift every element to the left
|
||||||
// starting at the gap so we can just shift each element in turn
|
// starting at the gap so we can just shift each element in turn
|
||||||
this.shiftLeft(listIndex, op.index, gapIndex);
|
this.shiftLeft(listKey, op.index, gapIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// forget the gap, we don't need it anymore. This is outside the check for
|
// forget the gap, we don't need it anymore. This is outside the check for
|
||||||
@ -340,17 +348,17 @@ export class SlidingSync {
|
|||||||
// forget the gap, not conditionally based on the presence of a room in the INSERT
|
// forget the gap, not conditionally based on the presence of a room in the INSERT
|
||||||
// position. Without this, DELETE 0; INSERT 0; would do the wrong thing.
|
// position. Without this, DELETE 0; INSERT 0; would do the wrong thing.
|
||||||
gapIndex = -1;
|
gapIndex = -1;
|
||||||
this.lists[listIndex].roomIndexToRoomId[op.index] = op.room_id;
|
this.lists[listKey].roomIndexToRoomId[op.index] = op.room_id;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "INVALIDATE": {
|
case "INVALIDATE": {
|
||||||
const startIndex = op.range[0];
|
const startIndex = op.range[0];
|
||||||
for (let i = startIndex; i <= op.range[1]; i++) {
|
for (let i = startIndex; i <= op.range[1]; i++) {
|
||||||
delete this.lists[listIndex].roomIndexToRoomId[i];
|
delete this.lists[listKey].roomIndexToRoomId[i];
|
||||||
}
|
}
|
||||||
console.log(
|
console.log(
|
||||||
"INVALIDATE",
|
"INVALIDATE",
|
||||||
listIndex,
|
listKey,
|
||||||
op.range[0],
|
op.range[0],
|
||||||
op.range[1],
|
op.range[1],
|
||||||
";",
|
";",
|
||||||
@ -364,11 +372,11 @@ export class SlidingSync {
|
|||||||
if (!roomId) {
|
if (!roomId) {
|
||||||
break; // we are at the end of list
|
break; // we are at the end of list
|
||||||
}
|
}
|
||||||
this.lists[listIndex].roomIndexToRoomId[i] = roomId;
|
this.lists[listKey].roomIndexToRoomId[i] = roomId;
|
||||||
}
|
}
|
||||||
console.log(
|
console.log(
|
||||||
"SYNC",
|
"SYNC",
|
||||||
listIndex,
|
listKey,
|
||||||
op.range[0],
|
op.range[0],
|
||||||
op.range[1],
|
op.range[1],
|
||||||
(op.room_ids || []).join(" "),
|
(op.room_ids || []).join(" "),
|
||||||
@ -381,7 +389,7 @@ export class SlidingSync {
|
|||||||
if (gapIndex !== -1) {
|
if (gapIndex !== -1) {
|
||||||
// we already have a DELETE operation to process, so process it
|
// we already have a DELETE operation to process, so process it
|
||||||
// Everything higher than the gap needs to be shifted left.
|
// Everything higher than the gap needs to be shifted left.
|
||||||
this.removeEntry(listIndex, gapIndex);
|
this.removeEntry(listKey, gapIndex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -389,38 +397,38 @@ export class SlidingSync {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shiftRight(listIndex, hi, low) {
|
shiftRight(listKey, hi, low) {
|
||||||
// l h
|
// l h
|
||||||
// 0,1,2,3,4 <- before
|
// 0,1,2,3,4 <- before
|
||||||
// 0,1,2,2,3 <- after, hi is deleted and low is duplicated
|
// 0,1,2,2,3 <- after, hi is deleted and low is duplicated
|
||||||
for (let i = hi; i > low; i--) {
|
for (let i = hi; i > low; i--) {
|
||||||
if (this.isIndexInRange(this.lists[listIndex], i)) {
|
if (this.isIndexInRange(this.lists[listKey], i)) {
|
||||||
this.lists[listIndex].roomIndexToRoomId[i] =
|
this.lists[listKey].roomIndexToRoomId[i] =
|
||||||
this.lists[listIndex].roomIndexToRoomId[
|
this.lists[listKey].roomIndexToRoomId[
|
||||||
i - 1
|
i - 1
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shiftLeft(listIndex, hi, low) {
|
shiftLeft(listKey, hi, low) {
|
||||||
// l h
|
// l h
|
||||||
// 0,1,2,3,4 <- before
|
// 0,1,2,3,4 <- before
|
||||||
// 0,1,3,4,4 <- after, low is deleted and hi is duplicated
|
// 0,1,3,4,4 <- after, low is deleted and hi is duplicated
|
||||||
for (let i = low; i < hi; i++) {
|
for (let i = low; i < hi; i++) {
|
||||||
if (this.isIndexInRange(this.lists[listIndex], i)) {
|
if (this.isIndexInRange(this.lists[listKey], i)) {
|
||||||
this.lists[listIndex].roomIndexToRoomId[i] =
|
this.lists[listKey].roomIndexToRoomId[i] =
|
||||||
this.lists[listIndex].roomIndexToRoomId[
|
this.lists[listKey].roomIndexToRoomId[
|
||||||
i + 1
|
i + 1
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeEntry(listIndex, index) {
|
removeEntry(listKey, index) {
|
||||||
// work out the max index
|
// work out the max index
|
||||||
let max = -1;
|
let max = -1;
|
||||||
for (const n in this.lists[listIndex].roomIndexToRoomId) {
|
for (const n in this.lists[listKey].roomIndexToRoomId) {
|
||||||
if (Number(n) > max) {
|
if (Number(n) > max) {
|
||||||
max = Number(n);
|
max = Number(n);
|
||||||
}
|
}
|
||||||
@ -429,14 +437,14 @@ export class SlidingSync {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Everything higher than the gap needs to be shifted left.
|
// Everything higher than the gap needs to be shifted left.
|
||||||
this.shiftLeft(listIndex, max, index);
|
this.shiftLeft(listKey, max, index);
|
||||||
delete this.lists[listIndex].roomIndexToRoomId[max];
|
delete this.lists[listKey].roomIndexToRoomId[max];
|
||||||
}
|
}
|
||||||
|
|
||||||
addEntry(listIndex, index) {
|
addEntry(listKey, index) {
|
||||||
// work out the max index
|
// work out the max index
|
||||||
let max = -1;
|
let max = -1;
|
||||||
for (const n in this.lists[listIndex].roomIndexToRoomId) {
|
for (const n in this.lists[listKey].roomIndexToRoomId) {
|
||||||
if (Number(n) > max) {
|
if (Number(n) > max) {
|
||||||
max = Number(n);
|
max = Number(n);
|
||||||
}
|
}
|
||||||
@ -445,7 +453,7 @@ export class SlidingSync {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Everything higher than the gap needs to be shifted right, +1 so we don't delete the highest element
|
// Everything higher than the gap needs to be shifted right, +1 so we don't delete the highest element
|
||||||
this.shiftRight(listIndex, max+1, index);
|
this.shiftRight(listKey, max+1, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
isIndexInRange(list, i) {
|
isIndexInRange(list, i) {
|
||||||
@ -461,18 +469,3 @@ export class SlidingSync {
|
|||||||
const sleep = (ms) => {
|
const sleep = (ms) => {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
};
|
};
|
||||||
|
|
||||||
// SYNC 0 2 a b c; SYNC 6 8 d e f; DELETE 7; INSERT 0 e;
|
|
||||||
// 0 1 2 3 4 5 6 7 8
|
|
||||||
// a b c d e f
|
|
||||||
// a b c d _ f
|
|
||||||
// e a b c d f <--- c=3 is wrong as we are not tracking it, ergo we need to see if `i` is in range else drop it
|
|
||||||
const indexInRange = (listIndex, i) => {
|
|
||||||
let isInRange = false;
|
|
||||||
activeLists[listIndex].activeRanges.forEach((r) => {
|
|
||||||
if (r[0] <= i && i <= r[1]) {
|
|
||||||
isInRange = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return isInRange;
|
|
||||||
};
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user