diff options
Diffstat (limited to 'src/server/websocket.ts')
-rw-r--r-- | src/server/websocket.ts | 75 |
1 files changed, 72 insertions, 3 deletions
diff --git a/src/server/websocket.ts b/src/server/websocket.ts index 2acdaa5a3..e94b48f4f 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -317,15 +317,83 @@ 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 +401,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 = ''; |