From 22926a13e3cd03f2e5ad4d82dddbdfaedb5f8cb4 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 7 Nov 2025 17:38:51 +0200 Subject: [PATCH 1/5] feat(files-tree): implement in tree drag and drop support --- .../confirm-move-file-dialog.component.html | 11 ++ .../confirm-move-file-dialog.component.scss | 0 ...confirm-move-file-dialog.component.spec.ts | 22 +++ .../confirm-move-file-dialog.component.ts | 152 ++++++++++++++++++ .../files-tree/files-tree.component.html | 3 + .../files-tree/files-tree.component.ts | 37 ++++- src/assets/i18n/en.json | 3 + 7 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.html create mode 100644 src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.scss create mode 100644 src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts create mode 100644 src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts diff --git a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.html b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.html new file mode 100644 index 000000000..1b67f9d6a --- /dev/null +++ b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.html @@ -0,0 +1,11 @@ +
+
+
+ + + + +
+
diff --git a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.scss b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts new file mode 100644 index 000000000..591533c91 --- /dev/null +++ b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ConfirmMoveFileDialogComponent } from './confirm-move-file-dialog.component'; + +describe('ConfirmMoveFileDialogComponent', () => { + let component: ConfirmMoveFileDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ConfirmMoveFileDialogComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ConfirmMoveFileDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts new file mode 100644 index 000000000..8c7813f3b --- /dev/null +++ b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts @@ -0,0 +1,152 @@ +import { select } from '@ngxs/store'; + +import { TranslatePipe, TranslateService } from '@ngx-translate/core'; + +import { Button } from 'primeng/button'; +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; +import { ScrollerModule } from 'primeng/scroller'; + +import { finalize, forkJoin, of } from 'rxjs'; +import { catchError } from 'rxjs/operators'; + +import { ChangeDetectionStrategy, Component, DestroyRef, inject } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + +import { FilesSelectors } from '@osf/features/files/store'; +import { CustomConfirmationService } from '@osf/shared/services/custom-confirmation.service'; +import { FilesService } from '@osf/shared/services/files.service'; +import { ToastService } from '@osf/shared/services/toast.service'; +import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource'; +import { FileMenuType } from '@shared/enums/file-menu-type.enum'; +import { FileModel } from '@shared/models/files/file.model'; + +@Component({ + selector: 'osf-move-file-dialog', + imports: [Button, TranslatePipe, ScrollerModule], + templateUrl: './confirm-move-file-dialog.component.html', + styleUrl: './confirm-move-file-dialog.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ConfirmMoveFileDialogComponent { + readonly config = inject(DynamicDialogConfig); + readonly dialogRef = inject(DynamicDialogRef); + private readonly filesService = inject(FilesService); + private readonly destroyRef = inject(DestroyRef); + private readonly translateService = inject(TranslateService); + private readonly toastService = inject(ToastService); + private readonly customConfirmationService = inject(CustomConfirmationService); + + readonly files = select(FilesSelectors.getMoveDialogFiles); + readonly currentProject = select(CurrentResourceSelectors.getCurrentResource); + readonly provider = select(FilesSelectors.getProvider); + + private fileProjectId = this.config.data.resourceId; + protected currentFolder = this.config.data.destination; + + get dragNodeName() { + const filesCount = this.config.data.files.length; + if (filesCount > 1) { + return this.translateService.instant('files.dialogs.moveFile.multipleFiles', { count: filesCount }); + } else { + return this.config.data.files[0]?.name; + } + } + + copyFiles(): void { + return this.copyOrMoveFiles(FileMenuType.Copy); + } + + moveFiles(): void { + return this.copyOrMoveFiles(FileMenuType.Move); + } + + private copyOrMoveFiles(action: FileMenuType): void { + const path = this.currentFolder.path; + if (!path) { + throw new Error(this.translateService.instant('files.dialogs.moveFile.pathError')); + } + const isMoveAction = action === FileMenuType.Move; + + const headerKey = isMoveAction ? 'files.dialogs.moveFile.movingHeader' : 'files.dialogs.moveFile.copingHeader'; + this.config.header = this.translateService.instant(headerKey); + const files: FileModel[] = this.config.data.files; + const totalFiles = files.length; + let completed = 0; + const conflictFiles: { file: FileModel; link: string }[] = []; + + files.forEach((file) => { + const link = file.links.move; + this.filesService + .moveFile(link, path, this.fileProjectId, this.provider(), action) + .pipe( + takeUntilDestroyed(this.destroyRef), + catchError((error) => { + if (error.status === 409) { + conflictFiles.push({ file, link }); + } else { + this.toastService.showError(error.error?.message ?? 'Error'); + } + return of(null); + }), + finalize(() => { + completed++; + if (completed === totalFiles) { + if (conflictFiles.length > 0) { + this.openReplaceMoveDialog(conflictFiles, path, action); + } else { + this.showToast(action); + this.config.header = this.translateService.instant('files.dialogs.moveFile.title'); + this.completeMove(); + } + } + }) + ) + .subscribe(); + }); + } + + private openReplaceMoveDialog( + conflictFiles: { file: FileModel; link: string }[], + path: string, + action: string + ): void { + this.customConfirmationService.confirmDelete({ + headerKey: conflictFiles.length > 1 ? 'files.dialogs.replaceFile.multiple' : 'files.dialogs.replaceFile.single', + messageKey: 'files.dialogs.replaceFile.message', + messageParams: { + name: conflictFiles.map((c) => c.file.name).join(', '), + }, + acceptLabelKey: 'common.buttons.replace', + onConfirm: () => { + const replaceRequests$ = conflictFiles.map(({ link }) => + this.filesService.moveFile(link, path, this.fileProjectId, this.provider(), action, true).pipe( + takeUntilDestroyed(this.destroyRef), + catchError(() => of(null)) + ) + ); + forkJoin(replaceRequests$).subscribe({ + next: () => { + this.showToast(action); + this.completeMove(); + }, + }); + }, + onReject: () => { + const totalFiles = this.config.data.files.length; + if (totalFiles > conflictFiles.length) { + this.showToast(action); + } + this.completeMove(); + }, + }); + } + + private showToast(action: string): void { + const messageType = action === 'move' ? 'moveFile' : 'copyFile'; + this.toastService.showSuccess(`files.dialogs.${messageType}.success`); + } + + private completeMove(): void { + this.dialogRef.close(true); + } +} diff --git a/src/app/shared/components/files-tree/files-tree.component.html b/src/app/shared/components/files-tree/files-tree.component.html index 18ac14c55..6c6444787 100644 --- a/src/app/shared/components/files-tree/files-tree.component.html +++ b/src/app/shared/components/files-tree/files-tree.component.html @@ -25,6 +25,8 @@
@if (file.previousFolder) { diff --git a/src/app/shared/components/files-tree/files-tree.component.ts b/src/app/shared/components/files-tree/files-tree.component.ts index 4b30a486b..593fd8a5d 100644 --- a/src/app/shared/components/files-tree/files-tree.component.ts +++ b/src/app/shared/components/files-tree/files-tree.component.ts @@ -3,7 +3,7 @@ import { select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; import { PrimeTemplate } from 'primeng/api'; -import { Tree, TreeNodeSelectEvent, TreeScrollIndexChangeEvent } from 'primeng/tree'; +import { Tree, TreeNodeDropEvent, TreeNodeSelectEvent, TreeScrollIndexChangeEvent } from 'primeng/tree'; import { Clipboard } from '@angular/cdk/clipboard'; import { DatePipe } from '@angular/common'; @@ -27,6 +27,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; import { ENVIRONMENT } from '@core/provider/environment.provider'; +import { ConfirmMoveFileDialogComponent } from '@osf/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component'; import { MoveFileDialogComponent } from '@osf/features/files/components/move-file-dialog/move-file-dialog.component'; import { RenameFileDialogComponent } from '@osf/features/files/components/rename-file-dialog/rename-file-dialog.component'; import { embedDynamicJs, embedStaticHtml } from '@osf/features/files/constants'; @@ -458,7 +459,41 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit { this.lastSelectedFile = selectedNode; } + onNodeDrop(event: TreeNodeDropEvent) { + const dropFile = event.dropNode as FileModel; + if (dropFile.kind !== FileKind.Folder) { + return; + } + const files = this.selectedFiles(); + const dragFile = event.dragNode as FileModel; + if (!files.includes(dragFile)) { + this.selectFile.emit(dragFile); + files.push(dragFile); + } + this.moveFilesTo(files, dropFile); + } + onNodeUnselect(event: TreeNodeSelectEvent) { this.unselectFile.emit(event.node as FileModel); } + + private moveFilesTo(files: FileModel[], destination: FileModel) { + const isMultiple = files.length > 1; + this.customDialogService + .open(ConfirmMoveFileDialogComponent, { + header: isMultiple ? 'files.dialogs.moveFile.dialogTitleMultiple' : 'files.dialogs.moveFile.dialogTitle', + width: '552px', + data: { + files, + destination, + resourceId: this.resourceId(), + storageProvider: this.storage()?.folder.provider, + foldersStack: structuredClone(this.foldersStack), + initialFolder: structuredClone(this.currentFolder()), + }, + }) + .onClose.subscribe(() => { + this.resetFilesProvider.emit(); + }); + } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 11e832513..db418db99 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1155,7 +1155,10 @@ "moveFile": { "cannotMove": "Cannot move or copy to this folder", "title": "Select Destination", + "dialogTitle": "Move file", + "dialogTitleMultiple": "Move files", "message": "Are you sure you want to move {{dragNodeName}} to {{dropNodeName}} ?", + "multipleFiles": "{{count}} files", "storage": "OSF Storage", "pathError": "Path is not specified!", "success": "Successfully moved.", From 730b77d79fba7816799336f9ba2abd2c76f361e7 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 7 Nov 2025 17:51:58 +0200 Subject: [PATCH 2/5] fix(translations): fixed typo --- src/assets/i18n/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index db418db99..495d94550 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1164,7 +1164,7 @@ "success": "Successfully moved.", "noMovePermission": "Cannot move or copy to this file provider", "movingHeader": "Moving...", - "copingHeader": "Coping..." + "copingHeader": "Copying..." }, "copyFile": { "success": "File successfully copied." From 2f2a3d6aabc9dd1f2230c253e0a167794e8c217e Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 10 Nov 2025 15:00:32 +0200 Subject: [PATCH 3/5] test(confirm-move-file-dialog): fixed tests --- ...confirm-move-file-dialog.component.spec.ts | 65 ++++++++++++++++++- .../confirm-move-file-dialog.component.ts | 2 - .../files-tree/files-tree.component.spec.ts | 2 + 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts index 591533c91..0e8ad4bdd 100644 --- a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts +++ b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts @@ -1,14 +1,65 @@ +import { TranslatePipe } from '@ngx-translate/core'; +import { MockComponents, MockPipe } from 'ng-mocks'; + +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; + +import { signal } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FileSelectDestinationComponent } from '@osf/shared/components/file-select-destination/file-select-destination.component'; +import { IconComponent } from '@osf/shared/components/icon/icon.component'; +import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component'; +import { CustomConfirmationService } from '@osf/shared/services/custom-confirmation.service'; +import { FilesService } from '@osf/shared/services/files.service'; +import { ToastService } from '@osf/shared/services/toast.service'; + +import { FilesSelectors } from '../../store'; + import { ConfirmMoveFileDialogComponent } from './confirm-move-file-dialog.component'; -describe('ConfirmMoveFileDialogComponent', () => { +import { OSFTestingModule } from '@testing/osf.testing.module'; +import { CustomConfirmationServiceMock } from '@testing/providers/custom-confirmation-provider.mock'; +import { provideMockStore } from '@testing/providers/store-provider.mock'; +import { ToastServiceMock } from '@testing/providers/toast-provider.mock'; + +describe('ConfirmConfirmMoveFileDialogComponent', () => { let component: ConfirmMoveFileDialogComponent; let fixture: ComponentFixture; + const mockFilesService = { + moveFiles: jest.fn(), + getMoveDialogFiles: jest.fn(), + }; + beforeEach(async () => { + const dialogRefMock = { + close: jest.fn(), + }; + + const dialogConfigMock = { + data: { files: [], destination: { name: 'files' } }, + }; + await TestBed.configureTestingModule({ - imports: [ConfirmMoveFileDialogComponent], + imports: [ + ConfirmMoveFileDialogComponent, + OSFTestingModule, + ...MockComponents(IconComponent, LoadingSpinnerComponent, FileSelectDestinationComponent), + MockPipe(TranslatePipe), + ], + providers: [ + { provide: DynamicDialogRef, useValue: dialogRefMock }, + { provide: DynamicDialogConfig, useValue: dialogConfigMock }, + { provide: FilesService, useValue: mockFilesService }, + { provide: ToastService, useValue: ToastServiceMock.simple() }, + { provide: CustomConfirmationService, useValue: CustomConfirmationServiceMock.simple() }, + provideMockStore({ + signals: [ + { selector: FilesSelectors.getMoveDialogFiles, value: signal([]) }, + { selector: FilesSelectors.getProvider, value: signal(null) }, + ], + }), + ], }).compileComponents(); fixture = TestBed.createComponent(ConfirmMoveFileDialogComponent); @@ -19,4 +70,14 @@ describe('ConfirmMoveFileDialogComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should initialize with correct properties', () => { + expect(component.config).toBeDefined(); + expect(component.dialogRef).toBeDefined(); + expect(component.files).toBeDefined(); + }); + + it('should get files from store', () => { + expect(component.files()).toEqual([]); + }); }); diff --git a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts index 8c7813f3b..85a400246 100644 --- a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts +++ b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts @@ -16,7 +16,6 @@ import { FilesSelectors } from '@osf/features/files/store'; import { CustomConfirmationService } from '@osf/shared/services/custom-confirmation.service'; import { FilesService } from '@osf/shared/services/files.service'; import { ToastService } from '@osf/shared/services/toast.service'; -import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource'; import { FileMenuType } from '@shared/enums/file-menu-type.enum'; import { FileModel } from '@shared/models/files/file.model'; @@ -37,7 +36,6 @@ export class ConfirmMoveFileDialogComponent { private readonly customConfirmationService = inject(CustomConfirmationService); readonly files = select(FilesSelectors.getMoveDialogFiles); - readonly currentProject = select(CurrentResourceSelectors.getCurrentResource); readonly provider = select(FilesSelectors.getProvider); private fileProjectId = this.config.data.resourceId; diff --git a/src/app/shared/components/files-tree/files-tree.component.spec.ts b/src/app/shared/components/files-tree/files-tree.component.spec.ts index 4190c6f9d..e868ab604 100644 --- a/src/app/shared/components/files-tree/files-tree.component.spec.ts +++ b/src/app/shared/components/files-tree/files-tree.component.spec.ts @@ -1,5 +1,6 @@ import { MockComponents, MockProvider } from 'ng-mocks'; +import { TreeDragDropService } from 'primeng/api'; import { DialogService } from 'primeng/dynamicdialog'; import { signal } from '@angular/core'; @@ -53,6 +54,7 @@ describe('FilesTreeComponent', () => { MockProvider(ToastService), MockProvider(CustomConfirmationService), MockProvider(DialogService), + TreeDragDropService, ], }).compileComponents(); From af2650ed954f25c07f1fb7d8f646127285debcbd Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 11 Nov 2025 14:22:40 +0200 Subject: [PATCH 4/5] fix(move-file-dialog): fixed toasts --- .../confirm-move-file-dialog.component.ts | 18 +++++++++++------- .../move-file-dialog.component.ts | 15 ++++++++++----- src/assets/i18n/en.json | 1 + 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts index 85a400246..9a8d5bcf5 100644 --- a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts +++ b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts @@ -4,7 +4,6 @@ import { TranslatePipe, TranslateService } from '@ngx-translate/core'; import { Button } from 'primeng/button'; import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; -import { ScrollerModule } from 'primeng/scroller'; import { finalize, forkJoin, of } from 'rxjs'; import { catchError } from 'rxjs/operators'; @@ -21,7 +20,7 @@ import { FileModel } from '@shared/models/files/file.model'; @Component({ selector: 'osf-move-file-dialog', - imports: [Button, TranslatePipe, ScrollerModule], + imports: [Button, TranslatePipe], templateUrl: './confirm-move-file-dialog.component.html', styleUrl: './confirm-move-file-dialog.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -82,7 +81,7 @@ export class ConfirmMoveFileDialogComponent { if (error.status === 409) { conflictFiles.push({ file, link }); } else { - this.toastService.showError(error.error?.message ?? 'Error'); + this.showErrorToast(action, error.error?.message); } return of(null); }), @@ -92,7 +91,7 @@ export class ConfirmMoveFileDialogComponent { if (conflictFiles.length > 0) { this.openReplaceMoveDialog(conflictFiles, path, action); } else { - this.showToast(action); + this.showSuccessToast(action); this.config.header = this.translateService.instant('files.dialogs.moveFile.title'); this.completeMove(); } @@ -124,7 +123,7 @@ export class ConfirmMoveFileDialogComponent { ); forkJoin(replaceRequests$).subscribe({ next: () => { - this.showToast(action); + this.showSuccessToast(action); this.completeMove(); }, }); @@ -132,18 +131,23 @@ export class ConfirmMoveFileDialogComponent { onReject: () => { const totalFiles = this.config.data.files.length; if (totalFiles > conflictFiles.length) { - this.showToast(action); + this.showErrorToast(action); } this.completeMove(); }, }); } - private showToast(action: string): void { + private showSuccessToast(action: string) { const messageType = action === 'move' ? 'moveFile' : 'copyFile'; this.toastService.showSuccess(`files.dialogs.${messageType}.success`); } + private showErrorToast(action: string, errorMessage?: string) { + const messageType = action === 'move' ? 'moveFile' : 'copyFile'; + this.toastService.showError(errorMessage ?? `files.dialogs.${messageType}.error`); + } + private completeMove(): void { this.dialogRef.close(true); } diff --git a/src/app/features/files/components/move-file-dialog/move-file-dialog.component.ts b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.ts index e3b33a856..4374dade2 100644 --- a/src/app/features/files/components/move-file-dialog/move-file-dialog.component.ts +++ b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.ts @@ -211,7 +211,7 @@ export class MoveFileDialogComponent { if (error.status === 409) { conflictFiles.push({ file, link }); } else { - this.toastService.showError(error.error?.message ?? 'Error'); + this.showErrorToast(action, error.error?.message ?? 'Error'); } return of(null); }), @@ -221,7 +221,7 @@ export class MoveFileDialogComponent { if (conflictFiles.length > 0) { this.openReplaceMoveDialog(conflictFiles, path, action); } else { - this.showToast(action); + this.showSuccessToast(action); this.config.header = this.translateService.instant('files.dialogs.moveFile.title'); this.completeMove(); } @@ -254,7 +254,7 @@ export class MoveFileDialogComponent { forkJoin(replaceRequests$).subscribe({ next: () => { - this.showToast(action); + this.showSuccessToast(action); this.completeMove(); }, }); @@ -262,18 +262,23 @@ export class MoveFileDialogComponent { onReject: () => { const totalFiles = this.config.data.files.length; if (totalFiles > conflictFiles.length) { - this.showToast(action); + this.showErrorToast(action); } this.completeMove(); }, }); } - private showToast(action: string): void { + private showSuccessToast(action: string) { const messageType = action === 'move' ? 'moveFile' : 'copyFile'; this.toastService.showSuccess(`files.dialogs.${messageType}.success`); } + private showErrorToast(action: string, errorMessage?: string) { + const messageType = action === 'move' ? 'moveFile' : 'copyFile'; + this.toastService.showError(errorMessage ?? `files.dialogs.${messageType}.error`); + } + private completeMove(): void { this.isFilesUpdating.set(false); this.actions.setCurrentFolder(this.initialFolder); diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 495d94550..0c126478d 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1163,6 +1163,7 @@ "pathError": "Path is not specified!", "success": "Successfully moved.", "noMovePermission": "Cannot move or copy to this file provider", + "error": "Failed to move or copy files, please try again later", "movingHeader": "Moving...", "copingHeader": "Copying..." }, From ce0ac16f72203663fbb9556e92d0b3154f7c1e01 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 11 Nov 2025 14:30:44 +0200 Subject: [PATCH 5/5] fix(confirm-move-dialog): fixed pr comments --- .../confirm-move-file-dialog.component.spec.ts | 5 ++--- .../confirm-move-file-dialog.component.ts | 2 +- .../move-file-dialog.component.spec.ts | 17 ++++++++--------- .../files-tree/files-tree.component.ts | 2 -- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts index 0e8ad4bdd..88f0214fa 100644 --- a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts +++ b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.spec.ts @@ -3,7 +3,6 @@ import { MockComponents, MockPipe } from 'ng-mocks'; import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; -import { signal } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FileSelectDestinationComponent } from '@osf/shared/components/file-select-destination/file-select-destination.component'; @@ -55,8 +54,8 @@ describe('ConfirmConfirmMoveFileDialogComponent', () => { { provide: CustomConfirmationService, useValue: CustomConfirmationServiceMock.simple() }, provideMockStore({ signals: [ - { selector: FilesSelectors.getMoveDialogFiles, value: signal([]) }, - { selector: FilesSelectors.getProvider, value: signal(null) }, + { selector: FilesSelectors.getMoveDialogFiles, value: [] }, + { selector: FilesSelectors.getProvider, value: null }, ], }), ], diff --git a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts index 9a8d5bcf5..373a517f2 100644 --- a/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts +++ b/src/app/features/files/components/confirm-move-file-dialog/confirm-move-file-dialog.component.ts @@ -35,7 +35,7 @@ export class ConfirmMoveFileDialogComponent { private readonly customConfirmationService = inject(CustomConfirmationService); readonly files = select(FilesSelectors.getMoveDialogFiles); - readonly provider = select(FilesSelectors.getProvider); + readonly provider = this.config.data.storageProvider; private fileProjectId = this.config.data.resourceId; protected currentFolder = this.config.data.destination; diff --git a/src/app/features/files/components/move-file-dialog/move-file-dialog.component.spec.ts b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.spec.ts index 3e5c8e56f..2dfcb0406 100644 --- a/src/app/features/files/components/move-file-dialog/move-file-dialog.component.spec.ts +++ b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.spec.ts @@ -3,7 +3,6 @@ import { MockComponents, MockPipe } from 'ng-mocks'; import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; -import { signal } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FileSelectDestinationComponent } from '@osf/shared/components/file-select-destination/file-select-destination.component'; @@ -56,14 +55,14 @@ describe('MoveFileDialogComponent', () => { { provide: CustomConfirmationService, useValue: CustomConfirmationServiceMock.simple() }, provideMockStore({ signals: [ - { selector: FilesSelectors.getMoveDialogFiles, value: signal([]) }, - { selector: FilesSelectors.getMoveDialogFilesTotalCount, value: signal(0) }, - { selector: FilesSelectors.isMoveDialogFilesLoading, value: signal(false) }, - { selector: FilesSelectors.getMoveDialogCurrentFolder, value: signal(null) }, - { selector: CurrentResourceSelectors.getCurrentResource, value: signal(null) }, - { selector: CurrentResourceSelectors.getResourceWithChildren, value: signal([]) }, - { selector: CurrentResourceSelectors.isResourceWithChildrenLoading, value: signal(false) }, - { selector: FilesSelectors.isMoveDialogConfiguredStorageAddonsLoading, value: signal(false) }, + { selector: FilesSelectors.getMoveDialogFiles, value: [] }, + { selector: FilesSelectors.getMoveDialogFilesTotalCount, value: 0 }, + { selector: FilesSelectors.isMoveDialogFilesLoading, value: false }, + { selector: FilesSelectors.getMoveDialogCurrentFolder, value: null }, + { selector: CurrentResourceSelectors.getCurrentResource, value: null }, + { selector: CurrentResourceSelectors.getResourceWithChildren, value: [] }, + { selector: CurrentResourceSelectors.isResourceWithChildrenLoading, value: false }, + { selector: FilesSelectors.isMoveDialogConfiguredStorageAddonsLoading, value: false }, ], }), ], diff --git a/src/app/shared/components/files-tree/files-tree.component.ts b/src/app/shared/components/files-tree/files-tree.component.ts index 593fd8a5d..d01320220 100644 --- a/src/app/shared/components/files-tree/files-tree.component.ts +++ b/src/app/shared/components/files-tree/files-tree.component.ts @@ -488,8 +488,6 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit { destination, resourceId: this.resourceId(), storageProvider: this.storage()?.folder.provider, - foldersStack: structuredClone(this.foldersStack), - initialFolder: structuredClone(this.currentFolder()), }, }) .onClose.subscribe(() => {