65717: Edit-bitstreams fetch all bundles and expandable bitstream list

This commit is contained in:
Kristof De Langhe
2019-10-23 17:04:23 +02:00
parent cbbc776922
commit 7e3ba86ccc
8 changed files with 138 additions and 20 deletions

View File

@@ -278,6 +278,9 @@
"item.bitstreams.upload.title": "Upload bitstream",
"item.edit.bitstreams.bundle.edit.buttons.upload": "Upload",
"item.edit.bitstreams.bundle.displaying": "Currently displaying {{ amount }} bitstreams of {{ total }}.",
"item.edit.bitstreams.bundle.load.all": "Load all ({{ total }})",
"item.edit.bitstreams.bundle.load.more": "Load more",
"item.edit.bitstreams.bundle.name": "BUNDLE: {{ name }}",
"item.edit.bitstreams.discard-button": "Discard",
"item.edit.bitstreams.edit.buttons.download": "Download",
@@ -490,6 +493,7 @@
"journalvolume.page.volume": "Volume",
"loading.bitstream": "Loading bitstream...",
"loading.bitstreams": "Loading bitstreams...",
"loading.browse-by": "Loading items...",
"loading.browse-by-page": "Loading page...",
"loading.collection": "Loading collection...",

View File

@@ -20,6 +20,7 @@ import { Item } from '../../../core/shared/item.model';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list';
import { Bundle } from '../../../core/shared/bundle.model';
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
@Component({
selector: 'ds-item-bitstreams',
@@ -36,6 +37,15 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
*/
bundles$: Observable<Bundle[]>;
/**
* The page options to use for fetching the bundles
*/
bundlesOptions = {
id: 'bundles-pagination-options',
currentPage: 1,
pageSize: 9999
} as any;
/**
* A subscription that checks when the item is deleted in cache and reloads the item by sending a new request
* This is used to update the item in cache after bitstreams are deleted
@@ -70,7 +80,7 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
* Actions to perform after the item has been initialized
*/
postItemInit(): void {
this.bundles$ = this.item.bundles.pipe(
this.bundles$ = this.itemService.getBundles(this.item.id, new PaginatedSearchOptions({pagination: this.bundlesOptions})).pipe(
getSucceededRemoteData(),
getRemoteDataPayload(),
map((bundlePage: PaginatedList<Bundle>) => bundlePage.page)

View File

@@ -28,5 +28,22 @@
</button>
</ds-item-edit-bitstream>
</div>
<ng-container *ngVar="(bitstreamsRD$ | async) as bitstreamsRD">
<div class="row" *ngIf="bitstreamsRD?.payload?.elementsPerPage < bitstreamsRD?.payload?.totalElements">
<ng-container *ngVar="(isLoadingMore$ | async) as loading">
<div class="col-6 col-sm-7 col-md-8 col-lg-9 row-element" *ngIf="!loading">
<span class="font-italic">{{'item.edit.bitstreams.bundle.displaying' | translate:{ amount: bitstreamsRD?.payload?.elementsPerPage, total: bitstreamsRD?.payload?.totalElements } }}</span>
</div>
<div class="col-6 col-sm-5 col-md-4 col-lg-3 row-element text-center" *ngIf="!loading">
<a [routerLink]="[]" (click)="loadMore()">{{'item.edit.bitstreams.bundle.load.more' | translate}}</a>
<span> | </span>
<a [routerLink]="[]" (click)="loadAll()">{{'item.edit.bitstreams.bundle.load.all' | translate:{ total: bitstreamsRD?.payload?.totalElements } }}</a>
</div>
<div class="col-12 row-element text-center" *ngIf="loading">
<span class="font-italic">{{'loading.bitstreams' | translate}}</span>
</div>
</ng-container>
</div>
</ng-container>
</div>
</ng-template>

View File

@@ -4,10 +4,16 @@ import { ObjectUpdatesService } from '../../../../core/data/object-updates/objec
import { Observable } from 'rxjs/internal/Observable';
import { FieldUpdates } from '../../../../core/data/object-updates/object-updates.reducer';
import { toBitstreamsArray } from '../../../../core/shared/item-bitstreams-utils';
import { switchMap, tap } from 'rxjs/operators';
import { map, switchMap, tap } from 'rxjs/operators';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { Item } from '../../../../core/shared/item.model';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { RemoteData } from '../../../../core/data/remote-data';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { BundleDataService } from '../../../../core/data/bundle-data.service';
import { PaginatedSearchOptions } from '../../../../+search-page/paginated-search-options.model';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
import { combineLatest as observableCombineLatest } from 'rxjs';
@Component({
selector: 'ds-item-edit-bitstream-bundle',
@@ -39,21 +45,60 @@ export class ItemEditBitstreamBundleComponent implements OnInit {
*/
@Input() url: string;
/**
* The bitstreams within this bundle retrieved from the REST API
*/
bitstreamsRD$: Observable<RemoteData<PaginatedList<Bitstream>>>;
/**
* The updates to the current bundle
*/
updates$: Observable<FieldUpdates>;
/**
* The amount of one bitstreams one "batch" resembles
* The user is able to increase the amount of bitstreams displayed per bundle by this batch size until all are shown
*/
batchSize = 10;
/**
* The page options to use for fetching the bitstreams
*/
bitstreamsOptions = {
id: 'bitstreams-pagination-options',
currentPage: 1,
pageSize: this.batchSize
} as any;
/**
* The current amount of bitstreams to display for this bundle
* Starts off with just one batch
*/
currentSize$ = new BehaviorSubject<number>(this.batchSize);
/**
* Are we currently loading more bitstreams?
*/
isLoadingMore$: Observable<boolean>;
constructor(private objectUpdatesService: ObjectUpdatesService,
private bundleService: BundleDataService,
private viewContainerRef: ViewContainerRef) {
}
ngOnInit(): void {
this.updates$ = this.bundle.bitstreams.pipe(
this.bitstreamsRD$ = this.currentSize$.pipe(
switchMap((size: number) => this.bundleService.getBitstreams(this.bundle.id,
new PaginatedSearchOptions({pagination: Object.assign({}, this.bitstreamsOptions, { pageSize: size })})))
);
this.updates$ = this.bitstreamsRD$.pipe(
toBitstreamsArray(),
tap((bitstreams: Bitstream[]) => this.objectUpdatesService.initialize(this.bundle.self, bitstreams, new Date(), true)),
switchMap((bitstreams: Bitstream[]) => this.objectUpdatesService.getFieldUpdatesByCustomOrder(this.bundle.self, bitstreams))
);
this.isLoadingMore$ = observableCombineLatest(this.currentSize$, this.bitstreamsRD$).pipe(
map(([size, bitstreamsRD]: [number, RemoteData<PaginatedList<Bitstream>>]) => size > bitstreamsRD.payload.page.length)
);
this.viewContainerRef.createEmbeddedView(this.bundleView);
}
@@ -65,4 +110,18 @@ export class ItemEditBitstreamBundleComponent implements OnInit {
drop(event: CdkDragDrop<any>) {
this.objectUpdatesService.saveMoveFieldUpdate(this.bundle.self, event.previousIndex, event.currentIndex);
}
/**
* Load more bitstreams (current size + batchSize)
*/
loadMore() {
this.currentSize$.next(this.currentSize$.value + this.batchSize);
}
/**
* Load all bitstreams
*/
loadAll() {
this.currentSize$.next(9999);
}
}

View File

@@ -11,9 +11,13 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { FindAllOptions } from './request.models';
import { FindAllOptions, GetRequest } from './request.models';
import { Observable } from 'rxjs/internal/Observable';
import { switchMap } from 'rxjs/operators';
import { map, switchMap, take } from 'rxjs/operators';
import { PaginatedSearchOptions } from '../../+search-page/paginated-search-options.model';
import { RemoteData } from './remote-data';
import { PaginatedList } from './paginated-list';
import { Bitstream } from '../shared/bitstream.model';
/**
* A service responsible for fetching/sending data from/to the REST API on the bundles endpoint
@@ -55,4 +59,23 @@ export class BundleDataService extends DataService<Bundle> {
switchMap((href: string) => this.halService.getEndpoint(this.bitstreamsEndpoint, `${href}/${bundleId}`))
);
}
/**
* Get a bundle's bitstreams using paginated search options
* @param bundleId The bundle's ID
* @param searchOptions The search options to use
*/
getBitstreams(bundleId: string, searchOptions?: PaginatedSearchOptions): Observable<RemoteData<PaginatedList<Bitstream>>> {
const hrefObs = this.getBitstreamsEndpoint(bundleId).pipe(
map((href) => searchOptions ? searchOptions.toRestUrl(href) : href)
);
hrefObs.pipe(
take(1)
).subscribe((href) => {
const request = new GetRequest(this.requestService.generateRequestId(), href);
this.requestService.configure(request);
});
return this.rdbService.buildList<Bitstream>(hrefObs);
}
}

View File

@@ -40,6 +40,7 @@ import { RemoteData } from './remote-data';
import { PaginatedList } from './paginated-list';
import { PaginatedSearchOptions } from '../../+search-page/paginated-search-options.model';
import { Bitstream } from '../shared/bitstream.model';
import { Bundle } from '../shared/bundle.model';
@Injectable()
export class ItemDataService extends DataService<Item> {
@@ -214,22 +215,22 @@ export class ItemDataService extends DataService<Item> {
}
/**
* Get the endpoint for an item's bitstreams
* Get the endpoint for an item's bundles
* @param itemId
*/
public getBitstreamsEndpoint(itemId: string): Observable<string> {
public getBundlesEndpoint(itemId: string): Observable<string> {
return this.halService.getEndpoint(this.linkPath).pipe(
switchMap((url: string) => this.halService.getEndpoint('bitstreams', `${url}/${itemId}`))
switchMap((url: string) => this.halService.getEndpoint('bundles', `${url}/${itemId}`))
);
}
/**
* Get an item's bitstreams using paginated search options
* Get an item's bundles using paginated search options
* @param itemId The item's ID
* @param searchOptions The search options to use
*/
public getBitstreams(itemId: string, searchOptions?: PaginatedSearchOptions): Observable<RemoteData<PaginatedList<Bitstream>>> {
const hrefObs = this.getBitstreamsEndpoint(itemId).pipe(
public getBundles(itemId: string, searchOptions?: PaginatedSearchOptions): Observable<RemoteData<PaginatedList<Bundle>>> {
const hrefObs = this.getBundlesEndpoint(itemId).pipe(
map((href) => searchOptions ? searchOptions.toRestUrl(href) : href)
);
hrefObs.pipe(
@@ -239,7 +240,7 @@ export class ItemDataService extends DataService<Item> {
this.requestService.configure(request);
});
return this.rdbService.buildList<Bitstream>(hrefObs);
return this.rdbService.buildList<Bundle>(hrefObs);
}
/**

View File

@@ -138,6 +138,7 @@ export class ObjectUpdatesService {
const objectUpdates = this.getObjectEntry(url);
return objectUpdates.pipe(map((objectEntry) => {
const fieldUpdates: FieldUpdates = {};
if (hasValue(objectEntry)) {
for (const uuid of objectEntry.customOrder.newOrder) {
let fieldUpdate = objectEntry.fieldUpdates[uuid];
if (isEmpty(fieldUpdate)) {
@@ -146,6 +147,7 @@ export class ObjectUpdatesService {
}
fieldUpdates[uuid] = fieldUpdate;
}
}
return fieldUpdates;
}))
}

View File

@@ -1,5 +1,5 @@
import { PipeTransform, Pipe } from '@angular/core';
import { hasValue } from '../empty.util';
import { hasValue, isNotEmpty } from '../empty.util';
@Pipe({name: 'dsObjectValues'})
/**
@@ -13,7 +13,9 @@ export class ObjectValuesPipe implements PipeTransform {
*/
transform(value, args:string[]): any {
const values = [];
if (isNotEmpty(value)) {
Object.values(value).forEach((v) => values.push(v));
}
return values;
}
}