Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: flickering loading indicator on batch delete #10763

Merged
merged 1 commit into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions changelog/unreleased/bugfix-flickering-loading-indicator
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Bugfix: Flickering loading indicator

The flickering loading indicator when batch deleting a lot of files has been fixed.

We also added a request limit that stops the network from being overrun with concurrent requests.

https://github.com/owncloud/web/pull/10763
83 changes: 44 additions & 39 deletions packages/web-app-files/src/store/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import PQueue from 'p-queue'

import { getParentPaths } from '@ownclouders/web-pkg'
import { ConfigurationManager, getParentPaths } from '@ownclouders/web-pkg'
import {
buildShare,
buildCollaboratorShare,
Expand All @@ -18,7 +18,7 @@ import {
SpaceResource
} from '@ownclouders/web-client/src/helpers'
import { WebDAV } from '@ownclouders/web-client/src/webdav'
import { ClientService, LoadingTaskCallbackArguments } from '@ownclouders/web-pkg'
import { ClientService } from '@ownclouders/web-pkg'
import { Language } from 'vue3-gettext'
import { eventBus } from '@ownclouders/web-pkg'
import { AncestorMetaData } from '@ownclouders/web-pkg'
Expand Down Expand Up @@ -165,7 +165,7 @@ export default {
space: SpaceResource
files: Resource[]
clientService: ClientService
loadingCallbackArgs: LoadingTaskCallbackArguments
configurationManager: ConfigurationManager
firstRun: boolean
} & Language
) {
Expand All @@ -175,47 +175,52 @@ export default {
space,
files,
clientService,
loadingCallbackArgs,
configurationManager,
firstRun = true
} = options
const { setProgress } = loadingCallbackArgs
const promises = []
const removedFiles = []
for (const [i, file] of files.entries()) {
const promise = clientService.webdav
.deleteFile(space, file)
.then(() => {
removedFiles.push(file)
})
.catch((error) => {
let title = $gettext('Failed to delete "%{file}"', { file: file.name })
if (error.statusCode === 423) {
if (firstRun) {
return context.dispatch('deleteFiles', {
...options,
space,
files: [file],
clientService,
firstRun: false
const removedFiles: Resource[] = []

const queue = new PQueue({
concurrency: configurationManager.options.concurrentRequests.resourceBatchActions
})

const promises = files.map((file) =>
queue.add(() =>
clientService.webdav
.deleteFile(space, file)
.then(() => {
removedFiles.push(file)
})
.catch((error) => {
let title = $gettext('Failed to delete "%{file}"', { file: file.name })
if (error.statusCode === 423) {
if (firstRun) {
return context.dispatch('deleteFiles', {
...options,
space,
files: [file],
clientService,
configurationManager,
firstRun: false
})
}

title = $gettext('Failed to delete "%{file}" - the file is locked', {
file: file.name
})
}
context.dispatch(
'showErrorMessage',
{
title,
error
},
{ root: true }
)
})
)
)

title = $gettext('Failed to delete "%{file}" - the file is locked', { file: file.name })
}
context.dispatch(
'showErrorMessage',
{
title,
error
},
{ root: true }
)
})
.finally(() => {
setProgress({ total: files.length, current: i + 1 })
})
promises.push(promise)
}
return Promise.all(promises).then(() => {
context.commit('REMOVE_FILES', removedFiles)
context.commit('REMOVE_FILES_FROM_SEARCHED', removedFiles)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,61 +179,58 @@ export const useFileActionsDeleteResources = ({ store }: { store?: Store<any> })

return Object.values(resourceSpaceMapping).map(
({ space: spaceForDeletion, resources: resourcesForDeletion }) => {
return loadingService.addTask(
(loadingCallbackArgs) => {
return store
.dispatch('Files/deleteFiles', {
...language,
space: spaceForDeletion,
files: resourcesForDeletion,
clientService,
loadingCallbackArgs
})
.then(async () => {
// Load quota
if (
isLocationSpacesActive(router, 'files-spaces-generic') &&
!['public', 'share'].includes(spaceForDeletion?.driveType)
) {
if (unref(hasSpacesEnabled)) {
const graphClient = clientService.graphAuthenticated
const driveResponse = await graphClient.drives.getDrive(
unref(resources)[0].storageId
)
store.commit('runtime/spaces/UPDATE_SPACE_FIELD', {
id: driveResponse.data.id,
field: 'spaceQuota',
value: driveResponse.data.quota
})
} else {
const user = await owncloudSdk.users.getUser(store.getters.user.id)
store.commit('SET_QUOTA', user.quota)
}
}

if (
unref(resourcesToDelete).length &&
isSameResource(unref(resourcesToDelete)[0], store.getters['Files/currentFolder'])
) {
// current folder is being deleted
return router.push(
createFileRouteOptions(spaceForDeletion, {
path: dirname(unref(resourcesToDelete)[0].path),
fileId: unref(resourcesToDelete)[0].parentFolderId
})
return loadingService.addTask(() => {
return store
.dispatch('Files/deleteFiles', {
...language,
space: spaceForDeletion,
files: resourcesForDeletion,
clientService,
configurationManager
})
.then(async () => {
// Load quota
if (
isLocationSpacesActive(router, 'files-spaces-generic') &&
!['public', 'share'].includes(spaceForDeletion?.driveType)
) {
if (unref(hasSpacesEnabled)) {
const graphClient = clientService.graphAuthenticated
const driveResponse = await graphClient.drives.getDrive(
unref(resources)[0].storageId
)
store.commit('runtime/spaces/UPDATE_SPACE_FIELD', {
id: driveResponse.data.id,
field: 'spaceQuota',
value: driveResponse.data.quota
})
} else {
const user = await owncloudSdk.users.getUser(store.getters.user.id)
store.commit('SET_QUOTA', user.quota)
}
}

const activeFilesCount = store.getters['Files/activeFiles'].length
const pageCount = Math.ceil(unref(activeFilesCount) / unref(itemsPerPage))
if (unref(currentPage) > 1 && unref(currentPage) > pageCount) {
// reset pagination to avoid empty lists (happens when deleting all items on the last page)
currentPageQuery.value = pageCount.toString()
}
})
},
{ indeterminate: false }
)
if (
unref(resourcesToDelete).length &&
isSameResource(unref(resourcesToDelete)[0], store.getters['Files/currentFolder'])
) {
// current folder is being deleted
return router.push(
createFileRouteOptions(spaceForDeletion, {
path: dirname(unref(resourcesToDelete)[0].path),
fileId: unref(resourcesToDelete)[0].parentFolderId
})
)
}

const activeFilesCount = store.getters['Files/activeFiles'].length
const pageCount = Math.ceil(unref(activeFilesCount) / unref(itemsPerPage))
if (unref(currentPage) > 1 && unref(currentPage) > pageCount) {
// reset pagination to avoid empty lists (happens when deleting all items on the last page)
currentPageQuery.value = pageCount.toString()
}
})
})
}
)
}
Expand Down