From b3bce86f305a64108d1ffe5e1ea7ff51a6f04d95 Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Thu, 14 Mar 2024 14:24:47 +0100 Subject: [PATCH 1/9] remove side-effect and add lastQueue --- .../CurrentCollection/CollectionPanel.vue | 11 +++- client/src/stores/collectionElementsStore.ts | 52 +++++++++++++------ 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/client/src/components/History/CurrentCollection/CollectionPanel.vue b/client/src/components/History/CurrentCollection/CollectionPanel.vue index 2f58d1356e2a..cd83d5fbc56b 100644 --- a/client/src/components/History/CurrentCollection/CollectionPanel.vue +++ b/client/src/components/History/CurrentCollection/CollectionPanel.vue @@ -43,7 +43,16 @@ const dsc = computed(() => { } return currentCollection; }); -const collectionElements = computed(() => collectionElementsStore.getCollectionElements(dsc.value, offset.value)); + +watch( + () => [dsc.value, offset.value], + () => { + collectionElementsStore.fetchMissingElements(dsc.value, offset.value); + }, + { immediate: true } +); + +const collectionElements = computed(() => collectionElementsStore.getCollectionElements(dsc.value)); const loading = computed(() => collectionElementsStore.isLoadingCollectionElements(dsc.value)); const jobState = computed(() => ("job_state_summary" in dsc.value ? dsc.value.job_state_summary : undefined)); const populatedStateMsg = computed(() => diff --git a/client/src/stores/collectionElementsStore.ts b/client/src/stores/collectionElementsStore.ts index ddeb60102012..eb5dee564504 100644 --- a/client/src/stores/collectionElementsStore.ts +++ b/client/src/stores/collectionElementsStore.ts @@ -4,6 +4,8 @@ import { computed, del, ref, set } from "vue"; import type { CollectionEntry, DCESummary, HDCASummary, HistoryContentItemBase } from "@/api"; import { isHDCA } from "@/api"; import { fetchCollectionDetails, fetchElementsFromCollection } from "@/api/datasetCollections"; +import { mergeArray } from "@/store/historyStore/model/utilities"; +import { ActionSkippedError, LastQueue } from "@/utils/lastQueue"; /** * Represents an element in a collection that has not been fetched yet. @@ -46,10 +48,9 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", } const getCollectionElements = computed(() => { - return (collection: CollectionEntry, offset = 0, limit = FETCH_LIMIT) => { + return (collection: CollectionEntry) => { const storedElements = storedCollectionElements.value[getCollectionKey(collection)] ?? initWithPlaceholderElements(collection); - fetchMissingElements({ collection, storedElements, offset, limit }); return storedElements; }; }); @@ -60,17 +61,22 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", }; }); - async function fetchMissingElements(params: { + const lastQueue = new LastQueue(1000, true); + + type FetchParams = { collection: CollectionEntry; - storedElements: DCEEntry[]; offset: number; limit: number; - }) { - const collectionKey = getCollectionKey(params.collection); + }; + + async function fetchMissing({ collection, offset, limit = FETCH_LIMIT }: FetchParams) { + const collectionKey = getCollectionKey(collection); + const storedElements = getCollectionElements.value(collection); + try { // We should fetch only missing (placeholder) elements from the range - const firstMissingIndexInRange = params.storedElements - .slice(params.offset, params.offset + params.limit) + const firstMissingIndexInRange = storedElements + .slice(offset, offset + limit) .findIndex((element) => isPlaceholder(element) && !element.fetching); if (firstMissingIndexInRange === -1) { @@ -78,26 +84,37 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", return; } // Adjust the offset to the first missing element - params.offset += firstMissingIndexInRange; + offset += firstMissingIndexInRange; set(loadingCollectionElements.value, collectionKey, true); // Mark all elements in the range as fetching - params.storedElements - .slice(params.offset, params.offset + params.limit) + storedElements + .slice(offset, offset + limit) .forEach((element) => isPlaceholder(element) && (element.fetching = true)); + const fetchedElements = await fetchElementsFromCollection({ - entry: params.collection, - offset: params.offset, - limit: params.limit, + entry: collection, + offset: offset, + limit: limit, }); - // Update only the elements that were fetched - params.storedElements.splice(params.offset, fetchedElements.length, ...fetchedElements); - set(storedCollectionElements.value, collectionKey, params.storedElements); + + return fetchedElements; } finally { del(loadingCollectionElements.value, collectionKey); } } + async function fetchMissingElements(collection: CollectionEntry, offset: number) { + try { + const elements = await lastQueue.enqueue(fetchMissing, { collection, offset }); + mergeArray(getCollectionKey(collection), elements, storedCollectionElements.value, "id"); + } catch (e) { + if (!(e instanceof ActionSkippedError)) { + throw e; + } + } + } + async function loadCollectionElements(collection: CollectionEntry) { const elements = await fetchElementsFromCollection({ entry: collection }); set(storedCollectionElements.value, getCollectionKey(collection), elements); @@ -154,5 +171,6 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", loadCollectionElements, saveCollections, getCollectionKey, + fetchMissingElements, }; }); From 6bf0a6922c1b02024aa2cd27adff0253987fde4c Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:32:49 +0100 Subject: [PATCH 2/9] remove loadCollectionElements --- .../CurrentCollection/CollectionPanel.vue | 4 +- .../src/store/historyStore/model/utilities.js | 6 +- client/src/stores/collectionElementsStore.ts | 56 +++++++++++++------ 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/client/src/components/History/CurrentCollection/CollectionPanel.vue b/client/src/components/History/CurrentCollection/CollectionPanel.vue index cd83d5fbc56b..012b5a41e28b 100644 --- a/client/src/components/History/CurrentCollection/CollectionPanel.vue +++ b/client/src/components/History/CurrentCollection/CollectionPanel.vue @@ -52,7 +52,7 @@ watch( { immediate: true } ); -const collectionElements = computed(() => collectionElementsStore.getCollectionElements(dsc.value)); +const collectionElements = computed(() => collectionElementsStore.getCollectionElements(dsc.value) ?? []); const loading = computed(() => collectionElementsStore.isLoadingCollectionElements(dsc.value)); const jobState = computed(() => ("job_state_summary" in dsc.value ? dsc.value.job_state_summary : undefined)); const populatedStateMsg = computed(() => @@ -108,7 +108,7 @@ watch( watch( jobState, () => { - collectionElementsStore.loadCollectionElements(dsc.value); + collectionElementsStore.invalidateCollectionElements(dsc.value); }, { deep: true } ); diff --git a/client/src/store/historyStore/model/utilities.js b/client/src/store/historyStore/model/utilities.js index 86b5ac13bc61..6a7ccb2d3f0c 100644 --- a/client/src/store/historyStore/model/utilities.js +++ b/client/src/store/historyStore/model/utilities.js @@ -1,9 +1,9 @@ -import Vue from "vue"; +import { set } from "vue"; /* This function merges the existing data with new incoming data. */ export function mergeArray(id, payload, items, itemKey) { if (!items[id]) { - Vue.set(items, id, []); + set(items, id, []); } const itemArray = items[id]; for (const item of payload) { @@ -16,7 +16,7 @@ export function mergeArray(id, payload, items, itemKey) { }); } } else { - Vue.set(itemArray, itemIndex, item); + set(itemArray, itemIndex, item); } } } diff --git a/client/src/stores/collectionElementsStore.ts b/client/src/stores/collectionElementsStore.ts index eb5dee564504..586b2eefcdc3 100644 --- a/client/src/stores/collectionElementsStore.ts +++ b/client/src/stores/collectionElementsStore.ts @@ -4,7 +4,6 @@ import { computed, del, ref, set } from "vue"; import type { CollectionEntry, DCESummary, HDCASummary, HistoryContentItemBase } from "@/api"; import { isHDCA } from "@/api"; import { fetchCollectionDetails, fetchElementsFromCollection } from "@/api/datasetCollections"; -import { mergeArray } from "@/store/historyStore/model/utilities"; import { ActionSkippedError, LastQueue } from "@/utils/lastQueue"; /** @@ -26,7 +25,11 @@ export interface ContentPlaceholder { fetching?: boolean; } -export type DCEEntry = ContentPlaceholder | DCESummary; +export type InvalidDCEEntry = (ContentPlaceholder | DCESummary) & { + valid: false; +}; + +export type DCEEntry = ContentPlaceholder | DCESummary | InvalidDCEEntry; const FETCH_LIMIT = 50; @@ -49,9 +52,7 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", const getCollectionElements = computed(() => { return (collection: CollectionEntry) => { - const storedElements = - storedCollectionElements.value[getCollectionKey(collection)] ?? initWithPlaceholderElements(collection); - return storedElements; + return storedCollectionElements.value[getCollectionKey(collection)]; }; }); @@ -64,25 +65,26 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", const lastQueue = new LastQueue(1000, true); type FetchParams = { + storedElements: DCEEntry[]; collection: CollectionEntry; offset: number; limit: number; }; - async function fetchMissing({ collection, offset, limit = FETCH_LIMIT }: FetchParams) { + async function fetchMissing({ storedElements, collection, offset, limit = FETCH_LIMIT }: FetchParams) { const collectionKey = getCollectionKey(collection); - const storedElements = getCollectionElements.value(collection); try { // We should fetch only missing (placeholder) elements from the range const firstMissingIndexInRange = storedElements .slice(offset, offset + limit) - .findIndex((element) => isPlaceholder(element) && !element.fetching); + .findIndex((element) => (isPlaceholder(element) && !element.fetching) || isInvalid(element)); if (firstMissingIndexInRange === -1) { // All elements in the range are already stored or being fetched return; } + // Adjust the offset to the first missing element offset += firstMissingIndexInRange; @@ -98,16 +100,32 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", limit: limit, }); - return fetchedElements; + return { fetchedElements, elementOffset: offset }; } finally { del(loadingCollectionElements.value, collectionKey); } } - async function fetchMissingElements(collection: CollectionEntry, offset: number) { + async function fetchMissingElements(collection: CollectionEntry, offset: number, limit = FETCH_LIMIT) { + const key = getCollectionKey(collection); + let storedElements = storedCollectionElements.value[key]; + + if (!storedElements) { + storedElements = initWithPlaceholderElements(collection); + set(storedCollectionElements.value, key, storedElements); + } + try { - const elements = await lastQueue.enqueue(fetchMissing, { collection, offset }); - mergeArray(getCollectionKey(collection), elements, storedCollectionElements.value, "id"); + const data = await (lastQueue.enqueue( + fetchMissing, + { storedElements, collection, offset, limit }, + key + ) as ReturnType); + + if (data) { + storedElements.splice(data.elementOffset, data.fetchedElements.length, ...data.fetchedElements); + set(storedCollectionElements.value, key, storedElements); + } } catch (e) { if (!(e instanceof ActionSkippedError)) { throw e; @@ -115,9 +133,11 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", } } - async function loadCollectionElements(collection: CollectionEntry) { - const elements = await fetchElementsFromCollection({ entry: collection }); - set(storedCollectionElements.value, getCollectionKey(collection), elements); + async function invalidateCollectionElements(collection: CollectionEntry) { + const storedElements = storedCollectionElements.value[getCollectionKey(collection)] ?? []; + storedElements.forEach((element) => { + (element as InvalidDCEEntry).valid = false; + }); } function saveCollections(historyContentsPayload: HistoryContentItemBase[]) { @@ -152,6 +172,10 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", return "id" in element === false; } + function isInvalid(element: DCEEntry): element is InvalidDCEEntry { + return (element as InvalidDCEEntry)["valid"] === false; + } + function initWithPlaceholderElements(collection: CollectionEntry): ContentPlaceholder[] { const totalElements = collection.element_count ?? 0; const placeholderElements = new Array(totalElements); @@ -168,7 +192,7 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", isLoadingCollectionElements, getCollection, fetchCollection, - loadCollectionElements, + invalidateCollectionElements, saveCollections, getCollectionKey, fetchMissingElements, From eae4c39f1c18130ab1811f2086eb22607ae664f3 Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:45:10 +0100 Subject: [PATCH 3/9] improve types --- client/src/stores/collectionElementsStore.ts | 10 +++------- client/src/utils/lastQueue.ts | 6 +++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/client/src/stores/collectionElementsStore.ts b/client/src/stores/collectionElementsStore.ts index 586b2eefcdc3..51ec0a0ad23f 100644 --- a/client/src/stores/collectionElementsStore.ts +++ b/client/src/stores/collectionElementsStore.ts @@ -62,8 +62,6 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", }; }); - const lastQueue = new LastQueue(1000, true); - type FetchParams = { storedElements: DCEEntry[]; collection: CollectionEntry; @@ -106,6 +104,8 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", } } + const lastQueue = new LastQueue(1000, true); + async function fetchMissingElements(collection: CollectionEntry, offset: number, limit = FETCH_LIMIT) { const key = getCollectionKey(collection); let storedElements = storedCollectionElements.value[key]; @@ -116,11 +116,7 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", } try { - const data = await (lastQueue.enqueue( - fetchMissing, - { storedElements, collection, offset, limit }, - key - ) as ReturnType); + const data = await lastQueue.enqueue(fetchMissing, { storedElements, collection, offset, limit }, key); if (data) { storedElements.splice(data.elementOffset, data.fetchedElements.length, ...data.fetchedElements); diff --git a/client/src/utils/lastQueue.ts b/client/src/utils/lastQueue.ts index 317d7dfa3a3c..ff27690f0b5f 100644 --- a/client/src/utils/lastQueue.ts +++ b/client/src/utils/lastQueue.ts @@ -1,4 +1,4 @@ -type QueuedAction R, R = unknown> = { +type QueuedAction R, R = ReturnType> = { action: T; arg: Parameters[0]; resolve: (value: R) => void; @@ -13,7 +13,7 @@ export class ActionSkippedError extends Error {} * This is useful when promises earlier enqueued become obsolete. * See also: https://stackoverflow.com/questions/53540348/js-async-await-tasks-queue */ -export class LastQueue R, R = unknown> { +export class LastQueue R, R = ReturnType> { throttlePeriod: number; /** Throw an error if a queued action is skipped. This avoids dangling promises */ rejectSkipped: boolean; @@ -34,7 +34,7 @@ export class LastQueue R, R = unknown> { promise?.reject(new ActionSkippedError()); } - async enqueue(action: T, arg: Parameters[0], key: string | number = 0) { + async enqueue(action: T, arg: Parameters[0], key: string | number = 0): Promise { return new Promise((resolve, reject) => { this.skipPromise(key); this.queuedPromises[key] = { action, arg, resolve, reject }; From 7b33959630f887a7734e2147becf26adb4e460d3 Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:53:02 +0100 Subject: [PATCH 4/9] fix tests --- .../stores/collectionElementsStore.test.ts | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/client/src/stores/collectionElementsStore.test.ts b/client/src/stores/collectionElementsStore.test.ts index b34435288554..4a2c7be2e2ce 100644 --- a/client/src/stores/collectionElementsStore.test.ts +++ b/client/src/stores/collectionElementsStore.test.ts @@ -38,13 +38,15 @@ describe("useCollectionElementsStore", () => { expect(store.storedCollectionElements).toEqual({}); expect(store.isLoadingCollectionElements(collection)).toEqual(false); - const offset = 0; + // Getting collection elements should be side effect free + store.getCollectionElements(collection); + expect(store.isLoadingCollectionElements(collection)).toEqual(false); + await flushPromises(); + expect(fetchCollectionElements).not.toHaveBeenCalled(); + const limit = 5; - // Getting collection elements should trigger a fetch and change the loading state - store.getCollectionElements(collection, offset, limit); - expect(store.isLoadingCollectionElements(collection)).toEqual(true); + store.fetchMissingElements(collection, 0, limit); await flushPromises(); - expect(store.isLoadingCollectionElements(collection)).toEqual(false); expect(fetchCollectionElements).toHaveBeenCalled(); const collectionKey = store.getCollectionKey(collection); @@ -70,18 +72,20 @@ describe("useCollectionElementsStore", () => { const offset = 0; const limit = storedCount; // Getting the same collection elements range should not trigger a fetch - store.getCollectionElements(collection, offset, limit); + store.fetchMissingElements(collection, offset, limit); expect(store.isLoadingCollectionElements(collection)).toEqual(false); expect(fetchCollectionElements).not.toHaveBeenCalled(); }); it("should fetch only missing elements if the requested range is not already stored", async () => { + jest.useFakeTimers(); + const totalElements = 10; const collection: HDCASummary = mockCollection("1", totalElements); const store = useCollectionElementsStore(); const initialElements = 3; - store.getCollectionElements(collection, 0, initialElements); + store.fetchMissingElements(collection, 0, initialElements); await flushPromises(); expect(fetchCollectionElements).toHaveBeenCalled(); const collectionKey = store.getCollectionKey(collection); @@ -92,11 +96,10 @@ describe("useCollectionElementsStore", () => { const offset = 2; const limit = 5; - // Getting collection elements should trigger a fetch in this case - store.getCollectionElements(collection, offset, limit); - expect(store.isLoadingCollectionElements(collection)).toEqual(true); + // Fetching collection elements should trigger a fetch in this case + store.fetchMissingElements(collection, offset, limit); + jest.runAllTimers(); await flushPromises(); - expect(store.isLoadingCollectionElements(collection)).toEqual(false); expect(fetchCollectionElements).toHaveBeenCalled(); elements = store.storedCollectionElements[collectionKey]; From a9505a5041591314910933004c96eeca3a3cae23 Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:25:41 +0100 Subject: [PATCH 5/9] insert unexpected elements --- client/src/stores/collectionElementsStore.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/src/stores/collectionElementsStore.ts b/client/src/stores/collectionElementsStore.ts index 51ec0a0ad23f..b6b38c133fd9 100644 --- a/client/src/stores/collectionElementsStore.ts +++ b/client/src/stores/collectionElementsStore.ts @@ -4,6 +4,7 @@ import { computed, del, ref, set } from "vue"; import type { CollectionEntry, DCESummary, HDCASummary, HistoryContentItemBase } from "@/api"; import { isHDCA } from "@/api"; import { fetchCollectionDetails, fetchElementsFromCollection } from "@/api/datasetCollections"; +import { ensureDefined } from "@/utils/assertions"; import { ActionSkippedError, LastQueue } from "@/utils/lastQueue"; /** @@ -119,7 +120,13 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", const data = await lastQueue.enqueue(fetchMissing, { storedElements, collection, offset, limit }, key); if (data) { - storedElements.splice(data.elementOffset, data.fetchedElements.length, ...data.fetchedElements); + const from = data.elementOffset; + const to = from + data.fetchedElements.length; + + for (let index = from; index < to; index++) { + storedElements[index] = ensureDefined(data.fetchedElements[index - from]); + } + set(storedCollectionElements.value, key, storedElements); } } catch (e) { From 5bbdb38c57314477d4c63dab3018a573fdce8098 Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:29:45 +0100 Subject: [PATCH 6/9] also fetch instead of just invalidating --- .../src/components/History/CurrentCollection/CollectionPanel.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/components/History/CurrentCollection/CollectionPanel.vue b/client/src/components/History/CurrentCollection/CollectionPanel.vue index 012b5a41e28b..10c830ca4601 100644 --- a/client/src/components/History/CurrentCollection/CollectionPanel.vue +++ b/client/src/components/History/CurrentCollection/CollectionPanel.vue @@ -109,6 +109,7 @@ watch( jobState, () => { collectionElementsStore.invalidateCollectionElements(dsc.value); + collectionElementsStore.fetchMissingElements(dsc.value, offset.value); }, { deep: true } ); From 9e2b3330fd26e20eff6d0f880769a78aae145e0c Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:38:22 +0100 Subject: [PATCH 7/9] make manual splice reactive --- client/src/stores/collectionElementsStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/stores/collectionElementsStore.ts b/client/src/stores/collectionElementsStore.ts index b6b38c133fd9..d265becded8b 100644 --- a/client/src/stores/collectionElementsStore.ts +++ b/client/src/stores/collectionElementsStore.ts @@ -124,7 +124,7 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", const to = from + data.fetchedElements.length; for (let index = from; index < to; index++) { - storedElements[index] = ensureDefined(data.fetchedElements[index - from]); + set(storedElements, index, ensureDefined(data.fetchedElements[index - from])); } set(storedCollectionElements.value, key, storedElements); From 740449394c4fe7c00756c635eb6b8b1ebd79acb5 Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:49:05 +0100 Subject: [PATCH 8/9] add edge case for element_count bug --- client/src/stores/collectionElementsStore.ts | 29 ++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/client/src/stores/collectionElementsStore.ts b/client/src/stores/collectionElementsStore.ts index d265becded8b..ac04e1839611 100644 --- a/client/src/stores/collectionElementsStore.ts +++ b/client/src/stores/collectionElementsStore.ts @@ -74,19 +74,25 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", const collectionKey = getCollectionKey(collection); try { - // We should fetch only missing (placeholder) elements from the range - const firstMissingIndexInRange = storedElements - .slice(offset, offset + limit) - .findIndex((element) => (isPlaceholder(element) && !element.fetching) || isInvalid(element)); + if (storedElements.length !== 0) { + // We should fetch only missing (placeholder) elements from the range + const firstMissingIndexInRange = storedElements + .slice(offset, offset + limit) + .findIndex((element) => (isPlaceholder(element) && !element.fetching) || isInvalid(element)); + + if (firstMissingIndexInRange === -1) { + // All elements in the range are already stored or being fetched + return; + } - if (firstMissingIndexInRange === -1) { - // All elements in the range are already stored or being fetched - return; + // Adjust the offset to the first missing element + offset += firstMissingIndexInRange; + } else { + // Edge case where element_count is incorrect + // TODO: remove me once element_count is reported reliably + offset = 0; } - // Adjust the offset to the first missing element - offset += firstMissingIndexInRange; - set(loadingCollectionElements.value, collectionKey, true); // Mark all elements in the range as fetching storedElements @@ -124,7 +130,8 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", const to = from + data.fetchedElements.length; for (let index = from; index < to; index++) { - set(storedElements, index, ensureDefined(data.fetchedElements[index - from])); + const element = ensureDefined(data.fetchedElements[index - from]); + set(storedElements, index, element); } set(storedCollectionElements.value, key, storedElements); From af17eb7c3c102915eff820c1d9da91a900322c1d Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:52:48 +0100 Subject: [PATCH 9/9] test element count directly --- client/src/stores/collectionElementsStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/stores/collectionElementsStore.ts b/client/src/stores/collectionElementsStore.ts index ac04e1839611..73c63ade28a2 100644 --- a/client/src/stores/collectionElementsStore.ts +++ b/client/src/stores/collectionElementsStore.ts @@ -74,7 +74,7 @@ export const useCollectionElementsStore = defineStore("collectionElementsStore", const collectionKey = getCollectionKey(collection); try { - if (storedElements.length !== 0) { + if (collection.element_count !== null) { // We should fetch only missing (placeholder) elements from the range const firstMissingIndexInRange = storedElements .slice(offset, offset + limit)