mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
65717: Edit-bitstreams fetch all bundles and expandable bitstream list
This commit is contained in:
@@ -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...",
|
||||
|
@@ -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)
|
||||
|
@@ -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>
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -138,13 +138,15 @@ export class ObjectUpdatesService {
|
||||
const objectUpdates = this.getObjectEntry(url);
|
||||
return objectUpdates.pipe(map((objectEntry) => {
|
||||
const fieldUpdates: FieldUpdates = {};
|
||||
for (const uuid of objectEntry.customOrder.newOrder) {
|
||||
let fieldUpdate = objectEntry.fieldUpdates[uuid];
|
||||
if (isEmpty(fieldUpdate)) {
|
||||
const identifiable = initialFields.find((object: Identifiable) => object.uuid === uuid);
|
||||
fieldUpdate = { field: identifiable, changeType: undefined };
|
||||
if (hasValue(objectEntry)) {
|
||||
for (const uuid of objectEntry.customOrder.newOrder) {
|
||||
let fieldUpdate = objectEntry.fieldUpdates[uuid];
|
||||
if (isEmpty(fieldUpdate)) {
|
||||
const identifiable = initialFields.find((object: Identifiable) => object.uuid === uuid);
|
||||
fieldUpdate = {field: identifiable, changeType: undefined};
|
||||
}
|
||||
fieldUpdates[uuid] = fieldUpdate;
|
||||
}
|
||||
fieldUpdates[uuid] = fieldUpdate;
|
||||
}
|
||||
return fieldUpdates;
|
||||
}))
|
||||
|
@@ -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 = [];
|
||||
Object.values(value).forEach((v) => values.push(v));
|
||||
if (isNotEmpty(value)) {
|
||||
Object.values(value).forEach((v) => values.push(v));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user