Group files by project in upload overlay
This commit is contained in:
parent
7a4743aa66
commit
f96c35649b
@ -5,6 +5,10 @@
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
|
||||
.heading-xl {
|
||||
max-width: 88%;
|
||||
}
|
||||
|
||||
redaction-circle-button {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
|
||||
@ -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[]) {
|
||||
|
||||
@ -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'])
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,4 +7,5 @@ export interface FileUploadModel {
|
||||
size: number;
|
||||
sizeError: any;
|
||||
projectId?: string;
|
||||
projectName?: string;
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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' : '';
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user