From b0f47e12019ed191152d1d6726b8321e2f38176d Mon Sep 17 00:00:00 2001 From: Christopher Ng Date: Thu, 11 Apr 2024 10:07:05 -0700 Subject: [PATCH] fix: Fix incorrect directory contents when navigating quickly Signed-off-by: Christopher Ng --- lib/composables/dav.ts | 74 ++++++++++++++++++++++++++++++++---------- package-lock.json | 6 ++++ package.json | 1 + 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/lib/composables/dav.ts b/lib/composables/dav.ts index 84ca5cf6..e1417c00 100644 --- a/lib/composables/dav.ts +++ b/lib/composables/dav.ts @@ -27,6 +27,7 @@ import { davGetClient, davGetDefaultPropfind, davGetRecentSearch, davRemoteURL, import { generateRemoteUrl } from '@nextcloud/router' import { join } from 'path' import { computed, onMounted, ref, watch } from 'vue' +import { CancelablePromise } from 'cancelable-promise' /** * Handle file loading using WebDAV @@ -68,6 +69,48 @@ export const useDAVFiles = function( const resultToNode = (result: FileStat) => davResultToNode(result, defaultRootPath.value, defaultRemoteUrl.value) + const getRecentNodes = (): CancelablePromise => { + const controller = new AbortController() + // unix timestamp in seconds, two weeks ago + const lastTwoWeek = Math.round(Date.now() / 1000) - (60 * 60 * 24 * 14) + return new CancelablePromise(async (resolve, reject, onCancel) => { + onCancel(() => controller.abort()) + try { + const { data } = await client.value.search('/', { + signal: controller.signal, + details: true, + data: davGetRecentSearch(lastTwoWeek), + }) as ResponseDataDetailed + const nodes = data.results.map(resultToNode) + resolve(nodes) + } catch (error) { + reject(error) + } + }) + } + + const getNodes = (): CancelablePromise => { + const controller = new AbortController() + return new CancelablePromise(async (resolve, reject, onCancel) => { + onCancel(() => controller.abort()) + try { + const results = await client.value.getDirectoryContents(`${defaultRootPath.value}${currentPath.value}`, { + signal: controller.signal, + details: true, + data: davGetDefaultPropfind(), + }) as ResponseDataDetailed + let nodes = results.data.map(resultToNode) + // Hack for the public endpoint which always returns folder itself + if (isPublicEndpoint.value) { + nodes = nodes.filter((file) => file.path !== currentPath.value) + } + resolve(nodes) + } catch (error) { + reject(error) + } + }) + } + /** * All files in current view and path */ @@ -78,6 +121,11 @@ export const useDAVFiles = function( */ const isLoading = ref(true) + /** + * The cancelable promise + */ + const promise = ref>(null) + /** * Create a new directory in the current path * @param name Name of the new directory @@ -112,31 +160,21 @@ export const useDAVFiles = function( * Force reload files using the DAV client */ async function loadDAVFiles() { + if (promise.value) { + promise.value.cancel() + } isLoading.value = true if (currentView.value === 'favorites') { - files.value = await getFavoriteNodes(client.value, currentPath.value, defaultRootPath.value) + promise.value = getFavoriteNodes(client.value, currentPath.value, defaultRootPath.value) } else if (currentView.value === 'recent') { - // unix timestamp in seconds, two weeks ago - const lastTwoWeek = Math.round(Date.now() / 1000) - (60 * 60 * 24 * 14) - const { data } = await client.value.search('/', { - details: true, - data: davGetRecentSearch(lastTwoWeek), - }) as ResponseDataDetailed - files.value = data.results.map(resultToNode) + promise.value = getRecentNodes() } else { - const results = await client.value.getDirectoryContents(`${defaultRootPath.value}${currentPath.value}`, { - details: true, - data: davGetDefaultPropfind(), - }) as ResponseDataDetailed - files.value = results.data.map(resultToNode) - - // Hack for the public endpoint which always returns folder itself - if (isPublicEndpoint.value) { - files.value = files.value.filter((file) => file.path !== currentPath.value) - } + promise.value = getNodes() } + files.value = await promise.value as Node[] + promise.value = null isLoading.value = false } diff --git a/package-lock.json b/package-lock.json index 1fc70f8c..b6c32adf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@nextcloud/typings": "^1.8.0", "@types/toastify-js": "^1.12.3", "@vueuse/core": "^10.9.0", + "cancelable-promise": "^4.3.1", "toastify-js": "^1.12.0", "vue-frag": "^1.4.3", "webdav": "^5.5.0" @@ -5260,6 +5261,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cancelable-promise": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/cancelable-promise/-/cancelable-promise-4.3.1.tgz", + "integrity": "sha512-A/8PwLk/T7IJDfUdQ68NR24QHa8rIlnN/stiJEBo6dmVUkD4K14LswG0w3VwdeK/o7qOwRUR1k2MhK5Rpy2m7A==" + }, "node_modules/caniuse-lite": { "version": "1.0.30001571", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001571.tgz", diff --git a/package.json b/package.json index 0a1b3563..f4fc6b1c 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "@nextcloud/typings": "^1.8.0", "@types/toastify-js": "^1.12.3", "@vueuse/core": "^10.9.0", + "cancelable-promise": "^4.3.1", "toastify-js": "^1.12.0", "vue-frag": "^1.4.3", "webdav": "^5.5.0"