Browse Source

Merge pull request #44254 from nextcloud/fix/drop-service-chrome

fix(files): Adjust dropservice to work with Blink engine
Ferdinand Thiessen 1 month ago
parent
commit
92df4af12b

+ 1 - 0
apps/files/src/components/DragAndDropNotice.vue

@@ -22,6 +22,7 @@
 	-->
 <template>
 	<div v-show="dragover"
+		data-cy-files-drag-drop-area
 		class="files-list__drag-drop-notice"
 		@drop="onDrop">
 		<div class="files-list__drag-drop-notice-wrapper">

+ 18 - 12
apps/files/src/services/DropService.ts

@@ -36,21 +36,27 @@ export const handleDrop = async (data: DataTransfer): Promise<Upload[]> => {
 	// TODO: Maybe handle `getAsFileSystemHandle()` in the future
 
 	const uploads = [] as Upload[]
-	for (const item of data.items) {
-		if (item.kind !== 'file') {
-			logger.debug('Skipping dropped item', { kind: item.kind, type: item.type })
-			continue
-		}
-
-		// MDN recommends to try both, as it might be renamed in the future
-		const entry = (item as unknown as { getAsEntry?: () => FileSystemEntry|undefined})?.getAsEntry?.() ?? item.webkitGetAsEntry()
-
+	// 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
+	const entries = [...data.items]
+		.filter((item) => {
+			if (item.kind !== 'file') {
+				logger.debug('Skipping dropped item', { kind: item.kind, type: item.type })
+				return false
+			}
+			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
+		})
+
+	for (const entry of entries) {
 		// Handle browser issues if Filesystem API is not available. Fallback to File API
-		if (entry === null) {
+		if (entry instanceof DataTransferItem) {
 			logger.debug('Could not get FilesystemEntry of item, falling back to file')
-			const file = item.getAsFile()
+			const file = entry.getAsFile()
 			if (file === null) {
-				logger.warn('Could not process DataTransferItem', { type: item.type, kind: item.kind })
+				logger.warn('Could not process DataTransferItem', { type: entry.type, kind: entry.kind })
 				showError(t('files', 'One of the dropped files could not be processed'))
 			} else {
 				uploads.push(await handleFileUpload(file))

+ 62 - 0
cypress/e2e/files/drag-n-drop.cy.ts

@@ -0,0 +1,62 @@
+import { getRowForFile } from './FilesUtils.ts'
+
+describe('files: Drag and Drop', { testIsolation: true }, () => {
+	beforeEach(() => {
+		cy.createRandomUser().then((user) => {
+			cy.login(user)
+		})
+		cy.visit('/apps/files')
+	})
+
+	it('can drop a file', () => {
+		const dataTransfer = new DataTransfer()
+		dataTransfer.items.add(new File([], 'single-file.txt'))
+
+		cy.intercept('PUT', /\/remote.php\/dav\/files\//).as('uploadFile')
+
+		cy.get('[data-cy-files-drag-drop-area]').should('not.be.visible')
+		// Trigger the drop notice
+		cy.get('main.app-content').trigger('dragover', { dataTransfer })
+		cy.get('[data-cy-files-drag-drop-area]').should('be.visible')
+
+		// Upload drop a file
+		cy.get('[data-cy-files-drag-drop-area]').selectFile({
+			fileName: 'single-file.txt',
+			contents: ['hello '.repeat(1024)],
+		}, { action: 'drag-drop' })
+
+		cy.wait('@uploadFile')
+
+		getRowForFile('single-file.txt').should('be.visible')
+		getRowForFile('single-file.txt').find('[data-cy-files-list-row-size]').should('contain', '6 KB')
+	})
+
+	it('can drop multiple files', () => {
+		const dataTransfer = new DataTransfer()
+		dataTransfer.items.add(new File([], 'first.txt'))
+		dataTransfer.items.add(new File([], 'second.txt'))
+
+		cy.intercept('PUT', /\/remote.php\/dav\/files\//).as('uploadFile')
+
+		// Trigger the drop notice
+		cy.get('main.app-content').trigger('dragover', { dataTransfer })
+		cy.get('[data-cy-files-drag-drop-area]').should('be.visible')
+
+		// Upload drop a file
+		cy.get('[data-cy-files-drag-drop-area]').selectFile([
+			{
+				fileName: 'first.txt',
+				contents: ['Hello'],
+			},
+			{
+				fileName: 'second.txt',
+				contents: ['World'],
+			},
+		], { action: 'drag-drop' })
+
+		cy.wait('@uploadFile')
+
+		getRowForFile('first.txt').should('be.visible')
+		getRowForFile('second.txt').should('be.visible')
+	})
+})

File diff suppressed because it is too large
+ 0 - 0
dist/files-main.js


File diff suppressed because it is too large
+ 0 - 0
dist/files-main.js.map


Some files were not shown because too many files changed in this diff