Group files by project in upload overlay

This commit is contained in:
Adina Țeudan 2021-01-18 22:20:58 +02:00
parent 7a4743aa66
commit f96c35649b
10 changed files with 102 additions and 45 deletions

View File

@ -5,6 +5,10 @@
flex-direction: row;
position: relative;
.heading-xl {
max-width: 88%;
}
redaction-circle-button {
position: absolute;
top: -5px;

View File

@ -225,7 +225,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
@HostListener('drop', ['$event'])
onDrop(event: DragEvent) {
handleFileDrop(event, this.appStateService.activeProjectId, this._uploadFiles.bind(this));
handleFileDrop(event, this.appStateService.activeProject, this._uploadFiles.bind(this));
}
@HostListener('dragover', ['$event'])
@ -235,7 +235,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
}
async uploadFiles(files: File[] | FileList) {
await this._uploadFiles(convertFiles(files, this.appStateService.activeProjectId));
await this._uploadFiles(convertFiles(files, this.appStateService.activeProject));
}
private async _uploadFiles(files: FileUploadModel[]) {

View File

@ -35,7 +35,7 @@ export class FileDropComponent implements OnInit {
@HostListener('drop', ['$event'])
onDrop(event: DragEvent) {
handleFileDrop(event, this._appStateService.activeProjectId, this.uploadFiles.bind(this));
handleFileDrop(event, this._appStateService.activeProject, this.uploadFiles.bind(this));
}
@HostListener('dragover', ['$event'])

View File

@ -14,6 +14,7 @@ import { DialogService } from '../dialogs/dialog.service';
export class FileUploadService {
static readonly MAX_PARALLEL_UPLOADS = 5;
files: FileUploadModel[] = [];
groupedFiles: { [key: string]: FileUploadModel[] } = {};
activeUploads = 0;
private _pendingUploads: FileUploadModel[] = [];
@ -73,13 +74,39 @@ export class FileUploadService {
}
this.files.push(...files);
files.forEach((newFile) => {
this._addFileToGroup(newFile);
this.scheduleUpload(newFile);
});
return files.length;
}
private _addFileToGroup(file: FileUploadModel) {
if (!this.groupedFiles[file.projectId]) {
this.groupedFiles[file.projectId] = [];
}
this.groupedFiles[file.projectId].push(file);
}
public filterFiles() {
for (const file of this.files) {
if (file.completed && !file.error) {
this.removeFile(file);
}
}
}
private _removeFileFromGroup(file: FileUploadModel) {
const index = this.groupedFiles[file.projectId].indexOf(file);
this.groupedFiles[file.projectId].splice(index, 1);
}
public get activeProjectKeys() {
return Object.keys(this.groupedFiles).filter((projectId) => this.groupedFiles[projectId].length > 0);
}
stopAllUploads() {
this.files = [];
this.groupedFiles = {};
}
private _handleUploads() {
@ -143,6 +170,7 @@ export class FileUploadService {
removeFile(item: FileUploadModel) {
const index = this.files.indexOf(item);
if (index > -1) {
this._removeFileFromGroup(item);
this.files.splice(index, 1);
}
}

View File

@ -7,4 +7,5 @@ export interface FileUploadModel {
size: number;
sizeError: any;
projectId?: string;
projectName?: string;
}

View File

@ -15,46 +15,53 @@
</div>
<div [hidden]="collapsed">
<div class="upload-download-list">
<div *ngFor="let model of uploadService.files" class="upload-download-list-item">
<div class="upload-download-line">
<div matTooltipPosition="above" [matTooltip]="model.file?.name" class="upload-download-item-name">
{{ model.file?.name }}
</div>
<div *ngIf="!model.completed && model.progress < 100" class="upload-download-progress">{{ model.progress }}%</div>
<div *ngIf="model.completed && model.error" class="upload-download-progress error">
<mat-icon svgIcon="red:error"></mat-icon>
</div>
<div *ngIf="model.completed && !model.error" class="upload-download-progress ok">
<mat-icon svgIcon="red:check"></mat-icon>
</div>
<div *ngFor="let projectId of uploadService.activeProjectKeys">
<div class="project-name-wrapper">
<span class="project-name">{{ uploadService.groupedFiles[projectId][0].projectName }}</span> ({{
uploadService.groupedFiles[projectId].length
}})
</div>
<div *ngIf="model.completed && model.error" class="upload-download-line">
<div matTooltipPosition="above" [matTooltip]="model.error.message" class="upload-download-item-name error">
{{ model.error.message }}
<div *ngFor="let model of uploadService.groupedFiles[projectId]" class="upload-download-list-item">
<div class="upload-download-line">
<div matTooltipPosition="above" [matTooltip]="model.file?.name" class="upload-download-item-name">
{{ model.file?.name }}
</div>
<div *ngIf="!model.completed && model.progress < 100" class="upload-download-progress">{{ model.progress }}%</div>
<div *ngIf="model.completed && model.error" class="upload-download-progress error">
<mat-icon svgIcon="red:error"></mat-icon>
</div>
<div *ngIf="model.completed && !model.error" class="upload-download-progress ok">
<mat-icon svgIcon="red:check"></mat-icon>
</div>
</div>
<div *ngIf="model.completed && model.error" class="upload-download-line">
<div matTooltipPosition="above" [matTooltip]="model.error.message" class="upload-download-item-name error">
{{ model.error.message }}
</div>
<div class="upload-download-progress">
<div
*ngIf="!model.sizeError"
(click)="uploadItem(model)"
[matTooltip]="'upload-status.dialog.actions.re-upload' | translate"
matTooltipPosition="above"
class="error-action pointer"
>
<mat-icon svgIcon="red:refresh"></mat-icon>
</div>
<div
(click)="cancelItem(model)"
[matTooltip]="'upload-status.dialog.actions.cancel' | translate"
matTooltipPosition="above"
class="error-action pointer"
>
<mat-icon svgIcon="red:close"></mat-icon>
<div class="upload-download-progress">
<div
*ngIf="!model.sizeError"
(click)="uploadItem(model)"
[matTooltip]="'upload-status.dialog.actions.re-upload' | translate"
matTooltipPosition="above"
class="error-action pointer"
>
<mat-icon svgIcon="red:refresh"></mat-icon>
</div>
<div
(click)="cancelItem(model)"
[matTooltip]="'upload-status.dialog.actions.cancel' | translate"
matTooltipPosition="above"
class="error-action pointer"
>
<mat-icon svgIcon="red:close"></mat-icon>
</div>
</div>
</div>
</div>
<div *ngIf="!model.completed" class="upload-download-progress" mat-line>
<mat-progress-bar *ngIf="model.progress !== 100" [value]="model.progress" color="primary" mode="determinate"></mat-progress-bar>
<div *ngIf="!model.completed" class="upload-download-progress" mat-line>
<mat-progress-bar *ngIf="model.progress !== 100" [value]="model.progress" color="primary" mode="determinate"></mat-progress-bar>
</div>
</div>
</div>
</div>

View File

@ -1,3 +1,17 @@
@import '../../../assets/styles/red-variables';
@import '../../../assets/styles/red-mixins';
.red-upload-download-overlay {
right: 10px;
}
.project-name-wrapper {
display: flex;
padding: 8px 20px 8px 8px;
background-color: $grey-4;
font-weight: 600;
.project-name {
@include line-clamp(1);
}
}

View File

@ -22,7 +22,7 @@ export class UploadStatusOverlay implements OnInit {
ngOnInit() {
this.uploadStatusInterval = setInterval(() => {
// keep only errors
this.uploadService.files = this.uploadService.files.filter((file) => !file.completed || file.error);
this.uploadService.filterFiles();
if (this.uploadService.files.length === 0) {
this.closeDialog();
}

View File

@ -1,6 +1,7 @@
import { FileUploadModel } from '../upload-download/model/file-upload.model';
import { ProjectWrapper } from '../state/model/project.wrapper';
export function handleFileDrop(event: DragEvent, projectId: string, uploadFiles: (files: FileUploadModel[]) => void) {
export function handleFileDrop(event: DragEvent, project: ProjectWrapper, uploadFiles: (files: FileUploadModel[]) => void) {
event.preventDefault();
event.stopPropagation();
const { dataTransfer } = event;
@ -16,16 +17,16 @@ export function handleFileDrop(event: DragEvent, projectId: string, uploadFiles:
} else {
files = dataTransfer.files;
dataTransfer.clearData();
uploadFiles(convertFiles(files, projectId));
uploadFiles(convertFiles(files, project));
}
const convertedFiles = convertFiles(files, projectId);
const convertedFiles = convertFiles(files, project);
if (convertedFiles.length > 0) {
uploadFiles(convertedFiles);
}
}
export function convertFiles(files: FileList | File[], projectId: string): FileUploadModel[] {
export function convertFiles(files: FileList | File[], project: ProjectWrapper): FileUploadModel[] {
let uploadFiles: FileUploadModel[] = [];
for (let i = 0; i < files.length; i++) {
const file = files[i];
@ -34,7 +35,8 @@ export function convertFiles(files: FileList | File[], projectId: string): FileU
progress: 0,
completed: false,
error: null,
projectId: projectId,
projectId: project.projectId,
projectName: project.projectName,
sizeError: false,
retryCount: 0,
size: file.size

View File

@ -1,4 +1,4 @@
import { AfterContentChecked, AfterContentInit, AfterViewChecked, Directive, ElementRef, HostBinding } from '@angular/core';
import { AfterContentChecked, Directive, ElementRef, HostBinding } from '@angular/core';
@Directive({
selector: '[redactionHasScrollbar]',
@ -8,6 +8,7 @@ export class HasScrollbarDirective implements AfterContentChecked {
constructor(private el: ElementRef) {}
@HostBinding('class') class = '';
ngAfterContentChecked() {
this.class = this.hasScrollbar ? 'has-scrollbar' : '';
}