Skip to content

Commit 074df09

Browse files
authored
Merge pull request #58680 from nextcloud/fix/drop-files
fix(files): properly handle dropping files
2 parents f6c79c0 + 7dba462 commit 074df09

3 files changed

Lines changed: 58 additions & 30 deletions

File tree

apps/files/src/components/FileEntryMixin.ts

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,18 @@ import type { IFileAction } from '@nextcloud/files'
77
import type { PropType } from 'vue'
88
import type { FileSource } from '../types.ts'
99

10-
import { showError } from '@nextcloud/dialogs'
10+
import { openConflictPicker } from '@nextcloud/dialogs'
1111
import { FileType, Folder, File as NcFile, Node, NodeStatus, Permission } from '@nextcloud/files'
1212
import { t } from '@nextcloud/l10n'
1313
import { generateUrl } from '@nextcloud/router'
1414
import { isPublicShare } from '@nextcloud/sharing/public'
15+
import { getConflicts, getUploader } from '@nextcloud/upload'
1516
import { vOnClickOutside } from '@vueuse/components'
1617
import { extname } from 'path'
1718
import Vue, { computed, defineComponent } from 'vue'
1819
import { action as sidebarAction } from '../actions/sidebarAction.ts'
1920
import logger from '../logger.ts'
20-
import { dataTransferToFileTree, onDropExternalFiles, onDropInternalFiles } from '../services/DropService.ts'
21+
import { onDropInternalFiles } from '../services/DropService.ts'
2122
import { getDragAndDropPreview } from '../utils/dragUtils.ts'
2223
import { hashCode } from '../utils/hashUtils.ts'
2324
import { isDownloadable } from '../utils/permissions.ts'
@@ -476,42 +477,69 @@ export default defineComponent({
476477
event.preventDefault()
477478
event.stopPropagation()
478479

479-
// Caching the selection
480-
const selection = this.draggingFiles
481-
const items = [...event.dataTransfer?.items || []] as DataTransferItem[]
482-
483-
// We need to process the dataTransfer ASAP before the
484-
// browser clears it. This is why we cache the items too.
485-
const fileTree = await dataTransferToFileTree(items)
486-
487-
// We might not have the target directory fetched yet
488-
const contents = await this.activeView?.getContents(this.source.path)
489-
const folder = contents?.folder
490-
if (!folder) {
491-
showError(this.t('files', 'Target folder does not exist any more'))
492-
return
493-
}
494-
495480
// If another button is pressed, cancel it. This
496481
// allows cancelling the drag with the right click.
497482
if (!this.canDrop || event.button) {
498483
return
499484
}
500485

486+
// Caching the selection
487+
const selection = this.draggingFiles
488+
const items = Array.from(event.dataTransfer?.items || [])
489+
490+
if (selection.length === 0 && items.some((item) => item.kind === 'file')) {
491+
const uploader = getUploader()
492+
await uploader.batchUpload(
493+
this.source.path,
494+
items.filter((item) => item.kind === 'file')
495+
.map((item) => 'webkitGetAsEntry' in item ? item.webkitGetAsEntry() : item.getAsFile())
496+
.filter(Boolean) as (FileSystemEntry | File)[],
497+
async (nodes, path) => {
498+
try {
499+
const { contents, folder } = await this.activeView!.getContents(path)
500+
const conflicts = getConflicts(nodes, contents)
501+
if (conflicts.length === 0) {
502+
return nodes
503+
}
504+
505+
const result = await openConflictPicker(
506+
folder.displayname,
507+
conflicts,
508+
(contents as Node[]).filter((node) => conflicts.some((conflict) => conflict.name === node.basename)),
509+
{
510+
recursive: true,
511+
},
512+
)
513+
if (result === null) {
514+
return false
515+
}
516+
return [
517+
...nodes.filter((node) => !conflicts.some((conflict) => conflict.name === node.name)),
518+
...result.selected,
519+
...result.renamed,
520+
]
521+
} catch {
522+
return nodes
523+
}
524+
},
525+
)
526+
this.dragover = false
527+
return
528+
}
529+
530+
// We might not have the target directory fetched yet
531+
const cachedContents = this.filesStore.getNodesByPath(this.activeView.id, this.source.path)
532+
const contents = cachedContents.length === 0
533+
? (await this.activeView!.getContents(this.source.path)).contents
534+
: cachedContents
535+
501536
const isCopy = event.ctrlKey
502537
this.dragover = false
503538

504-
logger.debug('Dropped', { event, folder, selection, fileTree })
505-
506-
// Check whether we're uploading files
507-
if (selection.length === 0 && fileTree.contents.length > 0) {
508-
await onDropExternalFiles(fileTree, folder, contents.contents)
509-
return
510-
}
539+
logger.debug('Dropped', { event, folder: this.source, selection, fileTree })
511540

512-
// Else we're moving/copying files
513541
const nodes = selection.map((source) => this.filesStore.getNode(source)) as Node[]
514-
await onDropInternalFiles(nodes, folder, contents.contents, isCopy)
542+
await onDropInternalFiles(nodes, this.source, contents, isCopy)
515543

516544
// Reset selection after we dropped the files
517545
// if the dropped files are within the selection

dist/files-main.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/files-main.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)