Revert custom file drop implementation
This commit is contained in:
parent
209c621a0b
commit
01f57af215
@ -29,7 +29,6 @@ test('dashboard saved view show all', async ({ page }) => {
|
|||||||
.locator('pngx-widget-frame')
|
.locator('pngx-widget-frame')
|
||||||
.filter({ hasText: 'Inbox' })
|
.filter({ hasText: 'Inbox' })
|
||||||
.getByRole('link', { name: 'Show all' })
|
.getByRole('link', { name: 'Show all' })
|
||||||
.first()
|
|
||||||
.click()
|
.click()
|
||||||
await expect(page).toHaveURL(/view\/7/)
|
await expect(page).toHaveURL(/view\/7/)
|
||||||
await expect(page.locator('pngx-document-list')).toHaveText(/8 documents/)
|
await expect(page.locator('pngx-document-list')).toHaveText(/8 documents/)
|
||||||
|
17
src-ui/package-lock.json
generated
17
src-ui/package-lock.json
generated
@ -28,6 +28,7 @@
|
|||||||
"ngx-color": "^9.0.0",
|
"ngx-color": "^9.0.0",
|
||||||
"ngx-cookie-service": "^16.0.1",
|
"ngx-cookie-service": "^16.0.1",
|
||||||
"ngx-drag-drop": "^16.1.0",
|
"ngx-drag-drop": "^16.1.0",
|
||||||
|
"ngx-file-drop": "^16.0.0",
|
||||||
"ngx-ui-tour-ng-bootstrap": "^13.0.4",
|
"ngx-ui-tour-ng-bootstrap": "^13.0.4",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
@ -14073,6 +14074,22 @@
|
|||||||
"@angular/core": "^16.0.0"
|
"@angular/core": "^16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ngx-file-drop": {
|
||||||
|
"version": "16.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-file-drop/-/ngx-file-drop-16.0.0.tgz",
|
||||||
|
"integrity": "sha512-33RPoZBAiMkV110Rzu3iOrzGcG5M20S4sAiwLzNylfJobu9qVw5XR83FhUelSeqJRoaDxXBRKAozYCSnUf2CNw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.5.0",
|
||||||
|
"npm": ">= 6.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": ">=14.0.0",
|
||||||
|
"@angular/core": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ngx-ui-tour-core": {
|
"node_modules/ngx-ui-tour-core": {
|
||||||
"version": "11.0.4",
|
"version": "11.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-11.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-11.0.4.tgz",
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
"ngx-color": "^9.0.0",
|
"ngx-color": "^9.0.0",
|
||||||
"ngx-cookie-service": "^16.0.1",
|
"ngx-cookie-service": "^16.0.1",
|
||||||
"ngx-drag-drop": "^16.1.0",
|
"ngx-drag-drop": "^16.1.0",
|
||||||
|
"ngx-file-drop": "^16.0.0",
|
||||||
"ngx-ui-tour-ng-bootstrap": "^13.0.4",
|
"ngx-ui-tour-ng-bootstrap": "^13.0.4",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
<pngx-toasts></pngx-toasts>
|
<pngx-toasts></pngx-toasts>
|
||||||
|
|
||||||
<pngx-file-drop>
|
<ngx-file-drop dropZoneClassName="main-dropzone" contentClassName="main-content" [disabled]="!dragDropEnabled"
|
||||||
<ng-container content>
|
(onFileDrop)="dropped($event)" (onFileOver)="fileOver()" (onFileLeave)="fileLeave()">
|
||||||
<router-outlet></router-outlet>
|
<ng-template ngx-file-drop-content-tmp>
|
||||||
</ng-container>
|
<div class="global-dropzone-overlay fade" [class.show]="fileIsOver" [class.hide]="hidden">
|
||||||
</pngx-file-drop>
|
<h2 i18n>Drop files to begin upload</h2>
|
||||||
|
</div>
|
||||||
|
<div [class.inert]="fileIsOver">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</ngx-file-drop>
|
||||||
|
|
||||||
<tour-step-template>
|
<tour-step-template>
|
||||||
<ng-template #tourStep let-step="step">
|
<ng-template #tourStep let-step="step">
|
||||||
|
@ -5,8 +5,10 @@ import {
|
|||||||
fakeAsync,
|
fakeAsync,
|
||||||
tick,
|
tick,
|
||||||
} from '@angular/core/testing'
|
} from '@angular/core/testing'
|
||||||
|
import { By } from '@angular/platform-browser'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import { RouterTestingModule } from '@angular/router/testing'
|
import { RouterTestingModule } from '@angular/router/testing'
|
||||||
|
import { NgxFileDropEntry, NgxFileDropModule } from 'ngx-file-drop'
|
||||||
import { TourService, TourNgBootstrapModule } from 'ngx-ui-tour-ng-bootstrap'
|
import { TourService, TourNgBootstrapModule } from 'ngx-ui-tour-ng-bootstrap'
|
||||||
import { Subject } from 'rxjs'
|
import { Subject } from 'rxjs'
|
||||||
import { routes } from './app-routing.module'
|
import { routes } from './app-routing.module'
|
||||||
@ -18,8 +20,8 @@ import {
|
|||||||
} from './services/consumer-status.service'
|
} from './services/consumer-status.service'
|
||||||
import { PermissionsService } from './services/permissions.service'
|
import { PermissionsService } from './services/permissions.service'
|
||||||
import { ToastService, Toast } from './services/toast.service'
|
import { ToastService, Toast } from './services/toast.service'
|
||||||
|
import { UploadDocumentsService } from './services/upload-documents.service'
|
||||||
import { SettingsService } from './services/settings.service'
|
import { SettingsService } from './services/settings.service'
|
||||||
import { FileDropComponent } from './components/file-drop/file-drop.component'
|
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe('AppComponent', () => {
|
||||||
let component: AppComponent
|
let component: AppComponent
|
||||||
@ -30,15 +32,17 @@ describe('AppComponent', () => {
|
|||||||
let toastService: ToastService
|
let toastService: ToastService
|
||||||
let router: Router
|
let router: Router
|
||||||
let settingsService: SettingsService
|
let settingsService: SettingsService
|
||||||
|
let uploadDocumentsService: UploadDocumentsService
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [AppComponent, ToastsComponent, FileDropComponent],
|
declarations: [AppComponent, ToastsComponent],
|
||||||
providers: [],
|
providers: [],
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule,
|
HttpClientTestingModule,
|
||||||
TourNgBootstrapModule,
|
TourNgBootstrapModule,
|
||||||
RouterTestingModule.withRoutes(routes),
|
RouterTestingModule.withRoutes(routes),
|
||||||
|
NgxFileDropModule,
|
||||||
],
|
],
|
||||||
}).compileComponents()
|
}).compileComponents()
|
||||||
|
|
||||||
@ -48,6 +52,7 @@ describe('AppComponent', () => {
|
|||||||
settingsService = TestBed.inject(SettingsService)
|
settingsService = TestBed.inject(SettingsService)
|
||||||
toastService = TestBed.inject(ToastService)
|
toastService = TestBed.inject(ToastService)
|
||||||
router = TestBed.inject(Router)
|
router = TestBed.inject(Router)
|
||||||
|
uploadDocumentsService = TestBed.inject(UploadDocumentsService)
|
||||||
fixture = TestBed.createComponent(AppComponent)
|
fixture = TestBed.createComponent(AppComponent)
|
||||||
component = fixture.componentInstance
|
component = fixture.componentInstance
|
||||||
})
|
})
|
||||||
@ -132,4 +137,44 @@ describe('AppComponent', () => {
|
|||||||
fileStatusSubject.next(new FileStatus())
|
fileStatusSubject.next(new FileStatus())
|
||||||
expect(toastSpy).toHaveBeenCalled()
|
expect(toastSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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 support drag drop', fakeAsync(() => {
|
||||||
|
expect(component.fileIsOver).toBeFalsy()
|
||||||
|
component.fileOver()
|
||||||
|
tick(1)
|
||||||
|
fixture.detectChanges()
|
||||||
|
expect(component.fileIsOver).toBeTruthy()
|
||||||
|
const dropzone = fixture.debugElement.query(
|
||||||
|
By.css('.global-dropzone-overlay')
|
||||||
|
)
|
||||||
|
expect(dropzone).not.toBeNull()
|
||||||
|
component.fileLeave()
|
||||||
|
tick(700)
|
||||||
|
fixture.detectChanges()
|
||||||
|
expect(dropzone.classes['hide']).toBeTruthy()
|
||||||
|
// drop
|
||||||
|
const toastSpy = jest.spyOn(toastService, 'show')
|
||||||
|
const uploadSpy = jest.spyOn(uploadDocumentsService, 'onNgxFileDrop')
|
||||||
|
component.dropped([
|
||||||
|
{
|
||||||
|
fileEntry: {
|
||||||
|
isFile: true,
|
||||||
|
file: () => {},
|
||||||
|
},
|
||||||
|
} as unknown as NgxFileDropEntry,
|
||||||
|
])
|
||||||
|
tick(3000)
|
||||||
|
expect(toastSpy).toHaveBeenCalled()
|
||||||
|
expect(uploadSpy).toHaveBeenCalled()
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
|
@ -5,6 +5,7 @@ import { Router } from '@angular/router'
|
|||||||
import { Subscription, first } from 'rxjs'
|
import { Subscription, first } from 'rxjs'
|
||||||
import { ConsumerStatusService } from './services/consumer-status.service'
|
import { ConsumerStatusService } from './services/consumer-status.service'
|
||||||
import { ToastService } from './services/toast.service'
|
import { ToastService } from './services/toast.service'
|
||||||
|
import { NgxFileDropEntry } from 'ngx-file-drop'
|
||||||
import { UploadDocumentsService } from './services/upload-documents.service'
|
import { UploadDocumentsService } from './services/upload-documents.service'
|
||||||
import { TasksService } from './services/tasks.service'
|
import { TasksService } from './services/tasks.service'
|
||||||
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
|
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
|
||||||
@ -24,11 +25,16 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
successSubscription: Subscription
|
successSubscription: Subscription
|
||||||
failedSubscription: Subscription
|
failedSubscription: Subscription
|
||||||
|
|
||||||
|
private fileLeaveTimeoutID: any
|
||||||
|
fileIsOver: boolean = false
|
||||||
|
hidden: boolean = true
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private settings: SettingsService,
|
private settings: SettingsService,
|
||||||
private consumerStatusService: ConsumerStatusService,
|
private consumerStatusService: ConsumerStatusService,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
private uploadDocumentsService: UploadDocumentsService,
|
||||||
private tasksService: TasksService,
|
private tasksService: TasksService,
|
||||||
public tourService: TourService,
|
public tourService: TourService,
|
||||||
private renderer: Renderer2,
|
private renderer: Renderer2,
|
||||||
@ -244,4 +250,46 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get dragDropEnabled(): boolean {
|
||||||
|
return (
|
||||||
|
this.settings.globalDropzoneEnabled &&
|
||||||
|
this.permissionsService.currentUserCan(
|
||||||
|
PermissionAction.Add,
|
||||||
|
PermissionType.Document
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fileOver() {
|
||||||
|
this.settings.globalDropzoneActive = true
|
||||||
|
// allows transition
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fileIsOver = true
|
||||||
|
}, 1)
|
||||||
|
this.hidden = false
|
||||||
|
// stop fileLeave timeout
|
||||||
|
clearTimeout(this.fileLeaveTimeoutID)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fileLeave(immediate: boolean = false) {
|
||||||
|
this.settings.globalDropzoneActive = false
|
||||||
|
const ms = immediate ? 0 : 500
|
||||||
|
|
||||||
|
this.fileLeaveTimeoutID = setTimeout(() => {
|
||||||
|
this.fileIsOver = false
|
||||||
|
// await transition completed
|
||||||
|
setTimeout(() => {
|
||||||
|
this.hidden = true
|
||||||
|
}, 150)
|
||||||
|
}, ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
public dropped(files: NgxFileDropEntry[]) {
|
||||||
|
this.settings.globalDropzoneActive = false
|
||||||
|
this.fileLeave(true)
|
||||||
|
this.uploadDocumentsService.onNgxFileDrop(files)
|
||||||
|
if (files.length > 0)
|
||||||
|
this.toastService.showInfo($localize`Initiating upload...`, 3000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ import { DateDropdownComponent } from './components/common/date-dropdown/date-dr
|
|||||||
import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component'
|
import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component'
|
||||||
import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component'
|
import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component'
|
||||||
import { BulkEditorComponent } from './components/document-list/bulk-editor/bulk-editor.component'
|
import { BulkEditorComponent } from './components/document-list/bulk-editor/bulk-editor.component'
|
||||||
|
import { NgxFileDropModule } from 'ngx-file-drop'
|
||||||
import { TextComponent } from './components/common/input/text/text.component'
|
import { TextComponent } from './components/common/input/text/text.component'
|
||||||
import { SelectComponent } from './components/common/input/select/select.component'
|
import { SelectComponent } from './components/common/input/select/select.component'
|
||||||
import { CheckComponent } from './components/common/input/check/check.component'
|
import { CheckComponent } from './components/common/input/check/check.component'
|
||||||
@ -99,7 +100,6 @@ import { ConsumptionTemplateEditDialogComponent } from './components/common/edit
|
|||||||
import { MailComponent } from './components/manage/mail/mail.component'
|
import { MailComponent } from './components/manage/mail/mail.component'
|
||||||
import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component'
|
import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component'
|
||||||
import { DndModule } from 'ngx-drag-drop'
|
import { DndModule } from 'ngx-drag-drop'
|
||||||
import { FileDropComponent } from './components/file-drop/file-drop.component'
|
|
||||||
|
|
||||||
import localeAf from '@angular/common/locales/af'
|
import localeAf from '@angular/common/locales/af'
|
||||||
import localeAr from '@angular/common/locales/ar'
|
import localeAr from '@angular/common/locales/ar'
|
||||||
@ -242,7 +242,6 @@ function initializeApp(settings: SettingsService) {
|
|||||||
ConsumptionTemplateEditDialogComponent,
|
ConsumptionTemplateEditDialogComponent,
|
||||||
MailComponent,
|
MailComponent,
|
||||||
UsersAndGroupsComponent,
|
UsersAndGroupsComponent,
|
||||||
FileDropComponent,
|
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -251,6 +250,7 @@ function initializeApp(settings: SettingsService) {
|
|||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
|
NgxFileDropModule,
|
||||||
PdfViewerModule,
|
PdfViewerModule,
|
||||||
NgSelectModule,
|
NgSelectModule,
|
||||||
ColorSliderModule,
|
ColorSliderModule,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<form class="justify-content-center d-flex flex-column align-items-center py-3 px-2">
|
<form class="justify-content-center d-flex flex-column align-items-center py-3 px-2">
|
||||||
<span class="text-muted" i18n>Drop documents anywhere or</span>
|
<span class="text-muted" i18n>Drop documents anywhere or</span>
|
||||||
<button class="btn btn-sm btn-outline-primary mt-3" (click)="fileUpload.click()" i18n>Browse files</button>
|
<button class="btn btn-sm btn-outline-primary mt-3" (click)="fileUpload.click()" i18n>Browse files</button>
|
||||||
<input type="file" class="visually-hidden" (change)="onFileSelected($event)" #fileUpload>
|
<input type="file" class="visually-hidden" (change)="onFileSelected($event)" multiple #fileUpload>
|
||||||
</form>
|
</form>
|
||||||
<div class="fixed-bottom p-2 p-md-4" [ngClass]="slimSidebarEnabled ? 'col-slim' : 'offset-md-3 offset-lg-2'">
|
<div class="fixed-bottom p-2 p-md-4" [ngClass]="slimSidebarEnabled ? 'col-slim' : 'offset-md-3 offset-lg-2'">
|
||||||
<div class="row d-flex justify-content-end">
|
<div class="row d-flex justify-content-end">
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
<div [class.inert]="fileIsOver">
|
|
||||||
<ng-content select="[content]"></ng-content>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="global-dropzone-overlay fade" [class.show]="fileIsOver" [class.hide]="hidden">
|
|
||||||
<h2 i18n>Drop files to begin upload</h2>
|
|
||||||
</div>
|
|
@ -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<FileDropComponent>
|
|
||||||
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()
|
|
||||||
}))
|
|
||||||
})
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -134,4 +134,35 @@ describe('UploadDocumentsService', () => {
|
|||||||
consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED)
|
consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED)
|
||||||
).toHaveLength(2)
|
).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/`
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { HttpEventType } from '@angular/common/http'
|
import { HttpEventType } from '@angular/common/http'
|
||||||
|
import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop'
|
||||||
import {
|
import {
|
||||||
ConsumerStatusService,
|
ConsumerStatusService,
|
||||||
FileStatusPhase,
|
FileStatusPhase,
|
||||||
@ -18,50 +19,61 @@ export class UploadDocumentsService {
|
|||||||
private consumerStatusService: ConsumerStatusService
|
private consumerStatusService: ConsumerStatusService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
uploadFiles(files: FileList) {
|
onNgxFileDrop(files: NgxFileDropEntry[]) {
|
||||||
for (let index = 0; index < files.length; index++) {
|
for (const droppedFile of files) {
|
||||||
const file = files.item(index)
|
if (droppedFile.fileEntry.isFile) {
|
||||||
|
const fileEntry = droppedFile.fileEntry as FileSystemFileEntry
|
||||||
let formData = new FormData()
|
fileEntry.file((file: File) => this.uploadFile(file))
|
||||||
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()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user