aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-05-10 10:12:54 -0400
committerbobzel <zzzman@gmail.com>2023-05-10 10:12:54 -0400
commitbbdefbe2ed56348ceafd83f34d02cc649b84e269 (patch)
tree5d3755a381c5b66c991b6746919d39fc78cd03bd /src
parentebb846116af9c7e65a9d674c765c71c0bf0a7d29 (diff)
parent97a743455e7fa3eee768b1d4d025b9dedc49f370 (diff)
Merge branch 'master' into UI_Update_Eric_Ma
Diffstat (limited to 'src')
-rw-r--r--src/fields/List.ts8
-rw-r--r--src/fields/util.ts2
-rw-r--r--src/server/websocket.ts71
3 files changed, 76 insertions, 5 deletions
diff --git a/src/fields/List.ts b/src/fields/List.ts
index e33627be5..dfd24cf7a 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -77,10 +77,16 @@ const listHandlers: any = {
item[OnUpdate] = updateFunction(list, i + start, item, this);
}
}
+ let hintArray: {val : any, index : number}[] = [];
+ for(let i = start; i < start + deleteCount; i++) {
+ hintArray.push({val : list.__fields[i], index : i});
+ }
const res = list.__fields.splice(start, deleteCount, ...items);
+ // the hint object sends the starting index of the slice and the number
+ // of elements to delete.
this[Update](
items.length === 0 && deleteCount
- ? { op: '$remFromSet', items: removed, length: list.__fields.length }
+ ? { op: '$remFromSet', items: removed, hint : { start : start, deleteCount : deleteCount }, length: list.__fields.length }
: items.length && !deleteCount && start === list.__fields.length
? { op: '$addToSet', items, length: list.__fields.length }
: undefined
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 92f3a69eb..d5b55867e 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -377,7 +377,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
diff?.op === '$addToSet'
? { $addToSet: { ['fields.' + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } }
: diff?.op === '$remFromSet'
- ? { $remFromSet: { ['fields.' + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } }
+ ? { $remFromSet: { ['fields.' + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)), hint : diff.hint } }
: { $set: { ['fields.' + prop]: SerializationHelper.Serialize(value) } };
!op.$set && ((op as any).length = diff.length);
const prevValue = ObjectField.MakeCopy(lastValue as List<any>);
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index 2acdaa5a3..42c84322b 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -317,15 +317,79 @@ export namespace WebSocket {
);
}
+ /**
+ * findClosestIndex() is a helper function that will try to find
+ * the closest index of a list that has the same value as
+ * a specified argument/index pair.
+ * @param list the list to search through
+ * @param indexesToDelete a list of indexes that are already marked for deletion
+ * so they will be ignored
+ * @param value the value of the item to remove
+ * @param hintIndex the index that the element was at on the client's copy of
+ * the data
+ * @returns the closest index with the same value or -1 if the element was not found.
+ */
+ function findClosestIndex(list: any, indexesToDelete: number[], value: any, hintIndex: number) {
+ let closestIndex = -1;
+ for (let i = 0; i < list.length; i++) {
+ if (list[i] === value && !indexesToDelete.includes(i)) {
+ if (Math.abs(i - hintIndex) < Math.abs(closestIndex - hintIndex)) {
+ closestIndex = i;
+ }
+ }
+ }
+ return closestIndex;
+ }
+
+ /**
+ * remFromListField() receives the items to remove and a hint
+ * from the client, and attempts to make the modification to the
+ * server's copy of the data. If server's copy does not match
+ * the client's after removal, the server will SEND BACk
+ * its version to the client.
+ * @param socket the socket that the client is connected on
+ * @param diff an object containing the items to remove and a hint
+ * (the hint contains start index and deleteCount, the number of
+ * items to delete)
+ * @param curListItems the server's current copy of the data
+ */
function remFromListField(socket: Socket, diff: Diff, curListItems?: Transferable): void {
diff.diff.$set = diff.diff.$remFromSet;
delete diff.diff.$remFromSet;
const updatefield = Array.from(Object.keys(diff.diff.$set))[0];
const remListItems = diff.diff.$set[updatefield].fields;
const curList = (curListItems as any)?.fields?.[updatefield.replace('fields.', '')]?.fields.filter((f: any) => f !== null) || [];
- diff.diff.$set[updatefield].fields = curList?.filter(
- (curItem: any) => !remListItems.some((remItem: any) => (remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem))
- );
+ const hint = diff.diff.$set.hint;
+
+ if (hint) {
+ // indexesToRemove stores the indexes that we mark for deletion, which is later used to filter the list (delete the elements)
+ let indexesToRemove: number[] = [];
+ for (let i = 0; i < hint.deleteCount; i++) {
+ if (curList[i + hint.start] === remListItems[i]) {
+ indexesToRemove.push(i + hint.start);
+ continue;
+ }
+
+ let closestIndex = findClosestIndex(curList, indexesToRemove, remListItems[i], i + hint.start);
+ if (closestIndex !== -1) {
+ indexesToRemove.push(closestIndex);
+ } else {
+ console.log('Item to delete was not found - index = -1');
+ }
+ }
+
+ diff.diff.$set[updatefield].fields = curList?.filter((curItem: any, index: number) => !indexesToRemove.includes(index));
+ } else {
+ // go back to the original way to delete if we didn't receive
+ // a hint from the client
+ diff.diff.$set[updatefield].fields = curList?.filter(
+ (curItem: any) => !remListItems.some((remItem: any) => (remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem))
+ );
+ }
+
+ // if the client and server have different versions of the data after
+ // deletion, they will have different lengths and the server will
+ // send its version of the data to the client
const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length;
delete diff.diff.length;
Database.Instance.update(
@@ -333,6 +397,7 @@ export namespace WebSocket {
diff.diff,
() => {
if (sendBack) {
+ // the two copies are different, so the server sends its copy.
console.log('SEND BACK');
const id = socket.id;
socket.id = '';