mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-1403] Upload Dialog - Delete files event (#2234)
* remove provisional service * Error and Delete events * delete status * separate upload events * update demo * files upload counter and errors * pendig files icon and action * remove multiple files error key * handle cancel files * fixed component theme * remove fdescribe * resolved comments * throw error
This commit is contained in:
committed by
Mario Romano
parent
1deaa22570
commit
07ba8bc15f
@@ -21,7 +21,7 @@ import { ActivatedRoute, Params, Router } from '@angular/router';
|
|||||||
import { MinimalNodeEntity } from 'alfresco-js-api';
|
import { MinimalNodeEntity } from 'alfresco-js-api';
|
||||||
import {
|
import {
|
||||||
AlfrescoApiService, AlfrescoContentService, AlfrescoTranslationService, CreateFolderDialogComponent,
|
AlfrescoApiService, AlfrescoContentService, AlfrescoTranslationService, CreateFolderDialogComponent,
|
||||||
DownloadZipDialogComponent, FileUploadCompleteEvent, FolderCreatedEvent, NotificationService,
|
DownloadZipDialogComponent, FileUploadEvent, FolderCreatedEvent, NotificationService,
|
||||||
SiteModel, UploadService
|
SiteModel, UploadService
|
||||||
} from 'ng2-alfresco-core';
|
} from 'ng2-alfresco-core';
|
||||||
import { DataColumn, DataRow } from 'ng2-alfresco-datatable';
|
import { DataColumn, DataRow } from 'ng2-alfresco-datatable';
|
||||||
@@ -136,7 +136,8 @@ export class FilesComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.uploadService.fileUploadComplete.debounceTime(300).subscribe(value => this.onFileUploadComplete(value));
|
this.uploadService.fileUploadComplete.debounceTime(300).subscribe(value => this.onFileUploadEvent(value));
|
||||||
|
this.uploadService.fileUploadDeleted.subscribe((value) => this.onFileUploadEvent(value));
|
||||||
this.contentService.folderCreated.subscribe(value => this.onFolderCreated(value));
|
this.contentService.folderCreated.subscribe(value => this.onFolderCreated(value));
|
||||||
|
|
||||||
// this.permissionsStyle.push(new PermissionStyleModel('document-list__create', PermissionsEnum.CREATE));
|
// this.permissionsStyle.push(new PermissionStyleModel('document-list__create', PermissionsEnum.CREATE));
|
||||||
@@ -161,7 +162,7 @@ export class FilesComponent implements OnInit {
|
|||||||
this.errorMessage = null;
|
this.errorMessage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileUploadComplete(event: FileUploadCompleteEvent) {
|
onFileUploadEvent(event: FileUploadEvent) {
|
||||||
if (event && event.file.options.parentId === this.documentList.currentFolderId) {
|
if (event && event.file.options.parentId === this.documentList.currentFolderId) {
|
||||||
this.documentList.reload();
|
this.documentList.reload();
|
||||||
}
|
}
|
||||||
|
@@ -34,3 +34,19 @@ export class FileUploadCompleteEvent extends FileUploadEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FileUploadDeleteEvent extends FileUploadEvent {
|
||||||
|
|
||||||
|
constructor(file: FileModel, public totalComplete: number = 0) {
|
||||||
|
super(file, FileUploadStatus.Deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FileUploadErrorEvent extends FileUploadEvent {
|
||||||
|
|
||||||
|
constructor(file: FileModel, public error: any, public totalError: number = 0) {
|
||||||
|
super(file, FileUploadStatus.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@@ -34,7 +34,8 @@ export enum FileUploadStatus {
|
|||||||
Progress = 3,
|
Progress = 3,
|
||||||
Cancelled = 4,
|
Cancelled = 4,
|
||||||
Aborted = 5,
|
Aborted = 5,
|
||||||
Error = 6
|
Error = 6,
|
||||||
|
Deleted = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FileModel {
|
export class FileModel {
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import { EventEmitter } from '@angular/core';
|
import { EventEmitter } from '@angular/core';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { FileModel, FileUploadOptions } from '../models/file.model';
|
import { FileModel, FileUploadOptions, FileUploadStatus } from '../models/file.model';
|
||||||
import { AlfrescoApiService } from './alfresco-api.service';
|
import { AlfrescoApiService } from './alfresco-api.service';
|
||||||
import { AlfrescoSettingsService } from './alfresco-settings.service';
|
import { AlfrescoSettingsService } from './alfresco-settings.service';
|
||||||
import { AppConfigModule } from './app-config.service';
|
import { AppConfigModule } from './app-config.service';
|
||||||
@@ -214,4 +214,31 @@ describe('UploadService', () => {
|
|||||||
expect(result.length).toBe(1);
|
expect(result.length).toBe(1);
|
||||||
expect(result[0]).toBe(file4);
|
expect(result[0]).toBe(file4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should call onUploadDeleted if file was deleted', () => {
|
||||||
|
const file = <any> ({ status: FileUploadStatus.Deleted });
|
||||||
|
spyOn(service.fileUploadDeleted, 'next');
|
||||||
|
|
||||||
|
service.cancelUpload(file);
|
||||||
|
|
||||||
|
expect(service.fileUploadDeleted.next).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call fileUploadError if file has error status', () => {
|
||||||
|
const file = <any> ({ status: FileUploadStatus.Error });
|
||||||
|
spyOn(service.fileUploadError, 'next');
|
||||||
|
|
||||||
|
service.cancelUpload(file);
|
||||||
|
|
||||||
|
expect(service.fileUploadError.next).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call fileUploadCancelled if file is in pending', () => {
|
||||||
|
const file = <any> ({ status: FileUploadStatus.Pending });
|
||||||
|
spyOn(service.fileUploadCancelled, 'next');
|
||||||
|
|
||||||
|
service.cancelUpload(file);
|
||||||
|
|
||||||
|
expect(service.fileUploadCancelled.next).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
import { EventEmitter, Injectable } from '@angular/core';
|
import { EventEmitter, Injectable } from '@angular/core';
|
||||||
import * as minimatch from 'minimatch';
|
import * as minimatch from 'minimatch';
|
||||||
import { Subject } from 'rxjs/Rx';
|
import { Subject } from 'rxjs/Rx';
|
||||||
import { FileUploadCompleteEvent, FileUploadEvent } from '../events/file.event';
|
import { FileUploadCompleteEvent, FileUploadDeleteEvent, FileUploadErrorEvent, FileUploadEvent } from '../events/file.event';
|
||||||
import { FileModel, FileUploadProgress, FileUploadStatus } from '../models/file.model';
|
import { FileModel, FileUploadProgress, FileUploadStatus } from '../models/file.model';
|
||||||
import { AlfrescoApiService } from './alfresco-api.service';
|
import { AlfrescoApiService } from './alfresco-api.service';
|
||||||
import { AppConfigService } from './app-config.service';
|
import { AppConfigService } from './app-config.service';
|
||||||
@@ -30,6 +30,7 @@ export class UploadService {
|
|||||||
private cache: { [key: string]: any } = {};
|
private cache: { [key: string]: any } = {};
|
||||||
private totalComplete: number = 0;
|
private totalComplete: number = 0;
|
||||||
private totalAborted: number = 0;
|
private totalAborted: number = 0;
|
||||||
|
private totalError: number = 0;
|
||||||
private activeTask: Promise<any> = null;
|
private activeTask: Promise<any> = null;
|
||||||
private excludedFileList: String[] = [];
|
private excludedFileList: String[] = [];
|
||||||
|
|
||||||
@@ -39,8 +40,9 @@ export class UploadService {
|
|||||||
fileUploadCancelled: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
fileUploadCancelled: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||||
fileUploadProgress: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
fileUploadProgress: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||||
fileUploadAborted: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
fileUploadAborted: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
||||||
fileUploadError: Subject<FileUploadEvent> = new Subject<FileUploadEvent>();
|
fileUploadError: Subject<FileUploadErrorEvent> = new Subject<FileUploadErrorEvent>();
|
||||||
fileUploadComplete: Subject<FileUploadCompleteEvent> = new Subject<FileUploadCompleteEvent>();
|
fileUploadComplete: Subject<FileUploadCompleteEvent> = new Subject<FileUploadCompleteEvent>();
|
||||||
|
fileUploadDeleted: Subject<FileUploadDeleteEvent> = new Subject<FileUploadDeleteEvent>();
|
||||||
|
|
||||||
constructor(private apiService: AlfrescoApiService, private appConfigService: AppConfigService) {
|
constructor(private apiService: AlfrescoApiService, private appConfigService: AppConfigService) {
|
||||||
this.excludedFileList = <String[]> this.appConfigService.get('files.excluded');
|
this.excludedFileList = <String[]> this.appConfigService.get('files.excluded');
|
||||||
@@ -123,17 +125,15 @@ export class UploadService {
|
|||||||
|
|
||||||
cancelUpload(...files: FileModel[]) {
|
cancelUpload(...files: FileModel[]) {
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
file.status = FileUploadStatus.Cancelled;
|
|
||||||
|
|
||||||
const promise = this.cache[file.id];
|
const promise = this.cache[file.id];
|
||||||
|
|
||||||
if (promise) {
|
if (promise) {
|
||||||
promise.abort();
|
promise.abort();
|
||||||
delete this.cache[file.id];
|
delete this.cache[file.id];
|
||||||
|
} else {
|
||||||
|
const performAction = this.getAction(file);
|
||||||
|
performAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = new FileUploadEvent(file, FileUploadStatus.Cancelled);
|
|
||||||
this.fileUpload.next(event);
|
|
||||||
this.fileUploadCancelled.next(event);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +141,7 @@ export class UploadService {
|
|||||||
this.queue = [];
|
this.queue = [];
|
||||||
this.totalComplete = 0;
|
this.totalComplete = 0;
|
||||||
this.totalAborted = 0;
|
this.totalAborted = 0;
|
||||||
|
this.totalError = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
getUploadPromise(file: FileModel) {
|
getUploadPromise(file: FileModel) {
|
||||||
@@ -183,7 +184,7 @@ export class UploadService {
|
|||||||
emitter.emit({ value: data });
|
emitter.emit({ value: data });
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.onUploadError(file, err);
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
@@ -212,13 +213,14 @@ export class UploadService {
|
|||||||
private onUploadError(file: FileModel, error: any): void {
|
private onUploadError(file: FileModel, error: any): void {
|
||||||
if (file) {
|
if (file) {
|
||||||
file.status = FileUploadStatus.Error;
|
file.status = FileUploadStatus.Error;
|
||||||
|
this.totalError++;
|
||||||
|
|
||||||
const promise = this.cache[file.id];
|
const promise = this.cache[file.id];
|
||||||
if (promise) {
|
if (promise) {
|
||||||
delete this.cache[file.id];
|
delete this.cache[file.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = new FileUploadEvent(file, FileUploadStatus.Error, error);
|
const event = new FileUploadErrorEvent(file, error, this.totalError);
|
||||||
this.fileUpload.next(event);
|
this.fileUpload.next(event);
|
||||||
this.fileUploadError.next(event);
|
this.fileUploadError.next(event);
|
||||||
}
|
}
|
||||||
@@ -257,4 +259,35 @@ export class UploadService {
|
|||||||
promise.next();
|
promise.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onUploadCancelled(file: FileModel): void {
|
||||||
|
if (file) {
|
||||||
|
file.status = FileUploadStatus.Cancelled;
|
||||||
|
|
||||||
|
const event = new FileUploadEvent(file, FileUploadStatus.Cancelled);
|
||||||
|
this.fileUpload.next(event);
|
||||||
|
this.fileUploadCancelled.next(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onUploadDeleted(file: FileModel): void {
|
||||||
|
if (file) {
|
||||||
|
file.status = FileUploadStatus.Deleted;
|
||||||
|
this.totalComplete--;
|
||||||
|
|
||||||
|
const event = new FileUploadDeleteEvent(file, this.totalComplete);
|
||||||
|
this.fileUpload.next(event);
|
||||||
|
this.fileUploadDeleted.next(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getAction(file) {
|
||||||
|
const actions = {
|
||||||
|
[FileUploadStatus.Pending]: () => this.onUploadCancelled(file),
|
||||||
|
[FileUploadStatus.Deleted]: () => this.onUploadDeleted(file),
|
||||||
|
[FileUploadStatus.Error]: () => this.onUploadError(file, null)
|
||||||
|
};
|
||||||
|
|
||||||
|
return actions[file.status];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -43,7 +43,7 @@
|
|||||||
@include mat-comment-list-theme($theme);
|
@include mat-comment-list-theme($theme);
|
||||||
@include mat-start-task-theme($theme);
|
@include mat-start-task-theme($theme);
|
||||||
@include mat-people-search-theme($theme);
|
@include mat-people-search-theme($theme);
|
||||||
@include mat-file-uploading-list-theme($theme);
|
@include mat-file-uploading-row-theme($theme);
|
||||||
@include mat-people-theme($theme);
|
@include mat-people-theme($theme);
|
||||||
@include mat-login-theme($theme);
|
@include mat-login-theme($theme);
|
||||||
@include mat-accordion-theme($theme);
|
@include mat-accordion-theme($theme);
|
||||||
|
@@ -240,6 +240,7 @@ Note:
|
|||||||
| fileUploadAborted | FileUploadEvent | Raised when file upload gets aborted by the server. |
|
| fileUploadAborted | FileUploadEvent | Raised when file upload gets aborted by the server. |
|
||||||
| fileUploadError | FileUploadEvent | Raised when an error occurs to file upload. |
|
| fileUploadError | FileUploadEvent | Raised when an error occurs to file upload. |
|
||||||
| fileUploadComplete | FileUploadCompleteEvent | Raised when file upload is complete. |
|
| fileUploadComplete | FileUploadCompleteEvent | Raised when file upload is complete. |
|
||||||
|
| fileUploadDelete | FileUploadDeleteEvent | Raised when uploaded file is removed from server. |
|
||||||
|
|
||||||
## Build from sources
|
## Build from sources
|
||||||
|
|
||||||
|
@@ -26,7 +26,6 @@ import { FileUploadingListComponent } from './src/components/file-uploading-list
|
|||||||
import { UploadButtonComponent } from './src/components/upload-button.component';
|
import { UploadButtonComponent } from './src/components/upload-button.component';
|
||||||
import { UploadDragAreaComponent } from './src/components/upload-drag-area.component';
|
import { UploadDragAreaComponent } from './src/components/upload-drag-area.component';
|
||||||
import { FileDraggableDirective } from './src/directives/file-draggable.directive';
|
import { FileDraggableDirective } from './src/directives/file-draggable.directive';
|
||||||
import { FileUploadService } from './src/services/file-uploading.service';
|
|
||||||
|
|
||||||
export * from './src/components/upload-button.component';
|
export * from './src/components/upload-button.component';
|
||||||
export * from './src/components/file-uploading-dialog.component';
|
export * from './src/components/file-uploading-dialog.component';
|
||||||
@@ -35,7 +34,6 @@ export * from './src/directives/file-draggable.directive';
|
|||||||
export * from './src/components/file-uploading-list.component';
|
export * from './src/components/file-uploading-list.component';
|
||||||
export * from './src/components/file-uploading-list-row.component';
|
export * from './src/components/file-uploading-list-row.component';
|
||||||
export * from './src/models/permissions.model';
|
export * from './src/models/permissions.model';
|
||||||
export * from './src/services/file-uploading.service';
|
|
||||||
|
|
||||||
export const UPLOAD_DIRECTIVES: any[] = [
|
export const UPLOAD_DIRECTIVES: any[] = [
|
||||||
FileDraggableDirective,
|
FileDraggableDirective,
|
||||||
@@ -46,10 +44,6 @@ export const UPLOAD_DIRECTIVES: any[] = [
|
|||||||
FileUploadingListRowComponent
|
FileUploadingListRowComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
export const UPLOAD_PROVIDERS: any[] = [
|
|
||||||
FileUploadService
|
|
||||||
];
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CoreModule,
|
CoreModule,
|
||||||
@@ -59,7 +53,6 @@ export const UPLOAD_PROVIDERS: any[] = [
|
|||||||
...UPLOAD_DIRECTIVES
|
...UPLOAD_DIRECTIVES
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
...UPLOAD_PROVIDERS,
|
|
||||||
{
|
{
|
||||||
provide: TRANSLATION_PROVIDER,
|
provide: TRANSLATION_PROVIDER,
|
||||||
multi: true,
|
multi: true,
|
||||||
@@ -76,10 +69,7 @@ export const UPLOAD_PROVIDERS: any[] = [
|
|||||||
export class UploadModule {
|
export class UploadModule {
|
||||||
static forRoot(): ModuleWithProviders {
|
static forRoot(): ModuleWithProviders {
|
||||||
return {
|
return {
|
||||||
ngModule: UploadModule,
|
ngModule: UploadModule
|
||||||
providers: [
|
|
||||||
...UPLOAD_PROVIDERS
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,7 +27,7 @@
|
|||||||
*ngIf="uploadList.isUploadCompleted()">
|
*ngIf="uploadList.isUploadCompleted()">
|
||||||
{{ 'FILE_UPLOAD.MESSAGES.UPLOAD_COMPLETED'
|
{{ 'FILE_UPLOAD.MESSAGES.UPLOAD_COMPLETED'
|
||||||
| translate: {
|
| translate: {
|
||||||
completed: (totalCompleted - uploadList.uploadCancelledFiles.length),
|
completed: totalCompleted,
|
||||||
total: filesUploadingList.length
|
total: filesUploadingList.length
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -42,12 +42,12 @@
|
|||||||
|
|
||||||
<section
|
<section
|
||||||
class="upload-dialog__info"
|
class="upload-dialog__info"
|
||||||
*ngIf="uploadList.uploadErrorFiles.length">
|
*ngIf="totalErrors">
|
||||||
{{
|
{{
|
||||||
(uploadList.uploadErrorFiles.length > 1
|
(totalErrors > 1
|
||||||
? 'FILE_UPLOAD.MESSAGES.UPLOAD_ERRORS'
|
? 'FILE_UPLOAD.MESSAGES.UPLOAD_ERRORS'
|
||||||
: 'FILE_UPLOAD.MESSAGES.UPLOAD_ERROR')
|
: 'FILE_UPLOAD.MESSAGES.UPLOAD_ERROR')
|
||||||
| translate: { total: uploadList.uploadErrorFiles.length }
|
| translate: { total: totalErrors }
|
||||||
}}
|
}}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
<adf-file-uploading-list-row
|
<adf-file-uploading-list-row
|
||||||
[file]="file"
|
[file]="file"
|
||||||
(remove)="uploadList.removeFile(file)"
|
(remove)="uploadList.removeFile(file)"
|
||||||
(cancel)="uploadList.cancelFileUpload(file)">
|
(cancel)="uploadList.cancelFile(file)">
|
||||||
</adf-file-uploading-list-row>
|
</adf-file-uploading-list-row>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</adf-file-uploading-list>
|
</adf-file-uploading-list>
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
color="primary"
|
color="primary"
|
||||||
*ngIf="!uploadList.isUploadCompleted() && !uploadList.isUploadCancelled()"
|
*ngIf="!uploadList.isUploadCompleted() && !uploadList.isUploadCancelled()"
|
||||||
md-button
|
md-button
|
||||||
(click)="uploadList.cancelAllFiles($event)">
|
(click)="uploadList.cancelAllFiles()">
|
||||||
{{ 'ADF_FILE_UPLOAD.BUTTON.CANCEL_ALL' | translate }}
|
{{ 'ADF_FILE_UPLOAD.BUTTON.CANCEL_ALL' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
@@ -16,8 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
|
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { FileModel, FileUploadCompleteEvent, UploadService } from 'ng2-alfresco-core';
|
import { FileModel, FileUploadCompleteEvent, FileUploadDeleteEvent,
|
||||||
import { Subscription } from 'rxjs/Rx';
|
FileUploadErrorEvent, UploadService } from 'ng2-alfresco-core';
|
||||||
|
import { Observable, Subscription } from 'rxjs/Rx';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-file-uploading-dialog, file-uploading-dialog',
|
selector: 'adf-file-uploading-dialog, file-uploading-dialog',
|
||||||
@@ -31,35 +32,47 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
|||||||
filesUploadingList: FileModel[] = [];
|
filesUploadingList: FileModel[] = [];
|
||||||
isDialogActive: boolean = false;
|
isDialogActive: boolean = false;
|
||||||
totalCompleted: number = 0;
|
totalCompleted: number = 0;
|
||||||
|
totalErrors: number = 0;
|
||||||
isDialogMinimized: boolean = false;
|
isDialogMinimized: boolean = false;
|
||||||
uploadFilesCompleted: boolean = false;
|
|
||||||
|
|
||||||
private listSubscription: Subscription;
|
private listSubscription: Subscription;
|
||||||
private counterSubscription: Subscription;
|
private counterSubscription: Subscription;
|
||||||
private fileUploadSubscription: Subscription;
|
private fileUploadSubscription: Subscription;
|
||||||
|
private errorSubscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private uploadService: UploadService,
|
private uploadService: UploadService,
|
||||||
private changeDetecor: ChangeDetectorRef) {
|
private changeDetecor: ChangeDetectorRef) {}
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.listSubscription = this.uploadService
|
this.listSubscription = this.uploadService
|
||||||
.queueChanged.subscribe((fileList: FileModel[]) => {
|
.queueChanged.subscribe((fileList: FileModel[]) => {
|
||||||
this.filesUploadingList = fileList;
|
this.filesUploadingList = fileList;
|
||||||
|
|
||||||
if (this.filesUploadingList.length > 0) {
|
if (this.filesUploadingList.length) {
|
||||||
this.isDialogActive = true;
|
this.isDialogActive = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.counterSubscription = this.uploadService
|
this.counterSubscription = Observable
|
||||||
.fileUploadComplete.subscribe((event: FileUploadCompleteEvent) => {
|
.merge(
|
||||||
|
this.uploadService.fileUploadComplete,
|
||||||
|
this.uploadService.fileUploadDeleted
|
||||||
|
)
|
||||||
|
.subscribe((event: (FileUploadDeleteEvent|FileUploadCompleteEvent)) => {
|
||||||
this.totalCompleted = event.totalComplete;
|
this.totalCompleted = event.totalComplete;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.errorSubscription = this.uploadService.fileUploadError
|
||||||
|
.subscribe((event: FileUploadErrorEvent) => {
|
||||||
|
this.totalErrors = event.totalError;
|
||||||
|
this.changeDetecor.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
this.fileUploadSubscription = this.uploadService
|
this.fileUploadSubscription = this.uploadService
|
||||||
.fileUpload.subscribe(() => this.changeDetecor.detectChanges());
|
.fileUpload.subscribe(() => {
|
||||||
|
this.changeDetecor.detectChanges();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,6 +88,7 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
close(): void {
|
close(): void {
|
||||||
this.totalCompleted = 0;
|
this.totalCompleted = 0;
|
||||||
|
this.totalErrors = 0;
|
||||||
this.filesUploadingList = [];
|
this.filesUploadingList = [];
|
||||||
this.isDialogActive = false;
|
this.isDialogActive = false;
|
||||||
this.isDialogMinimized = false;
|
this.isDialogMinimized = false;
|
||||||
@@ -87,5 +101,6 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
|||||||
this.listSubscription.unsubscribe();
|
this.listSubscription.unsubscribe();
|
||||||
this.counterSubscription.unsubscribe();
|
this.counterSubscription.unsubscribe();
|
||||||
this.fileUploadSubscription.unsubscribe();
|
this.fileUploadSubscription.unsubscribe();
|
||||||
|
this.errorSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,28 +1,28 @@
|
|||||||
<div class="adf-file-uploading-list">
|
<div class="adf-file-uploading-row">
|
||||||
<md-icon
|
<md-icon
|
||||||
md-list-icon
|
md-list-icon
|
||||||
class="list-row__type">
|
class="adf-file-uploading-row__type">
|
||||||
insert_drive_file
|
insert_drive_file
|
||||||
</md-icon>
|
</md-icon>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="list-row__name"
|
class="adf-file-uploading-row__name"
|
||||||
title="{{ file.name }}">
|
title="{{ file.name }}">
|
||||||
{{ file.name }}
|
{{ file.name }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
*ngIf="file.status === FileUploadStatus.Progress"
|
*ngIf="file.status === FileUploadStatus.Progress || file.status === FileUploadStatus.Starting"
|
||||||
(click)="onCancel(file)"
|
(click)="onCancel(file)"
|
||||||
class="list-row__group list-row__group--toggle"
|
class="adf-file-uploading-row__group adf-file-uploading-row__group--toggle"
|
||||||
title="{{ 'ADF_FILE_UPLOAD.BUTTON.CANCEL_FILE' | translate }}">
|
title="{{ 'ADF_FILE_UPLOAD.BUTTON.CANCEL_FILE' | translate }}">
|
||||||
<span class="list-row__status">
|
<span class="adf-file-uploading-row__status">
|
||||||
{{ file.progress.loaded | adfFileSize }} / {{ file.progress.total | adfFileSize }}
|
{{ file.progress.loaded | adfFileSize }} / {{ file.progress.total | adfFileSize }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<md-icon
|
<md-icon
|
||||||
md-list-icon
|
md-list-icon
|
||||||
class="list-row__action list-row__action--cancel">
|
class="adf-file-uploading-row__action adf-file-uploading-row__action--cancel">
|
||||||
clear
|
clear
|
||||||
</md-icon>
|
</md-icon>
|
||||||
</div>
|
</div>
|
||||||
@@ -30,24 +30,41 @@
|
|||||||
<div
|
<div
|
||||||
*ngIf="file.status === FileUploadStatus.Complete"
|
*ngIf="file.status === FileUploadStatus.Complete"
|
||||||
(click)="onRemove(file)"
|
(click)="onRemove(file)"
|
||||||
class="list-row__group list-row__group--toggle"
|
class="adf-file-uploading-row__group adf-file-uploading-row__group--toggle"
|
||||||
title="{{ 'ADF_FILE_UPLOAD.BUTTON.REMOVE_FILE' | translate }}">
|
title="{{ 'ADF_FILE_UPLOAD.BUTTON.REMOVE_FILE' | translate }}">
|
||||||
<md-icon
|
<md-icon
|
||||||
md-list-icon
|
md-list-icon
|
||||||
class="list-row__status list-row__status--done">
|
class="adf-file-uploading-row__status adf-file-uploading-row__status--done">
|
||||||
check_circle
|
check_circle
|
||||||
</md-icon>
|
</md-icon>
|
||||||
|
|
||||||
<md-icon
|
<md-icon
|
||||||
md-list-icon
|
md-list-icon
|
||||||
class="list-row__action list-row__action--remove">
|
class="adf-file-uploading-row__action adf-file-uploading-row__action--remove">
|
||||||
|
remove_circle
|
||||||
|
</md-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
*ngIf="file.status === FileUploadStatus.Pending"
|
||||||
|
(click)="onCancel(file)"
|
||||||
|
class="adf-file-uploading-row__group adf-file-uploading-row__group--toggle">
|
||||||
|
<md-icon
|
||||||
|
md-list-icon
|
||||||
|
class="adf-file-uploading-row__status adf-file-uploading-row__status--pending">
|
||||||
|
schedule
|
||||||
|
</md-icon>
|
||||||
|
|
||||||
|
<md-icon
|
||||||
|
md-list-icon
|
||||||
|
class="adf-file-uploading-row__action adf-file-uploading-row__action--remove">
|
||||||
remove_circle
|
remove_circle
|
||||||
</md-icon>
|
</md-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
*ngIf="file.status === FileUploadStatus.Error"
|
*ngIf="file.status === FileUploadStatus.Error"
|
||||||
class="list-row__block list-row__status--error"
|
class="adf-file-uploading-row__block adf-file-uploading-row__status--error"
|
||||||
title="{{ file.response }}">
|
title="{{ file.response }}">
|
||||||
<md-icon md-list-icon>
|
<md-icon md-list-icon>
|
||||||
report_problem
|
report_problem
|
||||||
@@ -55,8 +72,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
*ngIf="file.status === FileUploadStatus.Cancelled || file.status === FileUploadStatus.Aborted"
|
*ngIf="file.status === FileUploadStatus.Cancelled ||
|
||||||
class="list-row__block list-row__status--cancelled">
|
file.status === FileUploadStatus.Aborted ||
|
||||||
|
file.status === FileUploadStatus.Deleted"
|
||||||
|
class="adf-file-uploading-row__block adf-file-uploading-row__status--cancelled">
|
||||||
{{ 'ADF_FILE_UPLOAD.STATUS.FILE_CANCELED_STATUS' | translate }}
|
{{ 'ADF_FILE_UPLOAD.STATUS.FILE_CANCELED_STATUS' | translate }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div>
|
@@ -1,23 +1,21 @@
|
|||||||
@import 'colors';
|
@import 'colors';
|
||||||
|
|
||||||
@mixin mat-file-uploading-list-theme($theme) {
|
@mixin mat-file-uploading-row-theme($theme) {
|
||||||
$primary: map-get($theme, primary);
|
$primary: map-get($theme, primary);
|
||||||
$accent: map-get($theme, accent);
|
$accent: map-get($theme, accent);
|
||||||
$warn: map-get($theme, warn);
|
$warn: map-get($theme, warn);
|
||||||
|
|
||||||
|
|
||||||
.adf-file-uploading-list {
|
.adf-file-uploading-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0.5em 1em 0.5em 1em;
|
padding: 0.5em 1em 0.5em 1em;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
&:not(:first-child) {
|
&:not(:first-child) {
|
||||||
border-top: 1px solid ;
|
border-top: 1px solid ;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-row {
|
|
||||||
cursor: default;
|
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -35,20 +33,20 @@
|
|||||||
&__group--toggle {
|
&__group--toggle {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.list-row__status {
|
.adf-file-uploading-row__status {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-row__action {
|
.adf-file-uploading-row__action {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.list-row__status {
|
.adf-file-uploading-row__status {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-row__action {
|
.adf-file-uploading-row__action {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,4 +69,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@@ -16,10 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { AlfrescoTranslationService, FileModel, FileUploadStatus, NodesApiService, NotificationService, UploadService } from 'ng2-alfresco-core';
|
import { AlfrescoTranslationService, FileUploadStatus, NodesApiService, NotificationService, UploadService } from 'ng2-alfresco-core';
|
||||||
import { Observable } from 'rxjs/Rx';
|
import { Observable } from 'rxjs/Rx';
|
||||||
import { UploadModule } from '../../index';
|
import { UploadModule } from '../../index';
|
||||||
import { FileUploadService } from '../services/file-uploading.service';
|
|
||||||
import { FileUploadingListComponent } from './file-uploading-list.component';
|
import { FileUploadingListComponent } from './file-uploading-list.component';
|
||||||
|
|
||||||
describe('FileUploadingListComponent', () => {
|
describe('FileUploadingListComponent', () => {
|
||||||
@@ -27,10 +26,13 @@ describe('FileUploadingListComponent', () => {
|
|||||||
let component: FileUploadingListComponent;
|
let component: FileUploadingListComponent;
|
||||||
let uploadService: UploadService;
|
let uploadService: UploadService;
|
||||||
let nodesApiService: NodesApiService;
|
let nodesApiService: NodesApiService;
|
||||||
let fileUploadService: FileUploadService;
|
|
||||||
let notificationService: NotificationService;
|
let notificationService: NotificationService;
|
||||||
let translateService: AlfrescoTranslationService;
|
let translateService: AlfrescoTranslationService;
|
||||||
let file = new FileModel(<File> { name: 'fake-name' });
|
let file: any;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
file = { data: { entry: { id: 'x' } } };
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -43,66 +45,128 @@ describe('FileUploadingListComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
nodesApiService = TestBed.get(NodesApiService);
|
nodesApiService = TestBed.get(NodesApiService);
|
||||||
uploadService = TestBed.get(UploadService);
|
uploadService = TestBed.get(UploadService);
|
||||||
fileUploadService = TestBed.get(FileUploadService);
|
|
||||||
notificationService = TestBed.get(NotificationService);
|
notificationService = TestBed.get(NotificationService);
|
||||||
translateService = TestBed.get(AlfrescoTranslationService);
|
translateService = TestBed.get(AlfrescoTranslationService);
|
||||||
fixture = TestBed.createComponent(FileUploadingListComponent);
|
fixture = TestBed.createComponent(FileUploadingListComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.files = [ file ];
|
|
||||||
file.data = { entry: { id: 'x' } };
|
|
||||||
|
|
||||||
spyOn(translateService, 'get').and.returnValue(Observable.of('some error message'));
|
spyOn(translateService, 'get').and.returnValue(Observable.of('some error message'));
|
||||||
|
spyOn(notificationService, 'openSnackMessage');
|
||||||
|
spyOn(uploadService, 'cancelUpload');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('cancelFileUpload()', () => {
|
describe('cancelFile()', () => {
|
||||||
it('should call uploadService api when cancelling a file', () => {
|
it('should call uploadService api when cancelling a file', () => {
|
||||||
spyOn(uploadService, 'cancelUpload');
|
component.cancelFile(file);
|
||||||
component.cancelFileUpload(file);
|
|
||||||
|
|
||||||
expect(uploadService.cancelUpload).toHaveBeenCalledWith(file);
|
expect(uploadService.cancelUpload).toHaveBeenCalledWith(file);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('removeFile()', () => {
|
describe('removeFile()', () => {
|
||||||
it('should remove file successfully when api returns success', () => {
|
it('should change file status when api returns success', () => {
|
||||||
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.of('success'));
|
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.of(file));
|
||||||
spyOn(fileUploadService, 'emitFileRemoved');
|
|
||||||
|
|
||||||
component.removeFile(file);
|
component.removeFile(file);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(fileUploadService.emitFileRemoved).toHaveBeenCalledWith(file);
|
expect(file.status).toBe(FileUploadStatus.Deleted);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should notify on remove file fail when api returns error', () => {
|
it('should change file status when api returns error', () => {
|
||||||
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.throw({}));
|
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.throw(file));
|
||||||
spyOn(notificationService, 'openSnackMessage');
|
|
||||||
|
component.removeFile(file);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(file.status).toBe(FileUploadStatus.Error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should notify fail when api returns error', () => {
|
||||||
|
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.throw(file));
|
||||||
|
|
||||||
component.removeFile(file);
|
component.removeFile(file);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(notificationService.openSnackMessage).toHaveBeenCalled();
|
expect(notificationService.openSnackMessage).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should call uploadService on error', () => {
|
||||||
|
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.throw(file));
|
||||||
|
|
||||||
|
component.removeFile(file);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(uploadService.cancelUpload).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call uploadService on success', () => {
|
||||||
|
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.of(file));
|
||||||
|
|
||||||
|
component.removeFile(file);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(uploadService.cancelUpload).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('cancelAllFiles()', () => {
|
describe('cancelAllFiles()', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(component, 'removeFile');
|
component.files = <any> [
|
||||||
spyOn(component, 'cancelFileUpload');
|
{
|
||||||
|
data: {
|
||||||
|
entry: { id: '1' }
|
||||||
|
},
|
||||||
|
status: FileUploadStatus.Cancelled
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
entry: { id: '2' }
|
||||||
|
},
|
||||||
|
status: FileUploadStatus.Error
|
||||||
|
}
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call removeFile() if file was uploaded', () => {
|
it('should not call deleteNode if there are no competed uploads', () => {
|
||||||
file.status = FileUploadStatus.Complete;
|
spyOn(nodesApiService, 'deleteNode');
|
||||||
component.cancelAllFiles(null);
|
|
||||||
|
|
||||||
expect(component.removeFile).toHaveBeenCalledWith(file);
|
component.cancelAllFiles();
|
||||||
|
|
||||||
|
expect(nodesApiService.deleteNode).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call cancelFileUpload() if file is being uploaded', () => {
|
it('should not call uploadService if there are no uploading files', () => {
|
||||||
file.status = FileUploadStatus.Progress;
|
component.cancelAllFiles();
|
||||||
component.cancelAllFiles(null);
|
|
||||||
|
|
||||||
expect(component.cancelFileUpload).toHaveBeenCalledWith(file);
|
expect(uploadService.cancelUpload).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call deleteNode when there are completed uploads', () => {
|
||||||
|
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.of({}));
|
||||||
|
|
||||||
|
component.files[0].status = FileUploadStatus.Complete;
|
||||||
|
component.cancelAllFiles();
|
||||||
|
|
||||||
|
expect(nodesApiService.deleteNode).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call uploadService when there are uploading files', () => {
|
||||||
|
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.of({}));
|
||||||
|
|
||||||
|
component.files[0].status = FileUploadStatus.Progress;
|
||||||
|
component.cancelAllFiles();
|
||||||
|
|
||||||
|
expect(uploadService.cancelUpload).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should notify on deleting file error', () => {
|
||||||
|
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.throw({}));
|
||||||
|
|
||||||
|
component.files[0].status = FileUploadStatus.Complete;
|
||||||
|
component.cancelAllFiles();
|
||||||
|
|
||||||
|
expect(notificationService.openSnackMessage).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -110,7 +174,6 @@ describe('FileUploadingListComponent', () => {
|
|||||||
it('should return false when at least one file is in progress', () => {
|
it('should return false when at least one file is in progress', () => {
|
||||||
component.files = <any> [
|
component.files = <any> [
|
||||||
{ status: FileUploadStatus.Progress },
|
{ status: FileUploadStatus.Progress },
|
||||||
{ status: FileUploadStatus.Cancelled },
|
|
||||||
{ status: FileUploadStatus.Complete }
|
{ status: FileUploadStatus.Complete }
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -120,23 +183,39 @@ describe('FileUploadingListComponent', () => {
|
|||||||
it('should return false when at least one file is in pending', () => {
|
it('should return false when at least one file is in pending', () => {
|
||||||
component.files = <any> [
|
component.files = <any> [
|
||||||
{ status: FileUploadStatus.Pending },
|
{ status: FileUploadStatus.Pending },
|
||||||
{ status: FileUploadStatus.Cancelled },
|
|
||||||
{ status: FileUploadStatus.Complete }
|
{ status: FileUploadStatus.Complete }
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(component.isUploadCompleted()).toBe(false);
|
expect(component.isUploadCompleted()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when none of the files is completed', () => {
|
it('should return false when at least one file is in starting state', () => {
|
||||||
component.files = <any> [
|
component.files = <any> [
|
||||||
{ status: FileUploadStatus.Error },
|
{ status: FileUploadStatus.Starting },
|
||||||
{ status: FileUploadStatus.Error },
|
{ status: FileUploadStatus.Complete }
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(component.isUploadCompleted()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when files are cancelled', () => {
|
||||||
|
component.files = <any> [
|
||||||
|
{ status: FileUploadStatus.Cancelled },
|
||||||
{ status: FileUploadStatus.Cancelled }
|
{ status: FileUploadStatus.Cancelled }
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(component.isUploadCompleted()).toBe(false);
|
expect(component.isUploadCompleted()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return true when there are deleted files', () => {
|
||||||
|
component.files = <any> [
|
||||||
|
{ status: FileUploadStatus.Complete },
|
||||||
|
{ status: FileUploadStatus.Deleted }
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(component.isUploadCompleted()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return true when none of the files is in progress', () => {
|
it('should return true when none of the files is in progress', () => {
|
||||||
component.files = <any> [
|
component.files = <any> [
|
||||||
{ status: FileUploadStatus.Error },
|
{ status: FileUploadStatus.Error },
|
||||||
@@ -191,7 +270,6 @@ describe('FileUploadingListComponent', () => {
|
|||||||
|
|
||||||
it('should return true when all files are aborted', () => {
|
it('should return true when all files are aborted', () => {
|
||||||
component.files = <any> [
|
component.files = <any> [
|
||||||
{ status: FileUploadStatus.Aborted },
|
|
||||||
{ status: FileUploadStatus.Aborted }
|
{ status: FileUploadStatus.Aborted }
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -202,53 +280,10 @@ describe('FileUploadingListComponent', () => {
|
|||||||
component.files = <any> [
|
component.files = <any> [
|
||||||
{ status: FileUploadStatus.Cancelled },
|
{ status: FileUploadStatus.Cancelled },
|
||||||
{ status: FileUploadStatus.Cancelled },
|
{ status: FileUploadStatus.Cancelled },
|
||||||
{ status: FileUploadStatus.Error }
|
{ status: FileUploadStatus.Aborted }
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(component.isUploadCancelled()).toBe(true);
|
expect(component.isUploadCancelled()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('uploadErrorFiles()', () => {
|
|
||||||
it('should return array of error files', () => {
|
|
||||||
component.files = <any> [
|
|
||||||
{ status: FileUploadStatus.Complete },
|
|
||||||
{ status: FileUploadStatus.Error },
|
|
||||||
{ status: FileUploadStatus.Error }
|
|
||||||
];
|
|
||||||
|
|
||||||
expect(component.uploadErrorFiles.length).toEqual(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return empty array when no error files found', () => {
|
|
||||||
component.files = <any> [
|
|
||||||
{ status: FileUploadStatus.Complete },
|
|
||||||
{ status: FileUploadStatus.Pending }
|
|
||||||
];
|
|
||||||
|
|
||||||
expect(component.uploadErrorFiles.length).toEqual(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('uploadCancelledFiles()', () => {
|
|
||||||
it('should return array of cancelled files', () => {
|
|
||||||
component.files = <any> [
|
|
||||||
{ status: FileUploadStatus.Cancelled },
|
|
||||||
{ status: FileUploadStatus.Complete },
|
|
||||||
{ status: FileUploadStatus.Error }
|
|
||||||
];
|
|
||||||
|
|
||||||
expect(component.uploadCancelledFiles.length).toEqual(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return emty array when no cancelled files found', () => {
|
|
||||||
component.files = <any> [
|
|
||||||
{ status: FileUploadStatus.Error },
|
|
||||||
{ status: FileUploadStatus.Complete },
|
|
||||||
{ status: FileUploadStatus.Pending }
|
|
||||||
];
|
|
||||||
|
|
||||||
expect(component.uploadCancelledFiles.length).toEqual(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import { Component, ContentChild, Input, TemplateRef } from '@angular/core';
|
import { Component, ContentChild, Input, TemplateRef } from '@angular/core';
|
||||||
import { AlfrescoTranslationService, FileModel, FileUploadStatus, NodesApiService, NotificationService, UploadService } from 'ng2-alfresco-core';
|
import { AlfrescoTranslationService, FileModel, FileUploadStatus, NodesApiService, NotificationService, UploadService } from 'ng2-alfresco-core';
|
||||||
import { FileUploadService } from '../services/file-uploading.service';
|
import { Observable } from 'rxjs/Rx';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-file-uploading-list, alfresco-file-uploading-list',
|
selector: 'adf-file-uploading-list, alfresco-file-uploading-list',
|
||||||
@@ -35,7 +35,6 @@ export class FileUploadingListComponent {
|
|||||||
files: FileModel[] = [];
|
files: FileModel[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private fileUploadService: FileUploadService,
|
|
||||||
private uploadService: UploadService,
|
private uploadService: UploadService,
|
||||||
private nodesApi: NodesApiService,
|
private nodesApi: NodesApiService,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
@@ -49,40 +48,42 @@ export class FileUploadingListComponent {
|
|||||||
*
|
*
|
||||||
* @memberOf FileUploadingListComponent
|
* @memberOf FileUploadingListComponent
|
||||||
*/
|
*/
|
||||||
cancelFileUpload(file: FileModel): void {
|
cancelFile(file: FileModel): void {
|
||||||
this.uploadService.cancelUpload(file);
|
this.uploadService.cancelUpload(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFile(file: FileModel): void {
|
removeFile(file: FileModel): void {
|
||||||
const { id } = file.data.entry;
|
this.deleteNode(file)
|
||||||
this.nodesApi
|
.subscribe(() => {
|
||||||
.deleteNode(id, { permanent: true })
|
if ( file.status === FileUploadStatus.Error) {
|
||||||
.subscribe(
|
this.notifyError(file);
|
||||||
() => this.onRemoveSuccess(file),
|
}
|
||||||
() => this.onRemoveFail(file)
|
|
||||||
);
|
this.uploadService.cancelUpload(file);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call the abort method for each file
|
* Call the appropriate method for each file, depending on state
|
||||||
*/
|
*/
|
||||||
cancelAllFiles(event: Event): void {
|
cancelAllFiles(): void {
|
||||||
if (event) {
|
this.getUploadingFiles()
|
||||||
event.preventDefault();
|
.forEach((file) => this.uploadService.cancelUpload(file));
|
||||||
}
|
|
||||||
|
const deletedFiles = this.files
|
||||||
this.files.forEach((file) => {
|
.filter((file) => file.status === FileUploadStatus.Complete)
|
||||||
const { status } = file;
|
.map((file) => this.deleteNode(file));
|
||||||
const { Complete, Progress, Pending } = FileUploadStatus;
|
|
||||||
|
Observable.forkJoin(...deletedFiles)
|
||||||
if (status === Complete) {
|
.subscribe((files: FileModel[]) => {
|
||||||
this.removeFile(file);
|
const errors = files
|
||||||
}
|
.filter((file) => file.status === FileUploadStatus.Error);
|
||||||
|
|
||||||
if (status === Progress || status === Pending) {
|
if (errors.length) {
|
||||||
this.cancelFileUpload(file);
|
this.notifyError(...errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.uploadService.cancelUpload(...files);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ export class FileUploadingListComponent {
|
|||||||
*/
|
*/
|
||||||
isUploadCompleted(): boolean {
|
isUploadCompleted(): boolean {
|
||||||
return !this.isUploadCancelled() &&
|
return !this.isUploadCancelled() &&
|
||||||
!!this.files.length &&
|
Boolean(this.files.length) &&
|
||||||
!this.files
|
!this.files
|
||||||
.some(({status}) =>
|
.some(({status}) =>
|
||||||
status === FileUploadStatus.Starting ||
|
status === FileUploadStatus.Starting ||
|
||||||
@@ -111,38 +112,55 @@ export class FileUploadingListComponent {
|
|||||||
.every(({status}) =>
|
.every(({status}) =>
|
||||||
status === FileUploadStatus.Aborted ||
|
status === FileUploadStatus.Aborted ||
|
||||||
status === FileUploadStatus.Cancelled ||
|
status === FileUploadStatus.Cancelled ||
|
||||||
status === FileUploadStatus.Error
|
status === FileUploadStatus.Deleted
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private deleteNode(file: FileModel): Observable<FileModel> {
|
||||||
* Gets all the files with status Error.
|
const { id } = file.data.entry;
|
||||||
* @returns {boolean} - false if there is none
|
|
||||||
*/
|
return this.nodesApi
|
||||||
get uploadErrorFiles(): FileModel[] {
|
.deleteNode(id, { permanent: true })
|
||||||
return this.files.filter(({status}) => status === FileUploadStatus.Error);
|
.map(() => {
|
||||||
|
file.status = FileUploadStatus.Deleted;
|
||||||
|
return file;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
file.status = FileUploadStatus.Error;
|
||||||
|
return Observable.of(file);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private notifyError(...files: FileModel[]) {
|
||||||
* Gets all the files with status Cancelled.
|
let translateSubscription = null;
|
||||||
* @returns {boolean} - false if there is none
|
|
||||||
*/
|
if (files.length === 1) {
|
||||||
get uploadCancelledFiles(): FileModel[] {
|
translateSubscription = this.translateService
|
||||||
return this.files.filter(({status}) => status === FileUploadStatus.Cancelled);
|
.get(
|
||||||
|
'FILE_UPLOAD.MESSAGES.REMOVE_FILE_ERROR',
|
||||||
|
{ fileName: files[0].name}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
translateSubscription = this.translateService
|
||||||
|
.get(
|
||||||
|
'FILE_UPLOAD.MESSAGES.REMOVE_FILES_ERROR',
|
||||||
|
{ total: files.length }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRemoveSuccess(file: FileModel): void {
|
translateSubscription
|
||||||
const { uploadService, fileUploadService } = this;
|
.subscribe(message => this.notificationService.openSnackMessage(message, 4000));
|
||||||
|
|
||||||
uploadService.cancelUpload(file);
|
|
||||||
fileUploadService.emitFileRemoved(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRemoveFail(file: FileModel): void {
|
private getUploadingFiles() {
|
||||||
this.translateService
|
return this.files.filter((item) => {
|
||||||
.get('FILE_UPLOAD.MESSAGES.REMOVE_FILE_ERROR', { fileName: file.name})
|
if (
|
||||||
.subscribe((message) => {
|
item.status === FileUploadStatus.Pending ||
|
||||||
this.notificationService.openSnackMessage(message, 4000);
|
item.status === FileUploadStatus.Progress ||
|
||||||
|
item.status === FileUploadStatus.Starting
|
||||||
|
) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,8 @@
|
|||||||
"PROGRESS": "Upload in progress...",
|
"PROGRESS": "Upload in progress...",
|
||||||
"FOLDER_ALREADY_EXIST": "The folder {0} already exist",
|
"FOLDER_ALREADY_EXIST": "The folder {0} already exist",
|
||||||
"FOLDER_NOT_SUPPORTED": "Folder upload isn't supported by your browser",
|
"FOLDER_NOT_SUPPORTED": "Folder upload isn't supported by your browser",
|
||||||
"REMOVE_FILE_ERROR": "Error removing file {{ fileName }}"
|
"REMOVE_FILE_ERROR": "Error removing file {{ fileName }}",
|
||||||
|
"REMOVE_FILES_ERROR": "Error removing {{ total }} files"
|
||||||
},
|
},
|
||||||
"ACTION": {
|
"ACTION": {
|
||||||
"UNDO": "Undo"
|
"UNDO": "Undo"
|
||||||
|
@@ -1,43 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Copyright 2016 Alfresco Software, Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { FileModel } from 'ng2-alfresco-core';
|
|
||||||
import { FileUploadService } from './file-uploading.service';
|
|
||||||
|
|
||||||
describe('FileUploadService', () => {
|
|
||||||
let service: FileUploadService;
|
|
||||||
let file = new FileModel(<File> { name: 'fake-name' });
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
service = new FileUploadService();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('emits file remove event', () => {
|
|
||||||
spyOn(service.remove, 'next');
|
|
||||||
service.emitFileRemoved(file);
|
|
||||||
|
|
||||||
expect(service.remove.next).toHaveBeenCalledWith(file);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('passes removed file data', () => {
|
|
||||||
service.emitFileRemoved(file);
|
|
||||||
|
|
||||||
service.remove.subscribe((data) => {
|
|
||||||
expect(data).toEqual(file);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,28 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Copyright 2016 Alfresco Software, Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Subject } from 'rxjs/Rx';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class FileUploadService {
|
|
||||||
remove = new Subject<string>();
|
|
||||||
|
|
||||||
emitFileRemoved(item: any) {
|
|
||||||
this.remove.next(item);
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user