diff --git a/src/app/item-page/edit-item-page/edit-item-page.module.ts b/src/app/item-page/edit-item-page/edit-item-page.module.ts index 97901bd7c8..cf4a3de74b 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.module.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgbTooltipModule, NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { SharedModule } from '../../shared/shared.module'; import { EditItemPageRoutingModule } from './edit-item-page.routing.module'; @@ -48,7 +48,8 @@ import { ResourcePoliciesModule } from '../../shared/resource-policies/resource- EditItemPageRoutingModule, SearchPageModule, DragDropModule, - ResourcePoliciesModule + ResourcePoliciesModule, + NgbModule ], declarations: [ EditItemPageComponent, diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.html b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.html index 71aa7b44de..5437525185 100644 --- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.html +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.html @@ -1,13 +1,33 @@
- - - - - + + + + + + +
+
+ +
+
+ + + +
+ +
+
+
+
+ +
- diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.scss b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.scss new file mode 100644 index 0000000000..c3694e6784 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.scss @@ -0,0 +1,4 @@ +.auth-bitstream-container { + margin-top: -1em; + margin-bottom: 1.5em; +} diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts index 97280c3ea0..2fe8a562c6 100644 --- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts @@ -1,11 +1,12 @@ +import { Observable } from 'rxjs/internal/Observable'; import { waitForAsync, ComponentFixture, inject, TestBed } from '@angular/core/testing'; import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { of as observableOf } from 'rxjs'; +import { of as observableOf, of } from 'rxjs'; import { TranslateModule } from '@ngx-translate/core'; import { cold } from 'jasmine-marbles'; -import { ItemAuthorizationsComponent } from './item-authorizations.component'; +import { ItemAuthorizationsComponent, BitstreamMapValue } from './item-authorizations.component'; import { Bitstream } from '../../../core/shared/bitstream.model'; import { Bundle } from '../../../core/shared/bundle.model'; import { Item } from '../../../core/shared/item.model'; @@ -57,8 +58,6 @@ describe('ItemAuthorizationsComponent test suite', () => { bitstreams: createSuccessfulRemoteDataObject$(createPaginatedList([bitstream3, bitstream4])) }); const bundles = [bundle1, bundle2]; - const bitstreamList1: PaginatedList = buildPaginatedList(new PageInfo(), [bitstream1, bitstream2]); - const bitstreamList2: PaginatedList = buildPaginatedList(new PageInfo(), [bitstream3, bitstream4]); const item = Object.assign(new Item(), { uuid: 'item', @@ -142,13 +141,12 @@ describe('ItemAuthorizationsComponent test suite', () => { expect(compAsAny.bundleBitstreamsMap.has('bundle1')).toBeTruthy(); expect(compAsAny.bundleBitstreamsMap.has('bundle2')).toBeTruthy(); let bitstreamList = compAsAny.bundleBitstreamsMap.get('bundle1'); - expect(bitstreamList).toBeObservable(cold('(a|)', { - a: bitstreamList1 + expect(bitstreamList.bitstreams).toBeObservable(cold('(a|)', { + a : [bitstream1, bitstream2] })); - bitstreamList = compAsAny.bundleBitstreamsMap.get('bundle2'); - expect(bitstreamList).toBeObservable(cold('(a|)', { - a: bitstreamList2 + expect(bitstreamList.bitstreams).toBeObservable(cold('(a|)', { + a: [bitstream3, bitstream4] })); }); diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts index 76597a135b..8ed2f9a12e 100644 --- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts @@ -1,3 +1,5 @@ +import { isEqual } from 'lodash'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @@ -6,7 +8,8 @@ import { catchError, filter, first, map, mergeMap, take } from 'rxjs/operators'; import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model'; import { - getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteDataWithNotEmptyPayload, + getFirstSucceededRemoteDataPayload, + getFirstSucceededRemoteDataWithNotEmptyPayload, } from '../../../core/shared/operators'; import { Item } from '../../../core/shared/item.model'; import { followLink } from '../../../shared/utils/follow-link-config.model'; @@ -25,7 +28,8 @@ interface BundleBitstreamsMapEntry { @Component({ selector: 'ds-item-authorizations', - templateUrl: './item-authorizations.component.html' + templateUrl: './item-authorizations.component.html', + styleUrls:['./item-authorizations.component.scss'] }) /** * Component that handles the item Authorizations @@ -36,13 +40,13 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { * A map that contains all bitstream of the item's bundles * @type {Observable>>>} */ - public bundleBitstreamsMap: Map>> = new Map>>(); + public bundleBitstreamsMap: Map = new Map(); /** - * The list of bundle for the item + * The list of all bundles for the item * @type {Observable>} */ - private bundles$: BehaviorSubject = new BehaviorSubject([]); + bundles$: BehaviorSubject = new BehaviorSubject([]); /** * The target editing item @@ -56,15 +60,48 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { */ private subs: Subscription[] = []; + /** + * The size of the bundles to be loaded on demand + * @type {number} + */ + bundlesPerPage = 6; + + /** + * The number of current page + * @type {number} + */ + bundlesPageSize = 1; + + /** + * The flag to show or not the 'Load more' button + * based on the condition if all the bundles are loaded or not + * @type {boolean} + */ + allBundlesLoaded = false; + + /** + * Initial size of loaded bitstreams + * The size of incrementation used in bitstream pagination + */ + bitstreamSize = 4; + + /** + * The size of the loaded bitstremas at a certain moment + * @private + */ + private bitstreamPageSize = 4; + /** * Initialize instance variables * * @param {LinkService} linkService * @param {ActivatedRoute} route + * @param nameService */ constructor( private linkService: LinkService, - private route: ActivatedRoute + private route: ActivatedRoute, + private nameService: DSONameService ) { } @@ -72,16 +109,53 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { * Initialize the component, setting up the bundle and bitstream within the item */ ngOnInit(): void { + this.getBundlesPerItem(); + } + + /** + * Return the item's UUID + */ + getItemUUID(): Observable { + return this.item$.pipe( + map((item: Item) => item.id), + first((UUID: string) => isNotEmpty(UUID)) + ); + } + + /** + * Return the item's name + */ + getItemName(): Observable { + return this.item$.pipe( + map((item: Item) => this.nameService.getName(item)) + ); + } + + /** + * Return all item's bundles + * + * @return an observable that emits all item's bundles + */ + getItemBundles(): Observable { + return this.bundles$.asObservable(); + } + + /** + * Get all bundles per item + * and all the bitstreams per bundle + * @param page number of current page + */ + getBundlesPerItem(page: number = 1) { this.item$ = this.route.data.pipe( map((data) => data.dso), getFirstSucceededRemoteDataWithNotEmptyPayload(), map((item: Item) => this.linkService.resolveLink( item, - followLink('bundles', {}, followLink('bitstreams')) + followLink('bundles', {findListOptions: {currentPage : page, elementsPerPage: this.bundlesPerPage}}, followLink('bitstreams')) )) ) as Observable; - const bundles$: Observable> = this.item$.pipe( + const bundles$: Observable> = this.item$.pipe( filter((item: Item) => isNotEmpty(item.bundles)), mergeMap((item: Item) => item.bundles), getFirstSucceededRemoteDataWithNotEmptyPayload(), @@ -96,37 +170,36 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { take(1), map((list: PaginatedList) => list.page) ).subscribe((bundles: Bundle[]) => { - this.bundles$.next(bundles); + if (isEqual(bundles.length,0) || bundles.length < this.bundlesPerPage) { + this.allBundlesLoaded = true; + } + if (isEqual(page, 1)) { + this.bundles$.next(bundles); + } else { + this.bundles$.next(this.bundles$.getValue().concat(bundles)); + } }), bundles$.pipe( take(1), mergeMap((list: PaginatedList) => list.page), map((bundle: Bundle) => ({ id: bundle.id, bitstreams: this.getBundleBitstreams(bundle) })) ).subscribe((entry: BundleBitstreamsMapEntry) => { - this.bundleBitstreamsMap.set(entry.id, entry.bitstreams); + let bitstreamMapValues: BitstreamMapValue = { + isCollapsed: true, + allBitstreamsLoaded: false, + bitstreams: null + }; + bitstreamMapValues.bitstreams = entry.bitstreams.pipe( + map((b: PaginatedList) => { + bitstreamMapValues.allBitstreamsLoaded = b?.page.length < this.bitstreamSize; + return [...b.page.slice(0, this.bitstreamSize)]; + }) + ); + this.bundleBitstreamsMap.set(entry.id, bitstreamMapValues); }) ); } - /** - * Return the item's UUID - */ - getItemUUID(): Observable { - return this.item$.pipe( - map((item: Item) => item.id), - first((UUID: string) => isNotEmpty(UUID)) - ); - } - - /** - * Return all item's bundles - * - * @return an observable that emits all item's bundles - */ - getItemBundles(): Observable { - return this.bundles$.asObservable(); - } - /** * Return all bundle's bitstreams * @@ -142,6 +215,46 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { ); } + /** + * Changes the collapsible state of the area that contains the bitstream list + * @param bundleId Id of bundle responsible for the requested bitstreams + */ + collapseArea(bundleId: string) { + this.bundleBitstreamsMap.get(bundleId).isCollapsed = !this.bundleBitstreamsMap.get(bundleId).isCollapsed; + } + + /** + * Loads as much bundles as initial value of bundleSize to be displayed + */ + onBundleLoad(){ + this.bundlesPageSize ++; + this.getBundlesPerItem(this.bundlesPageSize); + } + + /** + * Calculates the bitstreams that are going to be loaded on demand, + * based on the number configured on this.bitstreamSize. + * @param bundle parent of bitstreams that are requested to be shown + * @returns Subscription + */ + onBitstreamsLoad(bundle: Bundle) { + return this.getBundleBitstreams(bundle).subscribe((res: PaginatedList) => { + let nextBitstreams = res?.page.slice(this.bitstreamPageSize, this.bitstreamPageSize + this.bitstreamSize); + let bitstreamsToShow = this.bundleBitstreamsMap.get(bundle.id).bitstreams.pipe( + map((existingBits: Bitstream[])=> { + return [... existingBits, ...nextBitstreams]; + }) + ); + this.bitstreamPageSize = this.bitstreamPageSize + this.bitstreamSize; + let bitstreamMapValues: BitstreamMapValue = { + bitstreams: bitstreamsToShow , + isCollapsed: this.bundleBitstreamsMap.get(bundle.id).isCollapsed, + allBitstreamsLoaded: res?.page.length <= this.bitstreamPageSize + }; + this.bundleBitstreamsMap.set(bundle.id, bitstreamMapValues); + }); + } + /** * Unsubscribe from all subscriptions */ @@ -151,3 +264,9 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { .forEach((subscription) => subscription.unsubscribe()); } } + +export interface BitstreamMapValue { + bitstreams: Observable; + isCollapsed: boolean; + allBitstreamsLoaded: boolean; +} diff --git a/src/app/shared/resource-policies/resource-policies.component.html b/src/app/shared/resource-policies/resource-policies.component.html index 0a1ccf7952..d1fd9266b1 100644 --- a/src/app/shared/resource-policies/resource-policies.component.html +++ b/src/app/shared/resource-policies/resource-policies.component.html @@ -4,9 +4,15 @@
- {{ 'resource-policies.table.headers.title.for.' + resourceType | translate }} {{resourceUUID}} + + {{ 'resource-policies.table.headers.title.for.' + resourceType | translate }} + {{resourceName}} + + ({{resourceUUID}}) + +
- -