aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src/services/DropService.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files/src/services/DropService.ts')
-rw-r--r--apps/files/src/services/DropService.ts143
1 files changed, 27 insertions, 116 deletions
diff --git a/apps/files/src/services/DropService.ts b/apps/files/src/services/DropService.ts
index 1013baeda6c..58d8239deb1 100644
--- a/apps/files/src/services/DropService.ts
+++ b/apps/files/src/services/DropService.ts
@@ -4,17 +4,14 @@
*/
import type { Upload } from '@nextcloud/upload'
-import type { RootDirectory } from './DropServiceUtils'
-import { Folder, Node, NodeStatus, davRootPath } from '@nextcloud/files'
+import { Folder, Node, NodeStatus } from '@nextcloud/files'
import { getUploader, hasConflict } from '@nextcloud/upload'
-import { join } from 'path'
-import { joinPaths } from '@nextcloud/paths'
import { showError, showInfo, showSuccess, showWarning } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n'
import Vue from 'vue'
-import { Directory, traverseTree, resolveConflict, createDirectoryIfNotExists } from './DropServiceUtils'
+import { resolveConflict } from './DropServiceUtils'
import { handleCopyMoveNodeTo } from '../actions/moveOrCopyAction'
import { MoveCopyAction } from '../actions/moveOrCopyActionUtils'
import logger from '../logger.ts'
@@ -28,7 +25,7 @@ import logger from '../logger.ts'
*
* @param items the list of DataTransferItems
*/
-export const dataTransferToFileTree = async (items: DataTransferItem[]): Promise<RootDirectory> => {
+export async function onDropExternalFiles(items: DataTransferItem[]): Promise<Upload[]> {
// Check if the browser supports the Filesystem API
// We need to cache the entries to prevent Blink engine bug that clears
// the list (`data.items`) after first access props of one of the entries
@@ -41,125 +38,39 @@ export const dataTransferToFileTree = async (items: DataTransferItem[]): Promise
return true
}).map((item) => {
// MDN recommends to try both, as it might be renamed in the future
- return (item as unknown as { getAsEntry?: () => FileSystemEntry|undefined })?.getAsEntry?.()
- ?? item?.webkitGetAsEntry?.()
- ?? item
- }) as (FileSystemEntry | DataTransferItem)[]
-
- let warned = false
- const fileTree = new Directory('root') as RootDirectory
-
- // Traverse the file tree
- for (const entry of entries) {
- // Handle browser issues if Filesystem API is not available. Fallback to File API
- if (entry instanceof DataTransferItem) {
- logger.warn('Could not get FilesystemEntry of item, falling back to file')
-
- const file = entry.getAsFile()
- if (file === null) {
- logger.warn('Could not process DataTransferItem', { type: entry.type, kind: entry.kind })
- showError(t('files', 'One of the dropped files could not be processed'))
- continue
- }
-
- // Warn the user that the browser does not support the Filesystem API
- // we therefore cannot upload directories recursively.
- if (file.type === 'httpd/unix-directory' || !file.type) {
- if (!warned) {
- logger.warn('Browser does not support Filesystem API. Directories will not be uploaded')
- showWarning(t('files', 'Your browser does not support the Filesystem API. Directories will not be uploaded'))
- warned = true
- }
- continue
+ return (item as unknown as DataTransferItem & { getAsEntry?: () => FileSystemEntry|undefined }).getAsEntry?.()
+ ?? item.webkitGetAsEntry?.()
+ ?? item.getAsFile()
+ }).filter((item: FileSystemEntry | File | null) => {
+ if (item === null) {
+ showWarning(t('files', 'One of the dropped files could not be processed'))
+ return false
}
+ return true
+ }) as (FileSystemEntry | File)[]
- fileTree.contents.push(file)
- continue
- }
-
- // Use Filesystem API
- try {
- fileTree.contents.push(await traverseTree(entry))
- } catch (error) {
- // Do not throw, as we want to continue with the other files
- logger.error('Error while traversing file tree', { error })
- }
- }
-
- return fileTree
-}
-
-export const onDropExternalFiles = async (root: RootDirectory, destination: Folder, contents: Node[]): Promise<Upload[]> => {
- const uploader = getUploader()
-
- // Check for conflicts on root elements
- if (await hasConflict(root.contents, contents)) {
- root.contents = await resolveConflict(root.contents, destination, contents)
- }
-
- if (root.contents.length === 0) {
- logger.info('No files to upload', { root })
- showInfo(t('files', 'No files to upload'))
+ if (entries.length === 0) {
+ logger.info('No valid files were dropped.')
return []
}
- // Let's process the files
- logger.debug(`Uploading files to ${destination.path}`, { root, contents: root.contents })
- const queue = [] as Promise<Upload>[]
-
- const uploadDirectoryContents = async (directory: Directory, path: string) => {
- for (const file of directory.contents) {
- // This is the relative path to the resource
- // from the current uploader destination
- const relativePath = join(path, file.name)
-
- // If the file is a directory, we need to create it first
- // then browse its tree and upload its contents.
- if (file instanceof Directory) {
- const absolutePath = joinPaths(davRootPath, destination.path, relativePath)
- try {
- console.debug('Processing directory', { relativePath })
- await createDirectoryIfNotExists(absolutePath)
- await uploadDirectoryContents(file, relativePath)
- } catch (error) {
- showError(t('files', 'Unable to create the directory {directory}', { directory: file.name }))
- logger.error('', { error, absolutePath, directory: file })
- }
- continue
- }
-
- // If we've reached a file, we can upload it
- logger.debug('Uploading file to ' + join(destination.path, relativePath), { file })
-
- // Overriding the root to avoid changing the current uploader context
- queue.push(uploader.upload(relativePath, file, destination.source))
- }
- }
-
- // Pause the uploader to prevent it from starting
- // while we compute the queue
- uploader.pause()
-
- // Upload the files. Using '/' as the starting point
- // as we already adjusted the uploader destination
- await uploadDirectoryContents(root, '/')
- uploader.start()
-
- // Wait for all promises to settle
- const results = await Promise.allSettled(queue)
+ // Get the uploader and start the batch upload
+ const uploader = getUploader()
- // Check for errors
- const errors = results.filter(result => result.status === 'rejected')
- if (errors.length > 0) {
- logger.error('Error while uploading files', { errors })
+ try {
+ uploader.pause()
+ const promise = uploader.batchUpload('', entries)
+ uploader.start()
+
+ const uploads = await promise
+ logger.debug('Files uploaded successfully', { uploads })
+ showSuccess(t('files', 'Files uploaded successfully'))
+ return uploads
+ } catch (error) {
+ logger.error('Error while uploading files', { error })
showError(t('files', 'Some files could not be uploaded'))
return []
}
-
- logger.debug('Files uploaded successfully')
- showSuccess(t('files', 'Files uploaded successfully'))
-
- return Promise.all(queue)
}
export const onDropInternalFiles = async (nodes: Node[], destination: Folder, contents: Node[], isCopy = false) => {