[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:
Cilibiu Bogdan
2017-08-24 12:45:13 +03:00
committed by Mario Romano
parent 1deaa22570
commit 07ba8bc15f
17 changed files with 385 additions and 302 deletions

View File

@@ -240,6 +240,7 @@ Note:
| fileUploadAborted | FileUploadEvent | Raised when file upload gets aborted by the server. |
| fileUploadError | FileUploadEvent | Raised when an error occurs to file upload. |
| fileUploadComplete | FileUploadCompleteEvent | Raised when file upload is complete. |
| fileUploadDelete | FileUploadDeleteEvent | Raised when uploaded file is removed from server. |
## Build from sources

View File

@@ -26,7 +26,6 @@ import { FileUploadingListComponent } from './src/components/file-uploading-list
import { UploadButtonComponent } from './src/components/upload-button.component';
import { UploadDragAreaComponent } from './src/components/upload-drag-area.component';
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/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-row.component';
export * from './src/models/permissions.model';
export * from './src/services/file-uploading.service';
export const UPLOAD_DIRECTIVES: any[] = [
FileDraggableDirective,
@@ -46,10 +44,6 @@ export const UPLOAD_DIRECTIVES: any[] = [
FileUploadingListRowComponent
];
export const UPLOAD_PROVIDERS: any[] = [
FileUploadService
];
@NgModule({
imports: [
CoreModule,
@@ -59,7 +53,6 @@ export const UPLOAD_PROVIDERS: any[] = [
...UPLOAD_DIRECTIVES
],
providers: [
...UPLOAD_PROVIDERS,
{
provide: TRANSLATION_PROVIDER,
multi: true,
@@ -76,10 +69,7 @@ export const UPLOAD_PROVIDERS: any[] = [
export class UploadModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: UploadModule,
providers: [
...UPLOAD_PROVIDERS
]
ngModule: UploadModule
};
}
}

View File

@@ -27,7 +27,7 @@
*ngIf="uploadList.isUploadCompleted()">
{{ 'FILE_UPLOAD.MESSAGES.UPLOAD_COMPLETED'
| translate: {
completed: (totalCompleted - uploadList.uploadCancelledFiles.length),
completed: totalCompleted,
total: filesUploadingList.length
}
}}
@@ -42,12 +42,12 @@
<section
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_ERROR')
| translate: { total: uploadList.uploadErrorFiles.length }
| translate: { total: totalErrors }
}}
</section>
@@ -59,7 +59,7 @@
<adf-file-uploading-list-row
[file]="file"
(remove)="uploadList.removeFile(file)"
(cancel)="uploadList.cancelFileUpload(file)">
(cancel)="uploadList.cancelFile(file)">
</adf-file-uploading-list-row>
</ng-template>
</adf-file-uploading-list>
@@ -70,7 +70,7 @@
color="primary"
*ngIf="!uploadList.isUploadCompleted() && !uploadList.isUploadCancelled()"
md-button
(click)="uploadList.cancelAllFiles($event)">
(click)="uploadList.cancelAllFiles()">
{{ 'ADF_FILE_UPLOAD.BUTTON.CANCEL_ALL' | translate }}
</button>

View File

@@ -16,8 +16,9 @@
*/
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FileModel, FileUploadCompleteEvent, UploadService } from 'ng2-alfresco-core';
import { Subscription } from 'rxjs/Rx';
import { FileModel, FileUploadCompleteEvent, FileUploadDeleteEvent,
FileUploadErrorEvent, UploadService } from 'ng2-alfresco-core';
import { Observable, Subscription } from 'rxjs/Rx';
@Component({
selector: 'adf-file-uploading-dialog, file-uploading-dialog',
@@ -31,35 +32,47 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
filesUploadingList: FileModel[] = [];
isDialogActive: boolean = false;
totalCompleted: number = 0;
totalErrors: number = 0;
isDialogMinimized: boolean = false;
uploadFilesCompleted: boolean = false;
private listSubscription: Subscription;
private counterSubscription: Subscription;
private fileUploadSubscription: Subscription;
private errorSubscription: Subscription;
constructor(
private uploadService: UploadService,
private changeDetecor: ChangeDetectorRef) {
}
private changeDetecor: ChangeDetectorRef) {}
ngOnInit() {
this.listSubscription = this.uploadService
.queueChanged.subscribe((fileList: FileModel[]) => {
this.filesUploadingList = fileList;
if (this.filesUploadingList.length > 0) {
if (this.filesUploadingList.length) {
this.isDialogActive = true;
}
});
this.counterSubscription = this.uploadService
.fileUploadComplete.subscribe((event: FileUploadCompleteEvent) => {
this.counterSubscription = Observable
.merge(
this.uploadService.fileUploadComplete,
this.uploadService.fileUploadDeleted
)
.subscribe((event: (FileUploadDeleteEvent|FileUploadCompleteEvent)) => {
this.totalCompleted = event.totalComplete;
});
this.errorSubscription = this.uploadService.fileUploadError
.subscribe((event: FileUploadErrorEvent) => {
this.totalErrors = event.totalError;
this.changeDetecor.detectChanges();
});
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 {
this.totalCompleted = 0;
this.totalErrors = 0;
this.filesUploadingList = [];
this.isDialogActive = false;
this.isDialogMinimized = false;
@@ -87,5 +101,6 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
this.listSubscription.unsubscribe();
this.counterSubscription.unsubscribe();
this.fileUploadSubscription.unsubscribe();
this.errorSubscription.unsubscribe();
}
}

View File

@@ -1,28 +1,28 @@
<div class="adf-file-uploading-list">
<div class="adf-file-uploading-row">
<md-icon
md-list-icon
class="list-row__type">
class="adf-file-uploading-row__type">
insert_drive_file
</md-icon>
<span
class="list-row__name"
class="adf-file-uploading-row__name"
title="{{ file.name }}">
{{ file.name }}
</span>
<div
*ngIf="file.status === FileUploadStatus.Progress"
*ngIf="file.status === FileUploadStatus.Progress || file.status === FileUploadStatus.Starting"
(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 }}">
<span class="list-row__status">
{{ file.progress.loaded | adfFileSize }} / {{ file.progress.total | adfFileSize }}
</span>
<span class="adf-file-uploading-row__status">
{{ file.progress.loaded | adfFileSize }} / {{ file.progress.total | adfFileSize }}
</span>
<md-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
</md-icon>
</div>
@@ -30,24 +30,41 @@
<div
*ngIf="file.status === FileUploadStatus.Complete"
(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 }}">
<md-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
</md-icon>
<md-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
</md-icon>
</div>
<div
*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 }}">
<md-icon md-list-icon>
report_problem
@@ -55,8 +72,10 @@
</div>
<div
*ngIf="file.status === FileUploadStatus.Cancelled || file.status === FileUploadStatus.Aborted"
class="list-row__block list-row__status--cancelled">
*ngIf="file.status === FileUploadStatus.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 }}
</div>
</div>
<div>

View File

@@ -1,74 +1,71 @@
@import 'colors';
@mixin mat-file-uploading-list-theme($theme) {
@mixin mat-file-uploading-row-theme($theme) {
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$warn: map-get($theme, warn);
.adf-file-uploading-list {
.adf-file-uploading-row {
display: flex;
align-items: center;
padding: 0.5em 1em 0.5em 1em;
cursor: default;
&:not(:first-child) {
border-top: 1px solid ;
}
.list-row {
cursor: default;
&__name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1 1 auto;
padding: 0 1em 0 0.5em;
}
&__name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1 1 auto;
padding: 0 1em 0 0.5em;
}
&__group, &__block {
min-width: 200px;
display: flex;
justify-content: flex-end;
}
&__group, &__block {
min-width: 200px;
&__group--toggle {
cursor: pointer;
.adf-file-uploading-row__status {
display: flex;
justify-content: flex-end;
}
&__group--toggle {
cursor: pointer;
.adf-file-uploading-row__action {
display: none;
}
.list-row__status {
display: flex;
}
.list-row__action {
&:hover {
.adf-file-uploading-row__status {
display: none;
}
&:hover {
.list-row__status {
display: none;
}
.list-row__action {
display: flex;
}
.adf-file-uploading-row__action {
display: flex;
}
}
}
&__status--done {
color: mat-color($accent);
}
&__status--done {
color: mat-color($accent);
}
&__status--error {
color: mat-color($warn);
}
&__status--error {
color: mat-color($warn);
}
&__action--cancel {
color: mat-color($warn);
}
&__action--cancel {
color: mat-color($warn);
}
&__action--remove {
color: mat-color($accent);
}
&__action--remove {
color: mat-color($accent);
}
}
}

View File

@@ -16,10 +16,9 @@
*/
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 { UploadModule } from '../../index';
import { FileUploadService } from '../services/file-uploading.service';
import { FileUploadingListComponent } from './file-uploading-list.component';
describe('FileUploadingListComponent', () => {
@@ -27,10 +26,13 @@ describe('FileUploadingListComponent', () => {
let component: FileUploadingListComponent;
let uploadService: UploadService;
let nodesApiService: NodesApiService;
let fileUploadService: FileUploadService;
let notificationService: NotificationService;
let translateService: AlfrescoTranslationService;
let file = new FileModel(<File> { name: 'fake-name' });
let file: any;
beforeEach(() => {
file = { data: { entry: { id: 'x' } } };
});
beforeEach(() => {
TestBed.configureTestingModule({
@@ -43,66 +45,128 @@ describe('FileUploadingListComponent', () => {
beforeEach(() => {
nodesApiService = TestBed.get(NodesApiService);
uploadService = TestBed.get(UploadService);
fileUploadService = TestBed.get(FileUploadService);
notificationService = TestBed.get(NotificationService);
translateService = TestBed.get(AlfrescoTranslationService);
fixture = TestBed.createComponent(FileUploadingListComponent);
component = fixture.componentInstance;
component.files = [ file ];
file.data = { entry: { id: 'x' } };
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', () => {
spyOn(uploadService, 'cancelUpload');
component.cancelFileUpload(file);
component.cancelFile(file);
expect(uploadService.cancelUpload).toHaveBeenCalledWith(file);
});
});
describe('removeFile()', () => {
it('should remove file successfully when api returns success', () => {
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.of('success'));
spyOn(fileUploadService, 'emitFileRemoved');
it('should change file status when api returns success', () => {
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.of(file));
component.removeFile(file);
fixture.detectChanges();
expect(fileUploadService.emitFileRemoved).toHaveBeenCalledWith(file);
expect(file.status).toBe(FileUploadStatus.Deleted);
});
it('should notify on remove file fail when api returns error', () => {
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.throw({}));
spyOn(notificationService, 'openSnackMessage');
it('should change file status when api returns error', () => {
spyOn(nodesApiService, 'deleteNode').and.returnValue(Observable.throw(file));
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);
fixture.detectChanges();
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()', () => {
beforeEach(() => {
spyOn(component, 'removeFile');
spyOn(component, 'cancelFileUpload');
component.files = <any> [
{
data: {
entry: { id: '1' }
},
status: FileUploadStatus.Cancelled
},
{
data: {
entry: { id: '2' }
},
status: FileUploadStatus.Error
}
];
});
it('should call removeFile() if file was uploaded', () => {
file.status = FileUploadStatus.Complete;
component.cancelAllFiles(null);
it('should not call deleteNode if there are no competed uploads', () => {
spyOn(nodesApiService, 'deleteNode');
expect(component.removeFile).toHaveBeenCalledWith(file);
component.cancelAllFiles();
expect(nodesApiService.deleteNode).not.toHaveBeenCalled();
});
it('should call cancelFileUpload() if file is being uploaded', () => {
file.status = FileUploadStatus.Progress;
component.cancelAllFiles(null);
it('should not call uploadService if there are no uploading files', () => {
component.cancelAllFiles();
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', () => {
component.files = <any> [
{ status: FileUploadStatus.Progress },
{ status: FileUploadStatus.Cancelled },
{ status: FileUploadStatus.Complete }
];
@@ -120,23 +183,39 @@ describe('FileUploadingListComponent', () => {
it('should return false when at least one file is in pending', () => {
component.files = <any> [
{ status: FileUploadStatus.Pending },
{ status: FileUploadStatus.Cancelled },
{ status: FileUploadStatus.Complete }
];
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> [
{ status: FileUploadStatus.Error },
{ status: FileUploadStatus.Error },
{ status: FileUploadStatus.Starting },
{ 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 }
];
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', () => {
component.files = <any> [
{ status: FileUploadStatus.Error },
@@ -191,7 +270,6 @@ describe('FileUploadingListComponent', () => {
it('should return true when all files are aborted', () => {
component.files = <any> [
{ status: FileUploadStatus.Aborted },
{ status: FileUploadStatus.Aborted }
];
@@ -202,53 +280,10 @@ describe('FileUploadingListComponent', () => {
component.files = <any> [
{ status: FileUploadStatus.Cancelled },
{ status: FileUploadStatus.Cancelled },
{ status: FileUploadStatus.Error }
{ status: FileUploadStatus.Aborted }
];
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);
});
});
});

View File

@@ -17,7 +17,7 @@
import { Component, ContentChild, Input, TemplateRef } from '@angular/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({
selector: 'adf-file-uploading-list, alfresco-file-uploading-list',
@@ -35,7 +35,6 @@ export class FileUploadingListComponent {
files: FileModel[] = [];
constructor(
private fileUploadService: FileUploadService,
private uploadService: UploadService,
private nodesApi: NodesApiService,
private notificationService: NotificationService,
@@ -49,41 +48,43 @@ export class FileUploadingListComponent {
*
* @memberOf FileUploadingListComponent
*/
cancelFileUpload(file: FileModel): void {
cancelFile(file: FileModel): void {
this.uploadService.cancelUpload(file);
}
removeFile(file: FileModel): void {
const { id } = file.data.entry;
this.nodesApi
.deleteNode(id, { permanent: true })
.subscribe(
() => this.onRemoveSuccess(file),
() => this.onRemoveFail(file)
);
this.deleteNode(file)
.subscribe(() => {
if ( file.status === FileUploadStatus.Error) {
this.notifyError(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 {
if (event) {
event.preventDefault();
}
cancelAllFiles(): void {
this.getUploadingFiles()
.forEach((file) => this.uploadService.cancelUpload(file));
this.files.forEach((file) => {
const { status } = file;
const { Complete, Progress, Pending } = FileUploadStatus;
const deletedFiles = this.files
.filter((file) => file.status === FileUploadStatus.Complete)
.map((file) => this.deleteNode(file));
if (status === Complete) {
this.removeFile(file);
}
Observable.forkJoin(...deletedFiles)
.subscribe((files: FileModel[]) => {
const errors = files
.filter((file) => file.status === FileUploadStatus.Error);
if (status === Progress || status === Pending) {
this.cancelFileUpload(file);
}
if (errors.length) {
this.notifyError(...errors);
}
});
this.uploadService.cancelUpload(...files);
});
}
/**
@@ -92,7 +93,7 @@ export class FileUploadingListComponent {
*/
isUploadCompleted(): boolean {
return !this.isUploadCancelled() &&
!!this.files.length &&
Boolean(this.files.length) &&
!this.files
.some(({status}) =>
status === FileUploadStatus.Starting ||
@@ -111,38 +112,55 @@ export class FileUploadingListComponent {
.every(({status}) =>
status === FileUploadStatus.Aborted ||
status === FileUploadStatus.Cancelled ||
status === FileUploadStatus.Error
status === FileUploadStatus.Deleted
);
}
/**
* Gets all the files with status Error.
* @returns {boolean} - false if there is none
*/
get uploadErrorFiles(): FileModel[] {
return this.files.filter(({status}) => status === FileUploadStatus.Error);
}
private deleteNode(file: FileModel): Observable<FileModel> {
const { id } = file.data.entry;
/**
* Gets all the files with status Cancelled.
* @returns {boolean} - false if there is none
*/
get uploadCancelledFiles(): FileModel[] {
return this.files.filter(({status}) => status === FileUploadStatus.Cancelled);
}
private onRemoveSuccess(file: FileModel): void {
const { uploadService, fileUploadService } = this;
uploadService.cancelUpload(file);
fileUploadService.emitFileRemoved(file);
}
private onRemoveFail(file: FileModel): void {
this.translateService
.get('FILE_UPLOAD.MESSAGES.REMOVE_FILE_ERROR', { fileName: file.name})
.subscribe((message) => {
this.notificationService.openSnackMessage(message, 4000);
return this.nodesApi
.deleteNode(id, { permanent: true })
.map(() => {
file.status = FileUploadStatus.Deleted;
return file;
})
.catch((error) => {
file.status = FileUploadStatus.Error;
return Observable.of(file);
});
}
private notifyError(...files: FileModel[]) {
let translateSubscription = null;
if (files.length === 1) {
translateSubscription = this.translateService
.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 }
);
}
translateSubscription
.subscribe(message => this.notificationService.openSnackMessage(message, 4000));
}
private getUploadingFiles() {
return this.files.filter((item) => {
if (
item.status === FileUploadStatus.Pending ||
item.status === FileUploadStatus.Progress ||
item.status === FileUploadStatus.Starting
) {
return item;
}
});
}
}

View File

@@ -26,7 +26,8 @@
"PROGRESS": "Upload in progress...",
"FOLDER_ALREADY_EXIST": "The folder {0} already exist",
"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": {
"UNDO": "Undo"

View File

@@ -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);
});
});
});

View 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);
}
}