73249: Fix issues with bitstream pagination on item pages.

This commit is contained in:
Yana De Pauw
2020-09-21 13:18:41 +02:00
parent 6960597381
commit dfbbd98862
7 changed files with 159 additions and 109 deletions

View File

@@ -1,87 +1,87 @@
<ds-metadata-field-wrapper [label]="label | translate">
<div *ngVar="(originals$ | async)?.payload as originals">
<h5 class="simple-view-element-header">{{"item.page.filesection.original.bundle" | translate}}</h5>
<ds-pagination *ngIf="originals?.page?.length > 0"
[hideGear]="true"
[hidePagerWhenSinglePage]="true"
[paginationOptions]="originalOptions"
[pageInfoState]="originals"
[collectionSize]="originals?.totalElements"
[disableRouteParameterUpdate]="true"
(pageChange)="switchOriginalPage($event)">
<div *ngIf="hasValuesInBundle(originals)">
<h5 class="simple-view-element-header">{{"item.page.filesection.original.bundle" | translate}}</h5>
<ds-pagination *ngIf="originals?.page?.length > 0"
[hideGear]="true"
[hidePagerWhenSinglePage]="true"
[paginationOptions]="originalOptions"
[pageInfoState]="originals"
[collectionSize]="originals?.totalElements"
[disableRouteParameterUpdate]="true"
(pageChange)="switchOriginalPage($event)">
<div class="file-section row" *ngFor="let file of originals?.page;">
<div class="col-3">
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
</div>
<div class="col-7">
<dl class="row">
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
<dd class="col-md-8">{{file.name}}</dd>
<div class="file-section row" *ngFor="let file of originals?.page;">
<div class="col-3">
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
</dl>
</div>
<div class="col-2">
<ds-file-download-link [href]="file._links.content.href" [download]="file.name">
{{"item.page.filesection.download" | translate}}
</ds-file-download-link>
</div>
</div>
<div class="col-7">
<dl class="row">
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
<dd class="col-md-8">{{file.name}}</dd>
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
</dl>
</div>
<div class="col-2">
<ds-file-download-link [href]="file._links.content.href" [download]="file.name">
{{"item.page.filesection.download" | translate}}
</ds-file-download-link>
</div>
</div>
</ds-pagination>
</ds-pagination>
</div>
</div>
<div *ngVar="(licenses$ | async)?.payload as licenses">
<h5 class="simple-view-element-header">{{"item.page.filesection.license.bundle" | translate}}</h5>
<ds-pagination *ngIf="licenses?.page?.length > 0"
[hideGear]="true"
[hidePagerWhenSinglePage]="true"
[paginationOptions]="licenseOptions"
[pageInfoState]="licenses"
[collectionSize]="licenses?.totalElements"
[disableRouteParameterUpdate]="true"
(pageChange)="switchLicensePage($event)">
<div *ngIf="hasValuesInBundle(licenses)">
<h5 class="simple-view-element-header">{{"item.page.filesection.license.bundle" | translate}}</h5>
<ds-pagination *ngIf="licenses?.page?.length > 0"
[hideGear]="true"
[hidePagerWhenSinglePage]="true"
[paginationOptions]="licenseOptions"
[pageInfoState]="licenses"
[collectionSize]="licenses?.totalElements"
[disableRouteParameterUpdate]="true"
(pageChange)="switchLicensePage($event)">
<div class="file-section row" *ngFor="let file of licenses?.page;">
<div class="col-3">
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
</div>
<div class="col-7">
<dl class="row">
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
<dd class="col-md-8">{{file.name}}</dd>
<div class="file-section row" *ngFor="let file of licenses?.page;">
<div class="col-3">
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
</dl>
</div>
<div class="col-2">
<ds-file-download-link [href]="file._links.content.href" [download]="file.name">
{{"item.page.filesection.download" | translate}}
</ds-file-download-link>
</div>
</div>
<div class="col-7">
<dl class="row">
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
<dd class="col-md-8">{{file.name}}</dd>
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
</dl>
</div>
<div class="col-2">
<ds-file-download-link [href]="file._links.content.href" [download]="file.name">
{{"item.page.filesection.download" | translate}}
</ds-file-download-link>
</div>
</div>
</ds-pagination>
</ds-pagination>
</div>
</div>
</ds-metadata-field-wrapper>

View File

