diff --git a/src-ui/src/app/components/file-drop/file-drop.component.html b/src-ui/src/app/components/file-drop/file-drop.component.html
deleted file mode 100644
index 0c0f4ea39..000000000
--- a/src-ui/src/app/components/file-drop/file-drop.component.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
Drop files to begin upload
-
diff --git a/src-ui/src/app/components/file-drop/file-drop.component.scss b/src-ui/src/app/components/file-drop/file-drop.component.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src-ui/src/app/components/file-drop/file-drop.component.spec.ts b/src-ui/src/app/components/file-drop/file-drop.component.spec.ts
deleted file mode 100644
index e174c18ad..000000000
--- a/src-ui/src/app/components/file-drop/file-drop.component.spec.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import { HttpClientTestingModule } from '@angular/common/http/testing'
-import {
- ComponentFixture,
- TestBed,
- fakeAsync,
- tick,
-} from '@angular/core/testing'
-import { By } from '@angular/platform-browser'
-import { PermissionsService } from 'src/app/services/permissions.service'
-import { SettingsService } from 'src/app/services/settings.service'
-import { ToastService } from 'src/app/services/toast.service'
-import { UploadDocumentsService } from 'src/app/services/upload-documents.service'
-import { ToastsComponent } from '../common/toasts/toasts.component'
-import { FileDropComponent } from './file-drop.component'
-
-describe('FileDropComponent', () => {
- let component: FileDropComponent
- let fixture: ComponentFixture
- let permissionsService: PermissionsService
- let toastService: ToastService
- let settingsService: SettingsService
- let uploadDocumentsService: UploadDocumentsService
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- declarations: [FileDropComponent, ToastsComponent],
- providers: [],
- imports: [HttpClientTestingModule],
- }).compileComponents()
-
- permissionsService = TestBed.inject(PermissionsService)
- settingsService = TestBed.inject(SettingsService)
- toastService = TestBed.inject(ToastService)
- uploadDocumentsService = TestBed.inject(UploadDocumentsService)
-
- fixture = TestBed.createComponent(FileDropComponent)
- component = fixture.componentInstance
- fixture.detectChanges()
- })
-
- it('should enable drag-drop if user has permissions', () => {
- jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
- expect(component.dragDropEnabled).toBeTruthy()
- })
-
- it('should disable drag-drop if user does not have permissions', () => {
- jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(false)
- expect(component.dragDropEnabled).toBeFalsy()
- })
-
- it('should disable drag-drop if disabled in settings', fakeAsync(() => {
- jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
- settingsService.globalDropzoneEnabled = false
- expect(component.dragDropEnabled).toBeFalsy()
-
- component.onDragOver(new Event('dragover') as DragEvent)
- tick(1)
- fixture.detectChanges()
- expect(component.fileIsOver).toBeFalsy()
- const dropzone = fixture.debugElement.query(
- By.css('.global-dropzone-overlay')
- )
- expect(dropzone.classes['hide']).toBeTruthy()
- component.onDragLeave(new Event('dragleave') as DragEvent)
- tick(700)
- fixture.detectChanges()
- // drop
- const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFiles')
- const dragEvent = new Event('drop')
- dragEvent['dataTransfer'] = {
- files: {
- item: () => {},
- length: 0,
- },
- }
- component.onDrop(dragEvent as DragEvent)
- tick(3000)
- expect(uploadSpy).not.toHaveBeenCalled()
- }))
-
- it('should support drag drop, initiate upload', fakeAsync(() => {
- jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
- expect(component.fileIsOver).toBeFalsy()
- component.onDragOver(new Event('dragover') as DragEvent)
- tick(1)
- fixture.detectChanges()
- expect(component.fileIsOver).toBeTruthy()
- const dropzone = fixture.debugElement.query(
- By.css('.global-dropzone-overlay')
- )
- component.onDragLeave(new Event('dragleave') as DragEvent)
- tick(700)
- fixture.detectChanges()
- expect(dropzone.classes['hide']).toBeTruthy()
- // drop
- const toastSpy = jest.spyOn(toastService, 'show')
- const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFiles')
- const dragEvent = new Event('drop')
- dragEvent['dataTransfer'] = {
- files: {
- item: () => {
- return new File(
- [new Blob(['testing'], { type: 'application/pdf' })],
- 'file.pdf'
- )
- },
- length: 1,
- } as unknown as FileList,
- }
- component.onDrop(dragEvent as DragEvent)
- tick(3000)
- expect(toastSpy).toHaveBeenCalled()
- expect(uploadSpy).toHaveBeenCalled()
- }))
-})
diff --git a/src-ui/src/app/components/file-drop/file-drop.component.ts b/src-ui/src/app/components/file-drop/file-drop.component.ts
deleted file mode 100644
index 8b74fb8bb..000000000
--- a/src-ui/src/app/components/file-drop/file-drop.component.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import { Component, HostListener } from '@angular/core'
-import {
- PermissionsService,
- PermissionAction,
- PermissionType,
-} from 'src/app/services/permissions.service'
-import { SettingsService } from 'src/app/services/settings.service'
-import { ToastService } from 'src/app/services/toast.service'
-import { UploadDocumentsService } from 'src/app/services/upload-documents.service'
-
-@Component({
- selector: 'pngx-file-drop',
- templateUrl: './file-drop.component.html',
- styleUrls: ['./file-drop.component.scss'],
-})
-export class FileDropComponent {
- private fileLeaveTimeoutID: any
- fileIsOver: boolean = false
- hidden: boolean = true
-
- constructor(
- private settings: SettingsService,
- private toastService: ToastService,
- private uploadDocumentsService: UploadDocumentsService,
- private permissionsService: PermissionsService
- ) {}
-
- public get dragDropEnabled(): boolean {
- return (
- this.settings.globalDropzoneEnabled &&
- this.permissionsService.currentUserCan(
- PermissionAction.Add,
- PermissionType.Document
- )
- )
- }
-
- @HostListener('dragover', ['$event ']) onDragOver(event: DragEvent) {
- if (!this.dragDropEnabled) return
- event.preventDefault()
- event.stopImmediatePropagation()
- this.settings.globalDropzoneActive = true
- // allows transition
- setTimeout(() => {
- this.fileIsOver = true
- }, 1)
- this.hidden = false
- // stop fileLeave timeout
- clearTimeout(this.fileLeaveTimeoutID)
- }
-
- @HostListener('dragleave', ['$event']) public onDragLeave(
- event: DragEvent,
- immediate: boolean = false
- ) {
- if (!this.dragDropEnabled) return
- event.preventDefault()
- event.stopImmediatePropagation()
- this.settings.globalDropzoneActive = false
-
- const ms = immediate ? 0 : 500
-
- this.fileLeaveTimeoutID = setTimeout(() => {
- this.fileIsOver = false
- // await transition completed
- setTimeout(() => {
- this.hidden = true
- }, 150)
- }, ms)
- }
-
- @HostListener('drop', ['$event']) public onDrop(event: DragEvent) {
- if (!this.dragDropEnabled) return
- event.preventDefault()
- event.stopImmediatePropagation()
- this.onDragLeave(event, true)
- this.uploadDocumentsService.uploadFiles(event.dataTransfer.files)
- if (event.dataTransfer.files.length)
- this.toastService.showInfo($localize`Initiating upload...`, 3000)
- }
-}
diff --git a/src-ui/src/app/services/upload-documents.service.spec.ts b/src-ui/src/app/services/upload-documents.service.spec.ts
index d00dd38db..f1e1ae8b5 100644
--- a/src-ui/src/app/services/upload-documents.service.spec.ts
+++ b/src-ui/src/app/services/upload-documents.service.spec.ts
@@ -134,4 +134,35 @@ describe('UploadDocumentsService', () => {
consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED)
).toHaveLength(2)
})
+
+ it('accepts files via drag and drop', () => {
+ const uploadSpy = jest.spyOn(
+ UploadDocumentsService.prototype as any,
+ 'uploadFile'
+ )
+ const fileEntry = {
+ name: 'file.pdf',
+ isDirectory: false,
+ isFile: true,
+ file: (callback) => {
+ return callback(
+ new File(
+ [new Blob(['testing'], { type: 'application/pdf' })],
+ 'file.pdf'
+ )
+ )
+ },
+ }
+ uploadDocumentsService.onNgxFileDrop([
+ {
+ relativePath: 'path/to/file.pdf',
+ fileEntry,
+ },
+ ])
+ expect(uploadSpy).toHaveBeenCalled()
+
+ let req = httpTestingController.match(
+ `${environment.apiBaseUrl}documents/post_document/`
+ )
+ })
})
diff --git a/src-ui/src/app/services/upload-documents.service.ts b/src-ui/src/app/services/upload-documents.service.ts
index a7188896e..6a086cd99 100644
--- a/src-ui/src/app/services/upload-documents.service.ts
+++ b/src-ui/src/app/services/upload-documents.service.ts
@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core'
import { HttpEventType } from '@angular/common/http'
+import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop'
import {
ConsumerStatusService,
FileStatusPhase,
@@ -18,50 +19,61 @@ export class UploadDocumentsService {
private consumerStatusService: ConsumerStatusService
) {}
- uploadFiles(files: FileList) {
- for (let index = 0; index < files.length; index++) {
- const file = files.item(index)
-
- let formData = new FormData()
- formData.append('document', file, file.name)
- let status = this.consumerStatusService.newFileUpload(file.name)
-
- status.message = $localize`Connecting...`
-
- this.uploadSubscriptions[file.name] = this.documentService
- .uploadDocument(formData)
- .subscribe({
- next: (event) => {
- if (event.type == HttpEventType.UploadProgress) {
- status.updateProgress(
- FileStatusPhase.UPLOADING,
- event.loaded,
- event.total
- )
- status.message = $localize`Uploading...`
- } else if (event.type == HttpEventType.Response) {
- status.taskId = event.body['task_id']
- status.message = $localize`Upload complete, waiting...`
- this.uploadSubscriptions[file.name]?.complete()
- }
- },
- error: (error) => {
- switch (error.status) {
- case 400: {
- this.consumerStatusService.fail(status, error.error.document)
- break
- }
- default: {
- this.consumerStatusService.fail(
- status,
- $localize`HTTP error: ${error.status} ${error.statusText}`
- )
- break
- }
- }
- this.uploadSubscriptions[file.name]?.complete()
- },
- })
+ onNgxFileDrop(files: NgxFileDropEntry[]) {
+ for (const droppedFile of files) {
+ if (droppedFile.fileEntry.isFile) {
+ const fileEntry = droppedFile.fileEntry as FileSystemFileEntry
+ fileEntry.file((file: File) => this.uploadFile(file))
+ }
}
}
+
+ uploadFiles(files: FileList) {
+ for (let index = 0; index < files.length; index++) {
+ this.uploadFile(files.item(index))
+ }
+ }
+
+ private uploadFile(file: File) {
+ let formData = new FormData()
+ formData.append('document', file, file.name)
+ let status = this.consumerStatusService.newFileUpload(file.name)
+
+ status.message = $localize`Connecting...`
+
+ this.uploadSubscriptions[file.name] = this.documentService
+ .uploadDocument(formData)
+ .subscribe({
+ next: (event) => {
+ if (event.type == HttpEventType.UploadProgress) {
+ status.updateProgress(
+ FileStatusPhase.UPLOADING,
+ event.loaded,
+ event.total
+ )
+ status.message = $localize`Uploading...`
+ } else if (event.type == HttpEventType.Response) {
+ status.taskId = event.body['task_id']
+ status.message = $localize`Upload complete, waiting...`
+ this.uploadSubscriptions[file.name]?.complete()
+ }
+ },
+ error: (error) => {
+ switch (error.status) {
+ case 400: {
+ this.consumerStatusService.fail(status, error.error.document)
+ break
+ }
+ default: {
+ this.consumerStatusService.fail(
+ status,
+ $localize`HTTP error: ${error.status} ${error.statusText}`
+ )
+ break
+ }
+ }
+ this.uploadSubscriptions[file.name]?.complete()
+ },
+ })
+ }
}