[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:
swapnil-verma-gl 2025-01-17 15:52:18 +05:30 committed by GitHub
parent 72e740502e
commit d30c933259
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 504 additions and 6 deletions

View File

@ -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",

View File

@ -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": {

View File

@ -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>

View File

@ -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%;
}
}
}
}
}

View File

@ -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);
}));
});

View File

@ -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');
}
});
}
}

View File

@ -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'
});
});
});
}); });

View File

@ -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'
});
}
} }

View File

@ -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);
}));
});
}); });

View File

@ -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 }
);
} }

View File

@ -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) {}
}