mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
[MNT-24575] Added dialog to display folder information (#4282)
* [MNT-24575] Created folder information dialog * [MNT-24575] Added unit tests * [MNT-24575] Integrated API calls into folder information * [MNT-24575] Added models * [MNT-24575] Added delay to API retry call. Added unit test for API retry functionality * [MNT-24575] Folder information is no longer shown in trashcan * [MNT-24575] Folder information is no longer shown in trashcan * [MNT-24575] Added return type to function. Consolidated different properties into a single type * [MNT-24575] Addressed Code review comments * [MNT-24575] Addressed code review findings. Added error handling and unit tests * [MNT-24575] Updated template to use adf-icons * [MNT-24575] Using variables in SCSS
This commit is contained in:
parent
72e740502e
commit
d30c933259
@ -632,6 +632,21 @@
|
|||||||
"visible": "app.selection.canDelete"
|
"visible": "app.selection.canDelete"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "app.context.menu.folder-info",
|
||||||
|
"title": "APP.ACTIONS.FOLDER_INFO",
|
||||||
|
"order": 800,
|
||||||
|
"icon": "info",
|
||||||
|
"actions": {
|
||||||
|
"click": "FOLDER_INFORMATION"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"visible": [
|
||||||
|
"app.selection.folder",
|
||||||
|
"!app.navigation.isTrashcan"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "app.create.separator.3",
|
"id": "app.create.separator.3",
|
||||||
"type": "separator",
|
"type": "separator",
|
||||||
@ -877,6 +892,21 @@
|
|||||||
"visible": "app.selection.canDelete"
|
"visible": "app.selection.canDelete"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "app.context.menu.folder-info",
|
||||||
|
"title": "APP.ACTIONS.FOLDER_INFO",
|
||||||
|
"order": 1200,
|
||||||
|
"icon": "info",
|
||||||
|
"actions": {
|
||||||
|
"click": "FOLDER_INFORMATION"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"visible": [
|
||||||
|
"app.selection.folder",
|
||||||
|
"!app.navigation.isTrashcan"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "app.create.separator.3",
|
"id": "app.create.separator.3",
|
||||||
"type": "separator",
|
"type": "separator",
|
||||||
|
@ -299,7 +299,8 @@
|
|||||||
"EDIT_OFFLINE": "Edit Offline",
|
"EDIT_OFFLINE": "Edit Offline",
|
||||||
"EDIT_OFFLINE_CANCEL": "Cancel Editing",
|
"EDIT_OFFLINE_CANCEL": "Cancel Editing",
|
||||||
"CHANGE_ASPECT": "Edit Aspects",
|
"CHANGE_ASPECT": "Edit Aspects",
|
||||||
"ADD_ASPECTS": "Add Aspects"
|
"ADD_ASPECTS": "Add Aspects",
|
||||||
|
"FOLDER_INFO": "Folder Information"
|
||||||
},
|
},
|
||||||
"DIALOGS": {
|
"DIALOGS": {
|
||||||
"CONFIRM_PURGE": {
|
"CONFIRM_PURGE": {
|
||||||
@ -451,6 +452,19 @@
|
|||||||
"OPTIONS_SETTINGS": "Options and settings",
|
"OPTIONS_SETTINGS": "Options and settings",
|
||||||
"MY_PROFILE": "My profile",
|
"MY_PROFILE": "My profile",
|
||||||
"EXPAND_NAVIGATION": "Expand navigation menu"
|
"EXPAND_NAVIGATION": "Expand navigation menu"
|
||||||
|
},
|
||||||
|
"FOLDER_INFO": {
|
||||||
|
"ICON": "Folder Icon",
|
||||||
|
"TITLE": "Folder Information",
|
||||||
|
"SIZE" : "Size",
|
||||||
|
"CALCULATING": "Calculating...",
|
||||||
|
"CALCULATED_SIZE_LARGE": "{{sizeInBytes}} bytes ({{sizeInLargeUnit}} {{unit}} on disk) for {{count}} files",
|
||||||
|
"CALCULATED_SIZE_NORMAL": "{{sizeInBytes}} bytes for {{count}} files",
|
||||||
|
"LOCATION": "Location",
|
||||||
|
"CREATED": "Created",
|
||||||
|
"MODIFIED": "Modified",
|
||||||
|
"DONE": "Done",
|
||||||
|
"ERROR": "Something went wrong, please close this dialog and try again"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"NODE_SELECTOR": {
|
"NODE_SELECTOR": {
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
<div class="aca-folder-info-header">
|
||||||
|
<adf-icon value="folder" class="aca-folder-icon" fontSet="material-icons"/>
|
||||||
|
<div class="aca-folder-title" data-automation-id="folder-info-name">{{ folderDetails.name }}</div>
|
||||||
|
</div>
|
||||||
|
<mat-divider/>
|
||||||
|
<div class="aca-folder-info-body">
|
||||||
|
<div class="aca-folder-info-item">
|
||||||
|
<div class="aca-folder-info-item-label">{{ 'APP.FOLDER_INFO.SIZE' | translate }}</div>
|
||||||
|
<div class="aca-folder-info-item-value"
|
||||||
|
data-automation-id="folder-info-size">{{ folderDetails.size }}</div>
|
||||||
|
</div>
|
||||||
|
<mat-divider/>
|
||||||
|
<div class="aca-folder-info-item">
|
||||||
|
<div class="aca-folder-info-item-label">{{ 'APP.FOLDER_INFO.LOCATION' | translate }}</div>
|
||||||
|
<div class="aca-folder-info-item-value"
|
||||||
|
data-automation-id="folder-info-location">{{ folderDetails.location }}</div>
|
||||||
|
</div>
|
||||||
|
<mat-divider/>
|
||||||
|
<div class="aca-folder-info-item">
|
||||||
|
<div class="aca-folder-info-item-label">{{ 'APP.FOLDER_INFO.CREATED' | translate }}</div>
|
||||||
|
<div class="aca-folder-info-item-value"
|
||||||
|
data-automation-id="folder-info-creation-date"
|
||||||
|
[title]="folderDetails.created | adfLocalizedDate">{{ folderDetails.created | adfTimeAgo }}</div>
|
||||||
|
</div>
|
||||||
|
<mat-divider/>
|
||||||
|
<div class="aca-folder-info-item">
|
||||||
|
<div class="aca-folder-info-item-label">{{ 'APP.FOLDER_INFO.MODIFIED' | translate }}</div>
|
||||||
|
<div class="aca-folder-info-item-value"
|
||||||
|
data-automation-id="folder-info-modify-date"
|
||||||
|
[title]="folderDetails.modified | adfLocalizedDate">{{ folderDetails.modified | adfTimeAgo }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,52 @@
|
|||||||
|
$folder-icon-color: #d9e021;
|
||||||
|
|
||||||
|
.app-folder-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid var(--theme-border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
.aca-folder-info {
|
||||||
|
&-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.aca-folder-icon {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
vertical-align: middle;
|
||||||
|
color: $folder-icon-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aca-folder-title {
|
||||||
|
flex: 1;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px 20px;
|
||||||
|
|
||||||
|
.aca-folder-info-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 20px 0;
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
width: 30%;
|
||||||
|
color: var(--theme-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-value {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
|
import { FolderInformationComponent } from './folder-information.component';
|
||||||
|
import { DIALOG_COMPONENT_DATA, RedirectAuthService } from '@alfresco/adf-core';
|
||||||
|
import { ContentService, NodesApiService } from '@alfresco/adf-content-services';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { EMPTY, Observable, of, Subject } from 'rxjs';
|
||||||
|
import { LibTestingModule } from '@alfresco/aca-shared';
|
||||||
|
import { JobIdBodyEntry, SizeDetails, SizeDetailsEntry, Node } from '@alfresco/js-api';
|
||||||
|
|
||||||
|
describe('FolderInformationComponent', () => {
|
||||||
|
let fixture: ComponentFixture<FolderInformationComponent>;
|
||||||
|
let nodeService: NodesApiService;
|
||||||
|
let initiateFolderSizeCalculationSpy: jasmine.Spy<(nodeId: string) => Observable<JobIdBodyEntry>>;
|
||||||
|
let getFolderSizeInfoSpy: jasmine.Spy<(nodeId: string, jobId: string) => Observable<SizeDetailsEntry>>;
|
||||||
|
|
||||||
|
const mockSub = new Subject<JobIdBodyEntry>();
|
||||||
|
const dialogData = {
|
||||||
|
name: 'mock-folder',
|
||||||
|
id: 'mock-folder-id',
|
||||||
|
path: {
|
||||||
|
name: 'mock-folder-path'
|
||||||
|
},
|
||||||
|
createdAt: new Date(2024, 1, 1, 11, 11),
|
||||||
|
modifiedAt: new Date(2024, 2, 2, 22, 22)
|
||||||
|
} as Node;
|
||||||
|
const mockSizeDetailsEntry: SizeDetailsEntry = {
|
||||||
|
entry: {
|
||||||
|
id: 'mock-id',
|
||||||
|
sizeInBytes: '1',
|
||||||
|
calculatedAt: 'mock-date',
|
||||||
|
numberOfFiles: 1,
|
||||||
|
status: SizeDetails.StatusEnum.COMPLETE,
|
||||||
|
jobId: 'mock-job-id'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getValueFromElement = (id: string): string => fixture.debugElement.query(By.css(`[data-automation-id="${id}"]`)).nativeElement.textContent;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [FolderInformationComponent, LibTestingModule],
|
||||||
|
providers: [
|
||||||
|
{ provide: DIALOG_COMPONENT_DATA, useValue: dialogData },
|
||||||
|
{ provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: EMPTY } }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
fixture = TestBed.createComponent(FolderInformationComponent);
|
||||||
|
nodeService = TestBed.inject(NodesApiService);
|
||||||
|
spyOn(TestBed.inject(ContentService), 'getNodeIcon').and.returnValue('./assets/images/ft_ic_folder.svg');
|
||||||
|
initiateFolderSizeCalculationSpy = spyOn(nodeService, 'initiateFolderSizeCalculation').and.returnValue(mockSub.asObservable());
|
||||||
|
getFolderSizeInfoSpy = spyOn(nodeService, 'getFolderSizeInfo').and.returnValue(EMPTY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render all information in init', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getValueFromElement('folder-info-name')).toBe('mock-folder');
|
||||||
|
expect(getValueFromElement('folder-info-size')).toBe('APP.FOLDER_INFO.CALCULATING');
|
||||||
|
expect(getValueFromElement('folder-info-location')).toBe('mock-folder-path');
|
||||||
|
expect(getValueFromElement('folder-info-creation-date')).toBe('01/02/2024 11:11');
|
||||||
|
expect(getValueFromElement('folder-info-modify-date')).toBe('02/03/2024 22:22');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should make API call on init to start folder size calculation', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(initiateFolderSizeCalculationSpy).toHaveBeenCalledWith('mock-folder-id');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch folder size only when the initial folder size calculation request is completed', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(initiateFolderSizeCalculationSpy).toHaveBeenCalledWith('mock-folder-id');
|
||||||
|
expect(getFolderSizeInfoSpy).not.toHaveBeenCalled();
|
||||||
|
mockSub.next({ entry: { jobId: 'mock-job-id' } });
|
||||||
|
expect(getFolderSizeInfoSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should make repeated calls to get folder size info, if the response returned from the API is IN_PROGRESS', fakeAsync(() => {
|
||||||
|
mockSizeDetailsEntry.entry.status = SizeDetails.StatusEnum.IN_PROGRESS;
|
||||||
|
getFolderSizeInfoSpy.and.returnValue(of(mockSizeDetailsEntry));
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getFolderSizeInfoSpy).not.toHaveBeenCalled();
|
||||||
|
mockSub.next({ entry: { jobId: 'mock-job-id' } });
|
||||||
|
expect(getFolderSizeInfoSpy).toHaveBeenCalledTimes(1);
|
||||||
|
tick(5000);
|
||||||
|
expect(getFolderSizeInfoSpy).toHaveBeenCalledTimes(2);
|
||||||
|
tick(5000);
|
||||||
|
expect(getFolderSizeInfoSpy).toHaveBeenCalledTimes(3);
|
||||||
|
mockSizeDetailsEntry.entry.status = SizeDetails.StatusEnum.COMPLETE;
|
||||||
|
tick(5000);
|
||||||
|
expect(getFolderSizeInfoSpy).toHaveBeenCalledTimes(4);
|
||||||
|
tick(5000);
|
||||||
|
expect(getFolderSizeInfoSpy).not.toHaveBeenCalledTimes(5);
|
||||||
|
}));
|
||||||
|
});
|
@ -0,0 +1,122 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { CommonModule, NgOptimizedImage } from '@angular/common';
|
||||||
|
import { DIALOG_COMPONENT_DATA, IconComponent, LocalizedDatePipe, TimeAgoPipe } from '@alfresco/adf-core';
|
||||||
|
import { Node, SizeDetails } from '@alfresco/js-api';
|
||||||
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
|
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||||
|
import { ContentService, NodesApiService } from '@alfresco/adf-content-services';
|
||||||
|
import { catchError, concatMap, expand, first, switchMap } from 'rxjs/operators';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
import { EMPTY, of, timer } from 'rxjs';
|
||||||
|
|
||||||
|
const MEMORY_UNIT_LIST = ['bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||||
|
|
||||||
|
class FolderDetails {
|
||||||
|
name: string;
|
||||||
|
size: string;
|
||||||
|
location: string;
|
||||||
|
created: Date;
|
||||||
|
modified: Date;
|
||||||
|
icon: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-folder-info',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, MatDividerModule, TimeAgoPipe, TranslateModule, LocalizedDatePipe, NgOptimizedImage, IconComponent],
|
||||||
|
templateUrl: './folder-information.component.html',
|
||||||
|
styleUrls: ['./folder-information.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
host: { class: 'app-folder-info' }
|
||||||
|
})
|
||||||
|
export class FolderInformationComponent implements OnInit {
|
||||||
|
readonly contentService = inject(ContentService);
|
||||||
|
readonly nodesService = inject(NodesApiService);
|
||||||
|
readonly translateService = inject(TranslateService);
|
||||||
|
|
||||||
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
|
data: Node = inject(DIALOG_COMPONENT_DATA);
|
||||||
|
folderDetails = new FolderDetails();
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.folderDetails.name = this.data.name;
|
||||||
|
this.folderDetails.location = this.data.path.name;
|
||||||
|
this.folderDetails.created = this.data.createdAt;
|
||||||
|
this.folderDetails.modified = this.data.modifiedAt;
|
||||||
|
this.folderDetails.icon = this.contentService.getNodeIcon(this.data);
|
||||||
|
this.folderDetails.size = this.translateService.instant('APP.FOLDER_INFO.CALCULATING');
|
||||||
|
|
||||||
|
this.nodesService
|
||||||
|
.initiateFolderSizeCalculation(this.data.id)
|
||||||
|
.pipe(
|
||||||
|
first(),
|
||||||
|
switchMap((jobIdEntry) => {
|
||||||
|
return this.nodesService.getFolderSizeInfo(this.data.id, jobIdEntry.entry.jobId).pipe(
|
||||||
|
expand((result) =>
|
||||||
|
result.entry.status === SizeDetails.StatusEnum.IN_PROGRESS
|
||||||
|
? timer(5000).pipe(concatMap(() => this.nodesService.getFolderSizeInfo(this.data.id, jobIdEntry.entry.jobId)))
|
||||||
|
: EMPTY
|
||||||
|
),
|
||||||
|
catchError(() => {
|
||||||
|
this.folderDetails.size = this.translateService.instant('APP.FOLDER_INFO.ERROR');
|
||||||
|
return of(null);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
takeUntilDestroyed(this.destroyRef),
|
||||||
|
catchError(() => {
|
||||||
|
this.folderDetails.size = this.translateService.instant('APP.FOLDER_INFO.ERROR');
|
||||||
|
return of(null);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((folderInfo) => {
|
||||||
|
if (folderInfo?.entry?.status === SizeDetails.StatusEnum.COMPLETE) {
|
||||||
|
let size = parseFloat(folderInfo.entry.sizeInBytes);
|
||||||
|
let unitIndex = 0;
|
||||||
|
let isMoreThanBytes = false;
|
||||||
|
while (size > 1000) {
|
||||||
|
isMoreThanBytes = true;
|
||||||
|
size = size / 1000;
|
||||||
|
unitIndex++;
|
||||||
|
}
|
||||||
|
const params = {
|
||||||
|
sizeInBytes: parseFloat(folderInfo.entry.sizeInBytes).toLocaleString('en'),
|
||||||
|
sizeInLargeUnit: size.toFixed(2),
|
||||||
|
unit: MEMORY_UNIT_LIST[unitIndex],
|
||||||
|
count: folderInfo.entry.numberOfFiles
|
||||||
|
};
|
||||||
|
this.folderDetails.size = this.translateService.instant(
|
||||||
|
isMoreThanBytes ? 'APP.FOLDER_INFO.CALCULATED_SIZE_LARGE' : 'APP.FOLDER_INFO.CALCULATED_SIZE_NORMAL',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.folderDetails.size = this.translateService.instant('APP.FOLDER_INFO.ERROR');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -53,10 +53,10 @@ import { AppHookService, AppSettingsService, ContentApiService } from '@alfresco
|
|||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { ContentManagementService } from './content-management.service';
|
import { ContentManagementService } from './content-management.service';
|
||||||
import { NodeActionsService } from './node-actions.service';
|
import { NodeActionsService } from './node-actions.service';
|
||||||
import { NotificationService, TranslationService } from '@alfresco/adf-core';
|
import { DialogComponent, DialogSize, NotificationService, TranslationService } from '@alfresco/adf-core';
|
||||||
import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { MatSnackBarModule, MatSnackBarRef, SimpleSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBarModule, MatSnackBarRef, SimpleSnackBar } from '@angular/material/snack-bar';
|
||||||
import { Node, NodeEntry, VersionPaging } from '@alfresco/js-api';
|
import { Node, NodeEntry, UserInfo, VersionPaging } from '@alfresco/js-api';
|
||||||
import {
|
import {
|
||||||
DocumentListService,
|
DocumentListService,
|
||||||
FileModel,
|
FileModel,
|
||||||
@ -67,6 +67,7 @@ import {
|
|||||||
NodesApiService,
|
NodesApiService,
|
||||||
ViewVersion
|
ViewVersion
|
||||||
} from '@alfresco/adf-content-services';
|
} from '@alfresco/adf-content-services';
|
||||||
|
import { FolderInformationComponent } from '../dialogs/folder-details/folder-information.component';
|
||||||
|
|
||||||
describe('ContentManagementService', () => {
|
describe('ContentManagementService', () => {
|
||||||
let dialog: MatDialog;
|
let dialog: MatDialog;
|
||||||
@ -1847,4 +1848,38 @@ describe('ContentManagementService', () => {
|
|||||||
expect(store.dispatch).toHaveBeenCalledWith(new NavigateRouteAction(['/libraries']));
|
expect(store.dispatch).toHaveBeenCalledWith(new NavigateRouteAction(['/libraries']));
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('folderInformationDialog', () => {
|
||||||
|
it('should open folder information dialog', () => {
|
||||||
|
spyOn(dialog, 'open');
|
||||||
|
|
||||||
|
const fakeNode: NodeEntry = {
|
||||||
|
entry: {
|
||||||
|
id: 'folder-node-id',
|
||||||
|
name: 'mock-folder-name',
|
||||||
|
nodeType: 'fake-node-type',
|
||||||
|
isFolder: true,
|
||||||
|
isFile: false,
|
||||||
|
modifiedAt: new Date(),
|
||||||
|
modifiedByUser: new UserInfo(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
createdByUser: new UserInfo()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
contentManagementService.showFolderInformation(fakeNode);
|
||||||
|
expect(dialog.open).toHaveBeenCalledWith(DialogComponent, {
|
||||||
|
data: {
|
||||||
|
title: 'APP.FOLDER_INFO.TITLE',
|
||||||
|
confirmButtonTitle: 'APP.FOLDER_INFO.DONE',
|
||||||
|
isCancelButtonHidden: true,
|
||||||
|
isCloseButtonHidden: false,
|
||||||
|
dialogSize: DialogSize.Large,
|
||||||
|
contentComponent: FolderInformationComponent,
|
||||||
|
componentData: fakeNode.entry
|
||||||
|
},
|
||||||
|
width: '700px'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -54,7 +54,7 @@ import {
|
|||||||
NodesApiService,
|
NodesApiService,
|
||||||
ShareDialogComponent
|
ShareDialogComponent
|
||||||
} from '@alfresco/adf-content-services';
|
} from '@alfresco/adf-content-services';
|
||||||
import { NotificationService, TranslationService, ConfirmDialogComponent } from '@alfresco/adf-core';
|
import { NotificationService, TranslationService, ConfirmDialogComponent, DialogComponent, DialogSize } from '@alfresco/adf-core';
|
||||||
import { DeletedNodesPaging, Node, NodeEntry, PathInfo, SiteBodyCreate, SiteEntry } from '@alfresco/js-api';
|
import { DeletedNodesPaging, Node, NodeEntry, PathInfo, SiteBodyCreate, SiteEntry } from '@alfresco/js-api';
|
||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
|
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
|
||||||
@ -63,6 +63,7 @@ import { forkJoin, Observable, of, zip } from 'rxjs';
|
|||||||
import { catchError, map, mergeMap, take, tap } from 'rxjs/operators';
|
import { catchError, map, mergeMap, take, tap } from 'rxjs/operators';
|
||||||
import { NodeActionsService } from './node-actions.service';
|
import { NodeActionsService } from './node-actions.service';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { FolderInformationComponent } from '../dialogs/folder-details/folder-information.component';
|
||||||
|
|
||||||
interface RestoredNode {
|
interface RestoredNode {
|
||||||
status: number;
|
status: number;
|
||||||
@ -1089,4 +1090,19 @@ export class ContentManagementService {
|
|||||||
document.querySelector<HTMLElement>(focusedElementSelector)?.focus();
|
document.querySelector<HTMLElement>(focusedElementSelector)?.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showFolderInformation(node: NodeEntry) {
|
||||||
|
this.dialogRef.open(DialogComponent, {
|
||||||
|
data: {
|
||||||
|
title: 'APP.FOLDER_INFO.TITLE',
|
||||||
|
confirmButtonTitle: 'APP.FOLDER_INFO.DONE',
|
||||||
|
isCancelButtonHidden: true,
|
||||||
|
isCloseButtonHidden: false,
|
||||||
|
dialogSize: DialogSize.Large,
|
||||||
|
contentComponent: FolderInformationComponent,
|
||||||
|
componentData: node.entry
|
||||||
|
},
|
||||||
|
width: '700px'
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import {
|
|||||||
DeleteNodesAction,
|
DeleteNodesAction,
|
||||||
EditFolderAction,
|
EditFolderAction,
|
||||||
ExpandInfoDrawerAction,
|
ExpandInfoDrawerAction,
|
||||||
|
FolderInformationAction,
|
||||||
FullscreenViewerAction,
|
FullscreenViewerAction,
|
||||||
ManageAspectsAction,
|
ManageAspectsAction,
|
||||||
ManagePermissionsAction,
|
ManagePermissionsAction,
|
||||||
@ -59,6 +60,7 @@ import { NavigationEnd, Router, ActivatedRoute } from '@angular/router';
|
|||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
|
import { NodeEntry, UserInfo } from '@alfresco/js-api';
|
||||||
|
|
||||||
describe('NodeEffects', () => {
|
describe('NodeEffects', () => {
|
||||||
let store: Store<any>;
|
let store: Store<any>;
|
||||||
@ -564,4 +566,52 @@ describe('NodeEffects', () => {
|
|||||||
expect(store.dispatch).toHaveBeenCalledWith(new NavigateUrlAction('personal-files/details/node-id?location=test-page'));
|
expect(store.dispatch).toHaveBeenCalledWith(new NavigateUrlAction('personal-files/details/node-id?location=test-page'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('folderInformation$', () => {
|
||||||
|
it('should call folder information dialog', () => {
|
||||||
|
const node: NodeEntry = {
|
||||||
|
entry: {
|
||||||
|
id: 'folder-node-id',
|
||||||
|
name: 'mock-folder-name',
|
||||||
|
nodeType: 'fake-node-type',
|
||||||
|
isFolder: true,
|
||||||
|
isFile: false,
|
||||||
|
modifiedAt: new Date(),
|
||||||
|
modifiedByUser: new UserInfo(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
createdByUser: new UserInfo()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(contentService, 'showFolderInformation').and.stub();
|
||||||
|
|
||||||
|
store.dispatch(new FolderInformationAction(node));
|
||||||
|
|
||||||
|
expect(contentService.showFolderInformation).toHaveBeenCalledWith(node);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call folder information dialog from the active folder selection', fakeAsync(() => {
|
||||||
|
spyOn(contentService, 'showFolderInformation').and.stub();
|
||||||
|
|
||||||
|
const node: NodeEntry = {
|
||||||
|
entry: {
|
||||||
|
id: 'folder-node-id',
|
||||||
|
name: 'mock-folder-name',
|
||||||
|
nodeType: 'fake-node-type',
|
||||||
|
isFolder: true,
|
||||||
|
isFile: false,
|
||||||
|
modifiedAt: new Date(),
|
||||||
|
modifiedByUser: new UserInfo(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
createdByUser: new UserInfo()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
store.dispatch(new SetSelectedNodesAction([node]));
|
||||||
|
|
||||||
|
tick(100);
|
||||||
|
|
||||||
|
store.dispatch(new FolderInformationAction(null));
|
||||||
|
|
||||||
|
expect(contentService.showFolderInformation).toHaveBeenCalledWith(node);
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -51,7 +51,8 @@ import {
|
|||||||
ShowLoaderAction,
|
ShowLoaderAction,
|
||||||
UndoDeleteNodesAction,
|
UndoDeleteNodesAction,
|
||||||
UnlockWriteAction,
|
UnlockWriteAction,
|
||||||
UnshareNodesAction
|
UnshareNodesAction,
|
||||||
|
FolderInformationAction
|
||||||
} from '@alfresco/aca-shared/store';
|
} from '@alfresco/aca-shared/store';
|
||||||
import { ContentManagementService } from '../../services/content-management.service';
|
import { ContentManagementService } from '../../services/content-management.service';
|
||||||
import { RenditionService } from '@alfresco/adf-content-services';
|
import { RenditionService } from '@alfresco/adf-content-services';
|
||||||
@ -460,4 +461,26 @@ export class NodeEffects {
|
|||||||
),
|
),
|
||||||
{ dispatch: false }
|
{ dispatch: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
folderInformation$ = createEffect(
|
||||||
|
() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType<FolderInformationAction>(NodeActionTypes.FolderInformation),
|
||||||
|
map((action) => {
|
||||||
|
if (action?.payload) {
|
||||||
|
this.contentService.showFolderInformation(action.payload);
|
||||||
|
} else {
|
||||||
|
this.store
|
||||||
|
.select(getAppSelection)
|
||||||
|
.pipe(take(1))
|
||||||
|
.subscribe((selection) => {
|
||||||
|
if (selection && !selection.isEmpty && selection.folder.entry) {
|
||||||
|
this.contentService.showFolderInformation(selection.folder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
),
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ export enum NodeActionTypes {
|
|||||||
Unshare = 'UNSHARE_NODES',
|
Unshare = 'UNSHARE_NODES',
|
||||||
Copy = 'COPY_NODES',
|
Copy = 'COPY_NODES',
|
||||||
Move = 'MOVE_NODES',
|
Move = 'MOVE_NODES',
|
||||||
|
FolderInformation = 'FOLDER_INFORMATION',
|
||||||
ManagePermissions = 'MANAGE_PERMISSIONS',
|
ManagePermissions = 'MANAGE_PERMISSIONS',
|
||||||
PrintFile = 'PRINT_FILE',
|
PrintFile = 'PRINT_FILE',
|
||||||
ManageVersions = 'MANAGE_VERSIONS',
|
ManageVersions = 'MANAGE_VERSIONS',
|
||||||
@ -180,3 +181,9 @@ export class ManageRulesAction implements Action {
|
|||||||
|
|
||||||
constructor(public payload: NodeEntry) {}
|
constructor(public payload: NodeEntry) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FolderInformationAction implements Action {
|
||||||
|
readonly type = NodeActionTypes.FolderInformation;
|
||||||
|
|
||||||
|
constructor(public payload: NodeEntry) {}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user