diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts
index b07e4ce16..5a8d2ae4e 100644
--- a/apps/red-ui/src/app/app.module.ts
+++ b/apps/red-ui/src/app/app.module.ts
@@ -104,6 +104,7 @@ import { RuleSetActionsComponent } from './components/rule-set-actions/rule-set-
import { RuleSetViewSwitchComponent } from './components/rule-set-view-switch/rule-set-view-switch.component';
import { MatSliderModule } from '@angular/material/slider';
import { PendingChangesGuard } from './utils/can-deactivate.guard';
+import { OverwriteFilesDialogComponent } from './dialogs/overwrite-files-dialog/overwrite-files-dialog.component';
export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
@@ -276,6 +277,7 @@ const matImports = [
ProjectOverviewScreenComponent,
AddEditProjectDialogComponent,
ConfirmationDialogComponent,
+ OverwriteFilesDialogComponent,
FilePreviewScreenComponent,
PdfViewerComponent,
AssignOwnerDialogComponent,
diff --git a/apps/red-ui/src/app/dialogs/dialog.service.ts b/apps/red-ui/src/app/dialogs/dialog.service.ts
index 6c39092b4..edf13c942 100644
--- a/apps/red-ui/src/app/dialogs/dialog.service.ts
+++ b/apps/red-ui/src/app/dialogs/dialog.service.ts
@@ -22,6 +22,7 @@ import { ManualAnnotationService } from '../screens/file/service/manual-annotati
import { ProjectWrapper } from '../state/model/project.wrapper';
import { AddEditDictionaryDialogComponent } from '../screens/admin/dictionary-listing-screen/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component';
import { AddEditRuleSetDialogComponent } from '../screens/admin/rule-sets-listing-screen/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component';
+import { OverwriteFilesDialogComponent } from './overwrite-files-dialog/overwrite-files-dialog.component';
const dialogConfig = {
width: '662px',
@@ -331,4 +332,18 @@ export class DialogService {
return ref;
}
+
+ openOverwriteFileDialog(filename: string): Promise<{ option?: 'overwrite' | 'no-overwrite'; remember?: boolean; cancel?: boolean }> {
+ const ref = this._dialog.open(OverwriteFilesDialogComponent, {
+ ...dialogConfig,
+ data: filename
+ });
+
+ return ref
+ .afterClosed()
+ .toPromise()
+ .then((res) => {
+ return res || { cancel: true };
+ });
+ }
}
diff --git a/apps/red-ui/src/app/dialogs/overwrite-files-dialog/overwrite-files-dialog.component.html b/apps/red-ui/src/app/dialogs/overwrite-files-dialog/overwrite-files-dialog.component.html
new file mode 100644
index 000000000..962a5daa9
--- /dev/null
+++ b/apps/red-ui/src/app/dialogs/overwrite-files-dialog/overwrite-files-dialog.component.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ {{ 'overwrite-files-dialog.options.remember' | translate }}
+
+
+
+
+
diff --git a/apps/red-ui/src/app/dialogs/overwrite-files-dialog/overwrite-files-dialog.component.scss b/apps/red-ui/src/app/dialogs/overwrite-files-dialog/overwrite-files-dialog.component.scss
new file mode 100644
index 000000000..d2bca127f
--- /dev/null
+++ b/apps/red-ui/src/app/dialogs/overwrite-files-dialog/overwrite-files-dialog.component.scss
@@ -0,0 +1,7 @@
+mat-checkbox {
+ margin-top: 16px;
+}
+
+.dialog-actions > div:not(:last-child) {
+ margin-right: 32px;
+}
diff --git a/apps/red-ui/src/app/dialogs/overwrite-files-dialog/overwrite-files-dialog.component.ts b/apps/red-ui/src/app/dialogs/overwrite-files-dialog/overwrite-files-dialog.component.ts
new file mode 100644
index 000000000..0f0d93a51
--- /dev/null
+++ b/apps/red-ui/src/app/dialogs/overwrite-files-dialog/overwrite-files-dialog.component.ts
@@ -0,0 +1,28 @@
+import { Component, Inject, OnInit } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+
+@Component({
+ selector: 'redaction-overwrite-files-dialog',
+ templateUrl: './overwrite-files-dialog.component.html',
+ styleUrls: ['./overwrite-files-dialog.component.scss']
+})
+export class OverwriteFilesDialogComponent implements OnInit {
+ public remember = false;
+
+ constructor(
+ private readonly _translateService: TranslateService,
+ public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public filename: string
+ ) {}
+
+ ngOnInit(): void {}
+
+ cancel() {
+ this.dialogRef.close();
+ }
+
+ selectOption(option: 'overwrite' | 'no-overwrite') {
+ this.dialogRef.close({ option, remember: this.remember });
+ }
+}
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 1df08a03c..bf8d37810 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
@@ -212,13 +212,15 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
event.preventDefault();
}
- uploadFiles(files: File[] | FileList) {
- this._uploadFiles(convertFiles(files, this.appStateService.activeProjectId));
+ async uploadFiles(files: File[] | FileList) {
+ await this._uploadFiles(convertFiles(files, this.appStateService.activeProjectId));
}
- private _uploadFiles(files: FileUploadModel[]) {
- this._fileUploadService.uploadFiles(files);
- this._uploadStatusOverlayService.openStatusOverlay();
+ private async _uploadFiles(files: FileUploadModel[]) {
+ const fileCount = await this._fileUploadService.uploadFiles(files);
+ if (fileCount) {
+ this._uploadStatusOverlayService.openStatusOverlay();
+ }
this._changeDetectorRef.detectChanges();
}
diff --git a/apps/red-ui/src/app/upload/file-drop/file-drop.component.ts b/apps/red-ui/src/app/upload/file-drop/file-drop.component.ts
index 193f8558a..663dbe54b 100644
--- a/apps/red-ui/src/app/upload/file-drop/file-drop.component.ts
+++ b/apps/red-ui/src/app/upload/file-drop/file-drop.component.ts
@@ -44,9 +44,11 @@ export class FileDropComponent implements OnInit {
event.preventDefault();
}
- uploadFiles(files: FileUploadModel[]) {
- this._fileUploadService.uploadFiles(files);
- this._uploadStatusOverlayService.openStatusOverlay();
+ async uploadFiles(files: FileUploadModel[]) {
+ const fileCount = await this._fileUploadService.uploadFiles(files);
+ if (fileCount) {
+ this._uploadStatusOverlayService.openStatusOverlay();
+ }
this._dialogRef.detach();
this._changeDetectorRef.detectChanges();
}
diff --git a/apps/red-ui/src/app/upload/file-upload.service.ts b/apps/red-ui/src/app/upload/file-upload.service.ts
index d02960f80..dbd377a24 100644
--- a/apps/red-ui/src/app/upload/file-upload.service.ts
+++ b/apps/red-ui/src/app/upload/file-upload.service.ts
@@ -6,6 +6,7 @@ import { FileManagementControllerService } from '@redaction/red-ui-http';
import { interval, Subscription } from 'rxjs';
import { AppConfigKey, AppConfigService } from '../app-config/app-config.service';
import { TranslateService } from '@ngx-translate/core';
+import { DialogService } from '../dialogs/dialog.service';
@Injectable({
providedIn: 'root'
@@ -23,7 +24,8 @@ export class FileUploadService {
private readonly _applicationRef: ApplicationRef,
private readonly _translateService: TranslateService,
private readonly _appConfigService: AppConfigService,
- private readonly _fileManagementControllerService: FileManagementControllerService
+ private readonly _fileManagementControllerService: FileManagementControllerService,
+ private readonly _dialogService: DialogService
) {
interval(2500).subscribe((val) => {
this._handleUploads();
@@ -39,20 +41,41 @@ export class FileUploadService {
}
}
- uploadFiles(files: FileUploadModel[]) {
+ async uploadFiles(files: FileUploadModel[]): Promise {
const maxSizeMB = this._appConfigService.getConfig(AppConfigKey.MAX_FILE_SIZE_MB, 50);
const maxSizeBytes = maxSizeMB * 1024 * 1024;
- files.forEach((file) => {
+ const projectFiles = this._appStateService.activeProject.files;
+ let option: 'overwrite' | 'no-overwrite' | undefined = undefined;
+ for (let idx = 0; idx < files.length; ++idx) {
+ const file = files[idx];
+ let currentOption = option;
+ if (!!projectFiles.find((pf) => pf.filename === file.file.name)) {
+ if (!option) {
+ const res = await this._dialogService.openOverwriteFileDialog(file.file.name);
+ if (res.cancel) {
+ return;
+ }
+ currentOption = res.option;
+ option = res.remember ? currentOption : undefined;
+ }
+
+ if (currentOption === 'no-overwrite') {
+ files.splice(idx, 1);
+ --idx;
+ continue;
+ }
+ }
if (file.size > maxSizeBytes) {
file.completed = true;
file.error = { message: this._translateService.instant('upload-status.error.file-size', { size: maxSizeMB }) };
file.sizeError = true;
}
- });
+ }
this.files.push(...files);
files.forEach((newFile) => {
this.scheduleUpload(newFile);
});
+ return files.length;
}
stopAllUploads() {
diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json
index 79b16d29f..d546729e5 100644
--- a/apps/red-ui/src/assets/i18n/en.json
+++ b/apps/red-ui/src/assets/i18n/en.json
@@ -695,5 +695,15 @@
"rule-editor": "Rule Editor",
"watermark": "Watermark",
"pending-changes-guard": "WARNING: You have unsaved changes. Press Cancel to go back and save these changes, or OK to lose these changes.",
- "reset-filters": "Reset Filters"
+ "reset-filters": "Reset Filters",
+ "overwrite-files-dialog": {
+ "title": "File already exists!",
+ "question": "{{filename}} already exists. What do you want to do?",
+ "options": {
+ "overwrite": "Overwrite",
+ "no-overwrite": "Keep old file",
+ "cancel": "Cancel all uploads",
+ "remember": "Remember option"
+ }
+ }
}