@@ -14,6 +14,8 @@ import {Bitstream} from '../../../../core/shared/bitstream.model';
import {of as observableOf} from 'rxjs';
import {MockBitstreamFormat1} from '../../../../shared/mocks/item.mock';
import {By} from '@angular/platform-browser';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
describe('FullFileSectionComponent', () => {
let comp: FullFileSectionComponent;
@@ -61,7 +63,8 @@ describe('FullFileSectionComponent', () => {
}), BrowserAnimationsModule],
declarations: [FullFileSectionComponent, VarDirective, FileSizePipe, MetadataFieldWrapperComponent],
providers: [
{provide: BitstreamDataService, useValue: bitstreamDataService}
{provide: BitstreamDataService, useValue: bitstreamDataService},
{provide: NotificationsService, useValue: new NotificationsServiceStub()}
],
schemas: [NO_ERRORS_SCHEMA]

View File

@@ -10,6 +10,10 @@ import { PaginationComponentOptions } from '../../../../shared/pagination/pagina
import { PaginatedList } from '../../../../core/data/paginated-list';
import { RemoteData } from '../../../../core/data/remote-data';
import { switchMap } from 'rxjs/operators';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
import { hasValue, isEmpty } from '../../../../shared/empty.util';
import { tap } from 'rxjs/internal/operators/tap';
/**
* This component renders the file section of the item
@@ -31,14 +35,14 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
licenses$: Observable<RemoteData<PaginatedList<Bitstream>>>;
pageSize = 5;
originalOptions = Object.assign(new PaginationComponentOptions(),{
originalOptions = Object.assign(new PaginationComponentOptions(), {
id: 'original-bitstreams-options',
currentPage: 1,
pageSize: this.pageSize
});
originalCurrentPage$ = new BehaviorSubject<number>(1);
licenseOptions = Object.assign(new PaginationComponentOptions(),{
licenseOptions = Object.assign(new PaginationComponentOptions(), {
id: 'license-bitstreams-options',
currentPage: 1,
pageSize: this.pageSize
@@ -46,9 +50,11 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
licenseCurrentPage$ = new BehaviorSubject<number>(1);
constructor(
bitstreamDataService: BitstreamDataService
bitstreamDataService: BitstreamDataService,
protected notificationsService: NotificationsService,
protected translateService: TranslateService
) {
super(bitstreamDataService);
super(bitstreamDataService, notificationsService, translateService);
}
ngOnInit(): void {
@@ -57,21 +63,33 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
initialize(): void {
this.originals$ = this.originalCurrentPage$.pipe(
switchMap((pageNumber: number) => this.bitstreamDataService.findAllByItemAndBundleName(
this.item,
'ORIGINAL',
{ elementsPerPage: this.pageSize, currentPage: pageNumber },
followLink( 'format')
))
switchMap((pageNumber: number) => this.bitstreamDataService.findAllByItemAndBundleName(
this.item,
'ORIGINAL',
{elementsPerPage: this.pageSize, currentPage: pageNumber},
followLink('format')
)),
tap((rd: RemoteData<PaginatedList<Bitstream>>) => {
if (hasValue(rd.error)) {
this.notificationsService.error(this.translateService.get('file-section.error.header'), `${rd.error.statusCode} ${rd.error.message}`);
}
}
)
);
this.licenses$ = this.licenseCurrentPage$.pipe(
switchMap((pageNumber: number) => this.bitstreamDataService.findAllByItemAndBundleName(
this.item,
'LICENSE',
{ elementsPerPage: this.pageSize, currentPage: pageNumber },
followLink( 'format')
))
switchMap((pageNumber: number) => this.bitstreamDataService.findAllByItemAndBundleName(
this.item,
'LICENSE',
{elementsPerPage: this.pageSize, currentPage: pageNumber},
followLink('format')
)),
tap((rd: RemoteData<PaginatedList<Bitstream>>) => {
if (hasValue(rd.error)) {
this.notificationsService.error(this.translateService.get('file-section.error.header'), `${rd.error.statusCode} ${rd.error.message}`);
}
}
)
);
}
@@ -93,4 +111,9 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
this.licenseOptions.currentPage = page;
this.licenseCurrentPage$.next(page);
}
hasValuesInBundle(bundle: PaginatedList<Bitstream>) {
console.log(bundle, hasValue(bundle), hasValue(bundle) && !isEmpty(bundle.page));
return hasValue(bundle) && !isEmpty(bundle.page);
}
}

View File

@@ -15,6 +15,8 @@ import {FileSizePipe} from '../../../../shared/utils/file-size-pipe';
import {PageInfo} from '../../../../core/shared/page-info.model';
import {MetadataFieldWrapperComponent} from '../../../field-components/metadata-field-wrapper/metadata-field-wrapper.component';
import {createPaginatedList} from '../../../../shared/testing/utils.test';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
describe('FileSectionComponent', () => {
let comp: FileSectionComponent;
@@ -62,7 +64,8 @@ describe('FileSectionComponent', () => {
}), BrowserAnimationsModule],
declarations: [FileSectionComponent, VarDirective, FileSizePipe, MetadataFieldWrapperComponent],
providers: [
{provide: BitstreamDataService, useValue: bitstreamDataService}
{provide: BitstreamDataService, useValue: bitstreamDataService},
{provide: NotificationsService, useValue: new NotificationsServiceStub()}
],
schemas: [NO_ERRORS_SCHEMA]

View File

@@ -4,10 +4,12 @@ import { BitstreamDataService } from '../../../../core/data/bitstream-data.servi
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { Item } from '../../../../core/shared/item.model';
import { filter, takeWhile } from 'rxjs/operators';
import { filter, take } from 'rxjs/operators';
import { RemoteData } from '../../../../core/data/remote-data';
import { hasNoValue, hasValue } from '../../../../shared/empty.util';
import { hasValue } from '../../../../shared/empty.util';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
/**
* This component renders the file section of the item
@@ -36,7 +38,9 @@ export class FileSectionComponent implements OnInit {
pageSize = 5;
constructor(
protected bitstreamDataService: BitstreamDataService
protected bitstreamDataService: BitstreamDataService,
protected notificationsService: NotificationsService,
protected translateService: TranslateService
) {
}
@@ -58,14 +62,22 @@ export class FileSectionComponent implements OnInit {
} else {
this.currentPage++;
}
this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'ORIGINAL', { currentPage: this.currentPage, elementsPerPage: this.pageSize }).pipe(
filter((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => hasValue(bitstreamsRD)),
takeWhile((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => hasNoValue(bitstreamsRD.payload) && hasNoValue(bitstreamsRD.error), true)
this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'ORIGINAL', {
currentPage: this.currentPage,
elementsPerPage: this.pageSize
}).pipe(
filter((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => hasValue(bitstreamsRD)),
filter((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => hasValue(!bitstreamsRD.isLoading)),
take(1),
).subscribe((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => {
const current: Bitstream[] = this.bitstreams$.getValue();
this.bitstreams$.next([...current, ...bitstreamsRD.payload.page]);
this.isLoading = false;
this.isLastPage = this.currentPage === bitstreamsRD.payload.totalPages;
if (bitstreamsRD.error) {
this.notificationsService.error(this.translateService.get('file-section.error.header'), `${bitstreamsRD.error.statusCode} ${bitstreamsRD.error.message}`);
} else if (hasValue(bitstreamsRD.payload)) {
const current: Bitstream[] = this.bitstreams$.getValue();
this.bitstreams$.next([...current, ...bitstreamsRD.payload.page]);
this.isLoading = false;
this.isLastPage = this.currentPage === bitstreamsRD.payload.totalPages;
}
});
}
}

View File

@@ -30,6 +30,8 @@ import { RestResponse } from '../cache/response.models';
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
import { configureRequest, getResponseFromEntry } from '../shared/operators';
import { combineLatest as observableCombineLatest } from 'rxjs';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { PageInfo } from '../shared/page-info.model';
/**
* A service to retrieve {@link Bitstream}s from the REST API
@@ -165,8 +167,10 @@ export class BitstreamDataService extends DataService<Bitstream> {
public findAllByItemAndBundleName(item: Item, bundleName: string, options?: FindListOptions, ...linksToFollow: Array<FollowLinkConfig<Bitstream>>): Observable<RemoteData<PaginatedList<Bitstream>>> {
return this.bundleService.findByItemAndName(item, bundleName).pipe(
switchMap((bundleRD: RemoteData<Bundle>) => {
if (hasValue(bundleRD.payload)) {
if (bundleRD.hasSucceeded && hasValue(bundleRD.payload)) {
return this.findAllByBundle(bundleRD.payload, options, ...linksToFollow);
} else if (!bundleRD.hasSucceeded && bundleRD.error.statusCode === 404) {
return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), []))
} else {
return [bundleRD as any];
}

View File

@@ -22,6 +22,7 @@ import { FindListOptions, GetRequest } from './request.models';
import { RequestService } from './request.service';
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
import { Bitstream } from '../shared/bitstream.model';
import { RemoteDataError } from './remote-data-error';
/**
* A service to retrieve {@link Bundle}s from the REST API
@@ -71,13 +72,17 @@ export class BundleDataService extends DataService<Bundle> {
if (hasValue(rd.payload) && hasValue(rd.payload.page)) {
const matchingBundle = rd.payload.page.find((bundle: Bundle) =>
bundle.name === bundleName);
return new RemoteData(
false,
false,
true,
undefined,
matchingBundle
);
if (hasValue(matchingBundle)) {
return new RemoteData(
false,
false,
true,
undefined,
matchingBundle
);
} else {
return new RemoteData(false, false, false, new RemoteDataError(404, 'Not found', `The bundle with name ${bundleName} was not found.` ))
}
} else {
return rd as any;
}