Skip to content

Commit

Permalink
Merge pull request #95 from concord-consortium/187178550-batch-delete…
Browse files Browse the repository at this point in the history
…-items

187178550 Batch Delete Item Requests
  • Loading branch information
tealefristoe authored Aug 14, 2024
2 parents 5007b36 + 23c64c2 commit 55f5fa8
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 32 deletions.
4 changes: 4 additions & 0 deletions src/lib/CodapInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@ const codapInterface = {
return initialInteractiveFrame;
},

getCodapVersion() {
return this.getInitialInteractiveFrame().codapVersion;
},

/**
* Returns the interactive state.
*
Expand Down
42 changes: 30 additions & 12 deletions src/lib/codap-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,22 @@ export class CodapHelper {
? unsharedResult.values : [];
const requests: CodapRequest[] = [];
if (emptyItems.length && (nonEmptyItemCount || unsharedCases.length)) {
emptyItems.forEach(item => {
// delete any "empty" user items as long as there are non-empty user items
requests.push({
action: "delete",
resource: collaboratorsResource(dataContextName, `itemByID[${item.id}]`),
});
});
// delete any "empty" user items as long as there are non-empty user items
if (codapInterface.getCodapVersion()) {
// Codap v3 (when codapVersion began to be sent) supports batched delete item requests
requests.push({
action: "delete",
resource: collaboratorsResource(dataContextName, `item`),
values: emptyItems.map(item => ({ id: item.id }))
})
} else {
emptyItems.forEach(item => {
requests.push({
action: "delete",
resource: collaboratorsResource(dataContextName, `itemByID[${item.id}]`),
});
});
}
}
// apply required sharing values to currently "unshared" cases.
// this occurs when items are generated from other plugins, for instance.
Expand Down Expand Up @@ -305,11 +314,20 @@ export class CodapHelper {
}

static async removeItems(dataContextName: string, itemValues: CodapItem[]) {
const requests = itemValues.map(item => ({
action: "delete",
resource: dataContextResource(dataContextName, `itemByID[${item.id}]`)
}));
return codapInterface.sendRequest(requests);
if (codapInterface.getCodapVersion()) {
// Codap v3 (when codapVersion began to be sent) supports batched delete item requests
return codapInterface.sendRequest({
action: "delete",
resource: dataContextResource(dataContextName, `item`),
values: itemValues.map(item => ({ id: item.id }))
});
} else {
const requests = itemValues.map(item => ({
action: "delete",
resource: dataContextResource(dataContextName, `itemByID[${item.id}]`)
}));
return codapInterface.sendRequest(requests);
}
}

static async addNewCollaborationCollections(dataContextName: string, personalDataKey: string,
Expand Down
49 changes: 29 additions & 20 deletions src/lib/firebase-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export class FirebaseItemHandlers {
hasReceivedInitialItems: boolean = false;
orderedItemIds: string[] = [];
itemIds: Set<string> = new Set();
queuedItems: CodapItem[] = [];
addItemsQueue: CodapItem[] = [];
removeItemsQueue: CodapItem[] = [];

constructor(user: string, userItemDataRef: firebase.database.Reference, clientHandlers: ClientItemsHandlers) {
this.user = user;
Expand Down Expand Up @@ -56,29 +57,37 @@ export class FirebaseItemHandlers {
this.clientHandlers.itemsAdded(this.user, items);

this.orderedItemIds = itemData.order;
items.forEach(item => this.itemIds.add(item.id));
items.forEach(item => this.itemIds.add(`${item.id}`));
}
};

sortedQueuedItems = () => {
if (this.queuedItems.length <= 1) return this.queuedItems;
if (this.addItemsQueue.length <= 1) return this.addItemsQueue;
const idIndexMap: { [id: string]: number } = {};
this.orderedItemIds.forEach((id, index) => idIndexMap[id] = index);
this.queuedItems.sort((a, b) => idIndexMap[a.id] - idIndexMap[b.id]);
return this.queuedItems;
this.addItemsQueue.sort((a, b) => idIndexMap[a.id] - idIndexMap[b.id]);
return this.addItemsQueue;
};

// When a batch of cases are added at once, we get multiple child_added
// When a batch of cases are added or removed at once, we get multiple child_added or child_removed
// notifications and a single update to the ordered IDs array in some order.
// We queue the new items until we have the number of items that matches the
// ordered IDs array and then add the new items in the correct order as a batch.
addQueuedItemsIfSynced = () => {
if (this.queuedItems.length &&
(this.itemIds.size + this.queuedItems.length === this.orderedItemIds.length)) {
const items = this.sortedQueuedItems();
this.clientHandlers.itemsAdded(this.user, items);
items.forEach(item => this.itemIds.add(item.id));
this.queuedItems = [];
// We queue the new/deleted items until we have the number of items that matches the
// ordered IDs array and then add/remove the items in the correct order as a batch.
handleQueuedItemsIfSynced = () => {
const sizeWithQueues = this.itemIds.size + this.addItemsQueue.length - this.removeItemsQueue.length;
if ((this.addItemsQueue.length || this.removeItemsQueue.length) &&
sizeWithQueues === this.orderedItemIds.length) {
if (this.addItemsQueue.length) {
const items = this.sortedQueuedItems();
this.clientHandlers.itemsAdded(this.user, items);
items.forEach(item => this.itemIds.add(`${item.id}`));
this.addItemsQueue = [];
}
if (this.removeItemsQueue.length) {
this.clientHandlers.itemsRemoved(this.user, this.removeItemsQueue);
this.removeItemsQueue.forEach(item => this.itemIds.delete(`${item.id}`));
this.removeItemsQueue = [];
}
}
};

Expand All @@ -87,8 +96,8 @@ export class FirebaseItemHandlers {
const itemValues = data && data.val() as CodapItemValues;
if (data && itemValues) {
const item: CodapItem = { id: data.key as string, values: itemValues };
this.queuedItems.push(item);
this.addQueuedItemsIfSynced();
this.addItemsQueue.push(item);
this.handleQueuedItemsIfSynced();
}
};

Expand All @@ -104,16 +113,16 @@ export class FirebaseItemHandlers {
const itemValues = data && data.val() as CodapItemValues;
if (data && itemValues) {
const item: CodapItem = { id: data.key as string, values: itemValues };
this.clientHandlers.itemsRemoved(this.user, [item]);
this.itemIds.delete(item.id);
this.removeItemsQueue.push(item);
this.handleQueuedItemsIfSynced();
}
};

handleOrderChanged = (data: firebase.database.DataSnapshot | null) => {
const order = data?.val();
if (order) {
this.orderedItemIds = order;
this.addQueuedItemsIfSynced();
this.handleQueuedItemsIfSynced();
}
};
}

0 comments on commit 55f5fa8

Please sign in to comment.