diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index 5a8d2ae4e..f369554ac 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -37,7 +37,7 @@ import { ToastrModule } from 'ngx-toastr'; import { ServiceWorkerModule } from '@angular/service-worker'; import { environment } from '../environments/environment'; import { AuthModule } from './auth/auth.module'; -import { FileUploadModule } from './upload/file-upload.module'; +import { FileUploadDownloadModule } from './upload-download/file-upload-download.module'; import { FullPageLoadingIndicatorComponent } from './utils/full-page-loading-indicator/full-page-loading-indicator.component'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { InitialsAvatarComponent } from './common/initials-avatar/initials-avatar.component'; @@ -357,7 +357,7 @@ const matImports = [ enableHtml: true, toastComponent: ToastComponent }), - FileUploadModule, + FileUploadDownloadModule, ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }), ColorPickerModule, AceEditorModule diff --git a/apps/red-ui/src/app/screens/project-overview-screen/bulk-actions/bulk-actions.component.ts b/apps/red-ui/src/app/screens/project-overview-screen/bulk-actions/bulk-actions.component.ts index abe5715c9..e7e071d4e 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/bulk-actions/bulk-actions.component.ts +++ b/apps/red-ui/src/app/screens/project-overview-screen/bulk-actions/bulk-actions.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; import { AppStateService } from '../../../state/app-state.service'; import { UserService } from '../../../user/user.service'; import { FileManagementControllerService, ReanalysisControllerService } from '@redaction/red-ui-http'; @@ -6,9 +6,9 @@ import { DialogService } from '../../../dialogs/dialog.service'; import { PermissionsService } from '../../../common/service/permissions.service'; import { FileStatusWrapper } from '../../file/model/file-status.wrapper'; import { FileActionService } from '../../file/service/file-action.service'; -import { download } from '../../../utils/file-download-utils'; -import { computerize } from '../../../utils/functions'; import { Observable } from 'rxjs'; +import { StatusOverlayService } from '../../../upload-download/status-overlay.service'; +import { FileDownloadService } from '../../../upload-download/file-download.service'; @Component({ selector: 'redaction-bulk-actions', @@ -27,7 +27,10 @@ export class BulkActionsComponent { private readonly _fileManagementControllerService: FileManagementControllerService, private readonly _reanalysisControllerService: ReanalysisControllerService, private readonly _permissionsService: PermissionsService, - private readonly _fileActionService: FileActionService + private readonly _fileActionService: FileActionService, + private readonly _statusOverlayService: StatusOverlayService, + private readonly _changeDetectorRef: ChangeDetectorRef, + private readonly _fileDownloadService: FileDownloadService ) {} private get selectedFiles(): FileStatusWrapper[] { @@ -118,15 +121,12 @@ export class BulkActionsComponent { // Bulk Download downloadRedactedFiles() { - this.loading = true; - this._fileManagementControllerService - .downloadRedactedFiles({ fileIds: this.selectedFiles.map((file) => file.fileId) }, this._appStateService.activeProjectId, false, 'response') - .subscribe((data) => { - download(data, 'redacted_files_' + computerize(this._appStateService.activeProject.name) + '.zip'); - }) - .add(() => { - this.loading = false; - }); + this._fileDownloadService.downloadProjectFiles( + this.selectedFiles.map((file) => file.fileId), + this._appStateService.activeProject + ); + this._statusOverlayService.openDownloadStatusOverlay(); + this._changeDetectorRef.detectChanges(); } private _performBulkAction(obs: Observable) { diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts index f1b888fac..0e9108b96 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts @@ -2,10 +2,10 @@ import { ChangeDetectorRef, Component, HostListener, OnDestroy, OnInit, ViewChil import { ActivatedRoute, Router } from '@angular/router'; import { NotificationService, NotificationType } from '../../notification/notification.service'; import { AppStateService } from '../../state/app-state.service'; -import { FileDropOverlayService } from '../../upload/file-drop/service/file-drop-overlay.service'; -import { FileUploadModel } from '../../upload/model/file-upload.model'; -import { FileUploadService } from '../../upload/file-upload.service'; -import { UploadStatusOverlayService } from '../../upload/upload-status-dialog/service/upload-status-overlay.service'; +import { FileDropOverlayService } from '../../upload-download/file-drop/service/file-drop-overlay.service'; +import { FileUploadModel } from '../../upload-download/model/file-upload.model'; +import { FileUploadService } from '../../upload-download/file-upload.service'; +import { StatusOverlayService } from '../../upload-download/status-overlay.service'; import { computerize, humanize } from '../../utils/functions'; import { DialogService } from '../../dialogs/dialog.service'; import { TranslateService } from '@ngx-translate/core'; @@ -28,6 +28,7 @@ import { debounce } from '../../utils/debounce'; import { download } from '../../utils/file-download-utils'; import { convertFiles, handleFileDrop } from '../../utils/file-drop-utils'; import { FilterComponent } from '../../common/filter/filter.component'; +import { FileDownloadService } from '../../upload-download/file-download.service'; @Component({ selector: 'redaction-project-overview-screen', @@ -67,7 +68,8 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { private readonly _dialogService: DialogService, private readonly _fileActionService: FileActionService, private readonly _fileUploadService: FileUploadService, - private readonly _uploadStatusOverlayService: UploadStatusOverlayService, + private readonly _fileDownloadService: FileDownloadService, + private readonly _statusOverlayService: StatusOverlayService, private readonly _router: Router, private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _translateService: TranslateService, @@ -239,7 +241,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { private async _uploadFiles(files: FileUploadModel[]) { const fileCount = await this._fileUploadService.uploadFiles(files); if (fileCount) { - this._uploadStatusOverlayService.openStatusOverlay(); + this._statusOverlayService.openUploadStatusOverlay(); } this._changeDetectorRef.detectChanges(); } @@ -379,16 +381,12 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { // Download Files public downloadRedactedFiles() { - this._fileManagementControllerService - .downloadRedactedFiles( - { fileIds: this.appStateService.activeProject.files.map((file) => file.fileId) }, - this.appStateService.activeProjectId, - false, - 'response' - ) - .subscribe((data) => { - download(data, 'redacted_files_' + computerize(this.appStateService.activeProject.name) + '.zip'); - }); + this._fileDownloadService.downloadProjectFiles( + this.appStateService.activeProject.files.map((file) => file.fileId), + this.appStateService.activeProject + ); + this._statusOverlayService.openDownloadStatusOverlay(); + this._changeDetectorRef.detectChanges(); } public get canDownloadRedactedFiles() { diff --git a/apps/red-ui/src/app/upload-download/download-status-overlay/download-status-overlay.component.html b/apps/red-ui/src/app/upload-download/download-status-overlay/download-status-overlay.component.html new file mode 100644 index 000000000..5fb91a664 --- /dev/null +++ b/apps/red-ui/src/app/upload-download/download-status-overlay/download-status-overlay.component.html @@ -0,0 +1,62 @@ +
+
+
+ {{ 'download-status.dialog.title' | translate: { len: downloadService.downloads.length } }} +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+ {{ model.project.name }} +
+ +
+ +
+
+ +
+
+
+
+ {{ model.error.message }} +
+ +
+
+ +
+
+ +
+
+
+
+
+
+
diff --git a/apps/red-ui/src/app/upload-download/download-status-overlay/download-status-overlay.component.scss b/apps/red-ui/src/app/upload-download/download-status-overlay/download-status-overlay.component.scss new file mode 100644 index 000000000..d587e0315 --- /dev/null +++ b/apps/red-ui/src/app/upload-download/download-status-overlay/download-status-overlay.component.scss @@ -0,0 +1,3 @@ +.red-upload-download-overlay { + left: 10px; +} diff --git a/apps/red-ui/src/app/upload-download/download-status-overlay/download-status-overlay.component.ts b/apps/red-ui/src/app/upload-download/download-status-overlay/download-status-overlay.component.ts new file mode 100644 index 000000000..25bfdf7f6 --- /dev/null +++ b/apps/red-ui/src/app/upload-download/download-status-overlay/download-status-overlay.component.ts @@ -0,0 +1,49 @@ +import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { OverlayRef } from '@angular/cdk/overlay'; +import { FileDownloadService } from '../file-download.service'; +import { ProjectDownloadModel } from '../model/project-download.model'; + +@Component({ + selector: 'redaction-download-status-overlay', + templateUrl: './download-status-overlay.component.html', + styleUrls: ['./download-status-overlay.component.scss'] +}) +export class DownloadStatusOverlay implements OnInit { + collapsed = false; + + downloadStatusInterval: number; + + constructor( + public readonly downloadService: FileDownloadService, + private readonly _overlayRef: OverlayRef, + private readonly _changeDetectorRef: ChangeDetectorRef + ) {} + + ngOnInit() { + this.downloadStatusInterval = setInterval(() => { + // keep only errors + this.downloadService.downloads = this.downloadService.downloads.filter((projectDownload) => !projectDownload.completed || projectDownload.error); + if (this.downloadService.downloads.length === 0) { + this.closeDialog(); + } + }, 2500); + } + + cancelItem(item: ProjectDownloadModel) { + this.downloadService.removeProject(item); + } + + downloadItem(item: ProjectDownloadModel) { + this.downloadService.scheduleDownload(item); + this._changeDetectorRef.detectChanges(); + } + + closeDialog() { + if (this.downloadStatusInterval) { + clearInterval(this.downloadStatusInterval); + this.downloadStatusInterval = null; + } + this.downloadService.stopAllDownloads(); + this._overlayRef.detach(); + } +} diff --git a/apps/red-ui/src/app/upload-download/file-download.service.ts b/apps/red-ui/src/app/upload-download/file-download.service.ts new file mode 100644 index 000000000..720d70131 --- /dev/null +++ b/apps/red-ui/src/app/upload-download/file-download.service.ts @@ -0,0 +1,113 @@ +import { ApplicationRef, Injectable } from '@angular/core'; +import { AppStateService } from '../state/app-state.service'; +import { FileManagementControllerService } from '@redaction/red-ui-http'; +import { interval, Subscription } from 'rxjs'; +import { AppConfigService } from '../app-config/app-config.service'; +import { TranslateService } from '@ngx-translate/core'; +import { DialogService } from '../dialogs/dialog.service'; +import { download } from '../utils/file-download-utils'; +import { computerize } from '../utils/functions'; +import { ProjectDownloadModel } from './model/project-download.model'; +import { ProjectWrapper } from '../state/model/project.wrapper'; + +@Injectable({ + providedIn: 'root' +}) +export class FileDownloadService { + static readonly MAX_PARALLEL_DOWNLOADS = 2; + + activeDownloadsCnt = 0; + + public downloads: ProjectDownloadModel[] = []; + private _pendingDownloads: ProjectDownloadModel[] = []; + private _activeDownloads: Subscription[] = []; + + constructor( + private readonly _applicationRef: ApplicationRef, + private readonly _translateService: TranslateService, + private readonly _appConfigService: AppConfigService, + private readonly _fileManagementControllerService: FileManagementControllerService, + private readonly _dialogService: DialogService + ) { + interval(2500).subscribe((val) => { + this._handleDownloads(); + }); + } + + public downloadProjectFiles(fileIds: string[], project: ProjectWrapper): void { + const item = { project, fileIds, completed: false, error: null }; + this.downloads.push(item); + this.scheduleDownload(item); + } + + stopAllDownloads() { + this.downloads = []; + } + + public scheduleDownload(item: ProjectDownloadModel) { + item.completed = false; + item.error = null; + this._pendingDownloads.push(item); + } + + private _handleDownloads() { + if (this._activeDownloads.length < FileDownloadService.MAX_PARALLEL_DOWNLOADS && this._pendingDownloads.length > 0) { + let cnt = FileDownloadService.MAX_PARALLEL_DOWNLOADS - this._activeDownloads.length; + while (cnt > 0) { + cnt--; + const popped = this._pendingDownloads.shift(); + if (popped) { + const sub = this._createSubscription(popped); + this._activeDownloads.push(sub); + } else { + return; + } + } + } + } + + private _createSubscription(downloadModel: ProjectDownloadModel) { + this.activeDownloadsCnt++; + const obs = this._fileManagementControllerService.downloadRedactedFiles( + { fileIds: downloadModel.fileIds }, + downloadModel.project.projectId, + false, + 'response' + ); + const subscription = obs.subscribe( + async (event) => { + if (event.status < 300) { + downloadModel.completed = true; + if (this.downloads.indexOf(downloadModel) !== -1) { + download(event, 'redacted_files_' + computerize(downloadModel.project.name) + '.zip'); + } + } else { + downloadModel.completed = true; + downloadModel.error = { message: this._translateService.instant('download-status.error.generic') }; + } + this._removeDownload(subscription); + }, + () => { + downloadModel.completed = true; + downloadModel.error = { message: this._translateService.instant('upload-status.error.generic') }; + this._removeDownload(subscription); + } + ); + return subscription; + } + + private _removeDownload(subscription: Subscription) { + const index = this._activeDownloads.indexOf(subscription); + if (index > -1) { + this._activeDownloads.splice(index, 1); + this.activeDownloadsCnt--; + } + } + + removeProject(item: ProjectDownloadModel) { + const index = this.downloads.indexOf(item); + if (index > -1) { + this.downloads.splice(index, 1); + } + } +} diff --git a/apps/red-ui/src/app/upload/file-drop/file-drop.component.html b/apps/red-ui/src/app/upload-download/file-drop/file-drop.component.html similarity index 100% rename from apps/red-ui/src/app/upload/file-drop/file-drop.component.html rename to apps/red-ui/src/app/upload-download/file-drop/file-drop.component.html diff --git a/apps/red-ui/src/app/upload/file-drop/file-drop.component.scss b/apps/red-ui/src/app/upload-download/file-drop/file-drop.component.scss similarity index 100% rename from apps/red-ui/src/app/upload/file-drop/file-drop.component.scss rename to apps/red-ui/src/app/upload-download/file-drop/file-drop.component.scss diff --git a/apps/red-ui/src/app/upload/file-drop/file-drop.component.ts b/apps/red-ui/src/app/upload-download/file-drop/file-drop.component.ts similarity index 86% rename from apps/red-ui/src/app/upload/file-drop/file-drop.component.ts rename to apps/red-ui/src/app/upload-download/file-drop/file-drop.component.ts index 663dbe54b..fc2fa014e 100644 --- a/apps/red-ui/src/app/upload/file-drop/file-drop.component.ts +++ b/apps/red-ui/src/app/upload-download/file-drop/file-drop.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, HostListener, OnInit } from '@angular/cor import { FileUploadService } from '../file-upload.service'; import { FileUploadModel } from '../model/file-upload.model'; import { OverlayRef } from '@angular/cdk/overlay'; -import { UploadStatusOverlayService } from '../upload-status-dialog/service/upload-status-overlay.service'; +import { StatusOverlayService } from '../status-overlay.service'; import { handleFileDrop } from '../../utils/file-drop-utils'; import { AppStateService } from '../../state/app-state.service'; @@ -17,7 +17,7 @@ export class FileDropComponent implements OnInit { private readonly _fileUploadService: FileUploadService, private readonly _appStateService: AppStateService, private readonly _changeDetectorRef: ChangeDetectorRef, - private readonly _uploadStatusOverlayService: UploadStatusOverlayService + private readonly _statusOverlayService: StatusOverlayService ) {} ngOnInit() {} @@ -47,7 +47,7 @@ export class FileDropComponent implements OnInit { async uploadFiles(files: FileUploadModel[]) { const fileCount = await this._fileUploadService.uploadFiles(files); if (fileCount) { - this._uploadStatusOverlayService.openStatusOverlay(); + this._statusOverlayService.openUploadStatusOverlay(); } this._dialogRef.detach(); this._changeDetectorRef.detectChanges(); diff --git a/apps/red-ui/src/app/upload/file-drop/service/file-drop-overlay.service.ts b/apps/red-ui/src/app/upload-download/file-drop/service/file-drop-overlay.service.ts similarity index 100% rename from apps/red-ui/src/app/upload/file-drop/service/file-drop-overlay.service.ts rename to apps/red-ui/src/app/upload-download/file-drop/service/file-drop-overlay.service.ts diff --git a/apps/red-ui/src/app/upload/file-upload.module.ts b/apps/red-ui/src/app/upload-download/file-upload-download.module.ts similarity index 60% rename from apps/red-ui/src/app/upload/file-upload.module.ts rename to apps/red-ui/src/app/upload-download/file-upload-download.module.ts index e3caf9215..c435098a2 100644 --- a/apps/red-ui/src/app/upload/file-upload.module.ts +++ b/apps/red-ui/src/app/upload-download/file-upload-download.module.ts @@ -6,10 +6,12 @@ import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatTooltipModule } from '@angular/material/tooltip'; import { FileDropComponent } from './file-drop/file-drop.component'; import { OverlayModule } from '@angular/cdk/overlay'; -import { UploadStatusOverlay } from './upload-status-dialog/upload-status-overlay.component'; +import { UploadStatusOverlay } from './upload-status-overlay/upload-status-overlay.component'; import { NgxDropzoneModule } from 'ngx-dropzone'; import { TranslateModule } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; +import { DownloadStatusOverlay } from './download-status-overlay/download-status-overlay.component'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; @NgModule({ imports: [ @@ -20,12 +22,13 @@ import { MatButtonModule } from '@angular/material/button'; MatListModule, NgxDropzoneModule, MatProgressBarModule, + MatProgressSpinnerModule, OverlayModule, MatButtonModule ], - declarations: [FileDropComponent, UploadStatusOverlay], + declarations: [FileDropComponent, UploadStatusOverlay, DownloadStatusOverlay], providers: [], - entryComponents: [FileDropComponent, UploadStatusOverlay], - exports: [FileDropComponent, UploadStatusOverlay] + entryComponents: [FileDropComponent, UploadStatusOverlay, DownloadStatusOverlay], + exports: [FileDropComponent, UploadStatusOverlay, DownloadStatusOverlay] }) -export class FileUploadModule {} +export class FileUploadDownloadModule {} diff --git a/apps/red-ui/src/app/upload/file-upload.service.ts b/apps/red-ui/src/app/upload-download/file-upload.service.ts similarity index 100% rename from apps/red-ui/src/app/upload/file-upload.service.ts rename to apps/red-ui/src/app/upload-download/file-upload.service.ts diff --git a/apps/red-ui/src/app/upload/model/file-upload.model.ts b/apps/red-ui/src/app/upload-download/model/file-upload.model.ts similarity index 100% rename from apps/red-ui/src/app/upload/model/file-upload.model.ts rename to apps/red-ui/src/app/upload-download/model/file-upload.model.ts diff --git a/apps/red-ui/src/app/upload-download/model/project-download.model.ts b/apps/red-ui/src/app/upload-download/model/project-download.model.ts new file mode 100644 index 000000000..245a6f98a --- /dev/null +++ b/apps/red-ui/src/app/upload-download/model/project-download.model.ts @@ -0,0 +1,8 @@ +import { ProjectWrapper } from '../../state/model/project.wrapper'; + +export interface ProjectDownloadModel { + fileIds: string[]; + project: ProjectWrapper; + completed: boolean; + error: any; +} diff --git a/apps/red-ui/src/app/upload-download/status-overlay.service.ts b/apps/red-ui/src/app/upload-download/status-overlay.service.ts new file mode 100644 index 000000000..62614d679 --- /dev/null +++ b/apps/red-ui/src/app/upload-download/status-overlay.service.ts @@ -0,0 +1,46 @@ +import { Injectable, Injector } from '@angular/core'; +import { Overlay, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; +import { UploadStatusOverlay } from './upload-status-overlay/upload-status-overlay.component'; +import { DownloadStatusOverlay } from './download-status-overlay/download-status-overlay.component'; + +@Injectable({ + providedIn: 'root' +}) +export class StatusOverlayService { + private readonly _uploadStatusOverlayRef: OverlayRef; + private readonly _downloadStatusOverlayRef: OverlayRef; + + constructor(private overlay: Overlay, private readonly _injector: Injector) { + this._uploadStatusOverlayRef = this.overlay.create(); + this._downloadStatusOverlayRef = this.overlay.create(); + } + + private _createUploadInjector() { + return Injector.create({ + providers: [{ provide: OverlayRef, useValue: this._uploadStatusOverlayRef }], + parent: this._injector + }); + } + + private _createDownloadInjector() { + return Injector.create({ + providers: [{ provide: OverlayRef, useValue: this._downloadStatusOverlayRef }], + parent: this._injector + }); + } + + openUploadStatusOverlay() { + const component = new ComponentPortal(UploadStatusOverlay, null, this._createUploadInjector()); + if (!this._uploadStatusOverlayRef.hasAttached()) { + this._uploadStatusOverlayRef.attach(component); + } + } + + openDownloadStatusOverlay() { + const component = new ComponentPortal(DownloadStatusOverlay, null, this._createDownloadInjector()); + if (!this._downloadStatusOverlayRef.hasAttached()) { + this._downloadStatusOverlayRef.attach(component); + } + } +} diff --git a/apps/red-ui/src/app/upload/upload-status-dialog/upload-status-overlay.component.html b/apps/red-ui/src/app/upload-download/upload-status-overlay/upload-status-overlay.component.html similarity index 75% rename from apps/red-ui/src/app/upload/upload-status-dialog/upload-status-overlay.component.html rename to apps/red-ui/src/app/upload-download/upload-status-overlay/upload-status-overlay.component.html index 93bc210c0..978d0f377 100644 --- a/apps/red-ui/src/app/upload/upload-status-dialog/upload-status-overlay.component.html +++ b/apps/red-ui/src/app/upload-download/upload-status-overlay/upload-status-overlay.component.html @@ -1,8 +1,6 @@ -
-
-
- {{ 'upload-status.dialog.title' | translate: { p1: uploadService.files.length } }} -
+
+
+
@@ -14,26 +12,26 @@
-
-
-
-
+
+
+
+
{{ model.file?.name }}
-
{{ model.progress }}%
-
+
{{ model.progress }}%
+
-
+
-
-
+
+
{{ model.error.message }}
-
+
-
+
diff --git a/apps/red-ui/src/app/upload-download/upload-status-overlay/upload-status-overlay.component.scss b/apps/red-ui/src/app/upload-download/upload-status-overlay/upload-status-overlay.component.scss new file mode 100644 index 000000000..3f1862cb0 --- /dev/null +++ b/apps/red-ui/src/app/upload-download/upload-status-overlay/upload-status-overlay.component.scss @@ -0,0 +1,3 @@ +.red-upload-download-overlay { + right: 10px; +} diff --git a/apps/red-ui/src/app/upload/upload-status-dialog/upload-status-overlay.component.ts b/apps/red-ui/src/app/upload-download/upload-status-overlay/upload-status-overlay.component.ts similarity index 100% rename from apps/red-ui/src/app/upload/upload-status-dialog/upload-status-overlay.component.ts rename to apps/red-ui/src/app/upload-download/upload-status-overlay/upload-status-overlay.component.ts diff --git a/apps/red-ui/src/app/upload/upload-status-dialog/service/upload-status-overlay.service.ts b/apps/red-ui/src/app/upload/upload-status-dialog/service/upload-status-overlay.service.ts deleted file mode 100644 index fc1234eb0..000000000 --- a/apps/red-ui/src/app/upload/upload-status-dialog/service/upload-status-overlay.service.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Injectable, Injector } from '@angular/core'; -import { Overlay, OverlayRef } from '@angular/cdk/overlay'; -import { ComponentPortal } from '@angular/cdk/portal'; -import { UploadStatusOverlay } from '../upload-status-overlay.component'; - -@Injectable({ - providedIn: 'root' -}) -export class UploadStatusOverlayService { - private readonly _statusOverlayRef: OverlayRef; - - constructor(private overlay: Overlay, private readonly _injector: Injector) { - this._statusOverlayRef = this.overlay.create(); - } - - private _createInjector() { - return Injector.create({ - providers: [{ provide: OverlayRef, useValue: this._statusOverlayRef }], - parent: this._injector - }); - } - - openStatusOverlay() { - const component = new ComponentPortal(UploadStatusOverlay, null, this._createInjector()); - if (!this._statusOverlayRef.hasAttached()) { - this._statusOverlayRef.attach(component); - } - } - - closeStatusOverlay() { - if (this._statusOverlayRef) { - this._statusOverlayRef.detach(); - } - } -} diff --git a/apps/red-ui/src/app/upload/upload-status-dialog/upload-status-overlay.component.scss b/apps/red-ui/src/app/upload/upload-status-dialog/upload-status-overlay.component.scss deleted file mode 100644 index 048600017..000000000 --- a/apps/red-ui/src/app/upload/upload-status-dialog/upload-status-overlay.component.scss +++ /dev/null @@ -1,90 +0,0 @@ -@import '../../../assets/styles/red-variables'; - -section { - background: white; - position: fixed; - bottom: 10px; - right: 10px; - border: 2px solid $grey-1; -} - -.upload-list { - max-height: 216px; - max-width: 400px; - overflow: auto; -} - -.red-upload-header { - display: flex; - flex-direction: row; - align-items: center; - position: relative; - padding: 10px; - background: $grey-1; - color: $white; - width: 380px; - cursor: pointer; - - mat-icon { - color: $white; - } -} - -.collapse-icon { - transform: translate(0, 2px); - - mat-icon { - width: 20px; - } -} - -.close-icon { - position: absolute; - right: 10px; - color: $white; -} - -.upload-list-item { - padding: 8px; - - mat-icon { - width: 16px; - } - - .upload-line { - display: flex !important; - height: 20px; - position: relative; - justify-content: flex-start; - - .upload-file-name { - text-overflow: ellipsis; - overflow: hidden; - display: block; - white-space: nowrap; - padding-right: 50px; - - &.error { - color: $red-1; - padding-right: 60px; - padding-top: 4px; - } - } - - .upload-progress { - position: absolute; - right: 0; - width: 50px; - display: flex; - justify-content: space-evenly; - - &.error { - color: $red-1; - } - - &.ok { - color: $blue-1; - } - } - } -} diff --git a/apps/red-ui/src/app/utils/file-drop-utils.ts b/apps/red-ui/src/app/utils/file-drop-utils.ts index 83395e26e..4bbb28fbe 100644 --- a/apps/red-ui/src/app/utils/file-drop-utils.ts +++ b/apps/red-ui/src/app/utils/file-drop-utils.ts @@ -1,4 +1,4 @@ -import { FileUploadModel } from '../upload/model/file-upload.model'; +import { FileUploadModel } from '../upload-download/model/file-upload.model'; export function handleFileDrop(event: DragEvent, projectId: string, uploadFiles: (files: FileUploadModel[]) => void) { event.preventDefault(); diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 9d183c2d6..c96143e5b 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -11,7 +11,7 @@ "upload-status": { "error": { "file-size": "File to large. Limit is {{size}}MB.", - "generic": "Failed to upload file ... " + "generic": "Failed to upload file... " }, "dialog": { "title": "File Upload", @@ -21,6 +21,19 @@ } } }, + "download-status": { + "error": { + "generic": "Failed to download project." + }, + "dialog": { + "title": "Downloads ({{len}})", + "tooltip": "{{len}} files", + "actions": { + "re-download": "Retry Download", + "cancel": "Cancel Download" + } + } + }, "pdf-viewer": { "text-popup": { "actions": { diff --git a/apps/red-ui/src/assets/styles/red-theme.scss b/apps/red-ui/src/assets/styles/red-theme.scss index 14ead86ed..c07ee1156 100644 --- a/apps/red-ui/src/assets/styles/red-theme.scss +++ b/apps/red-ui/src/assets/styles/red-theme.scss @@ -24,3 +24,4 @@ @import 'red-breadcrumbs'; @import 'red-editor'; @import 'red-slider'; +@import 'red-upload-download-overlay'; diff --git a/apps/red-ui/src/assets/styles/red-upload-download-overlay.scss b/apps/red-ui/src/assets/styles/red-upload-download-overlay.scss new file mode 100644 index 000000000..55f1cdacf --- /dev/null +++ b/apps/red-ui/src/assets/styles/red-upload-download-overlay.scss @@ -0,0 +1,90 @@ +@import 'red-variables'; + +.red-upload-download-overlay { + background: white; + position: fixed; + bottom: 10px; + border: 2px solid $grey-1; + + .upload-download-list { + max-height: 216px; + max-width: 400px; + overflow: auto; + } + + .red-upload-download-header { + display: flex; + flex-direction: row; + align-items: center; + position: relative; + padding: 10px; + background: $grey-1; + color: $white; + width: 380px; + cursor: pointer; + + mat-icon { + color: $white; + } + } + + .collapse-icon { + transform: translate(0, 2px); + + mat-icon { + width: 20px; + } + } + + .close-icon { + position: absolute; + right: 10px; + color: $white; + } + + .upload-download-list-item { + padding: 8px; + + mat-icon { + width: 16px; + } + + .upload-download-line { + display: flex !important; + height: 20px; + position: relative; + justify-content: flex-start; + align-items: center; + + .upload-download-item-name { + text-overflow: ellipsis; + overflow: hidden; + display: block; + white-space: nowrap; + padding-right: 50px; + + &.error { + color: $red-1; + padding-right: 60px; + padding-top: 4px; + } + } + + .upload-download-progress { + position: absolute; + right: 0; + width: 50px; + display: flex; + justify-content: space-evenly; + + &.error { + color: $red-1; + } + + &.ok { + color: $blue-1; + } + } + } + } +}