mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge remote-tracking branch 'upstream/main' into w2p-80369_Use-thumbnail-links-for-Items-and-Bitstreams
This commit is contained in:
@@ -6,21 +6,30 @@
|
|||||||
</button>
|
</button>
|
||||||
</h5>
|
</h5>
|
||||||
<ng-container *ngVar="updates$ | async as updates">
|
<ng-container *ngVar="updates$ | async as updates">
|
||||||
<ng-container *ngIf="updates">
|
<ng-container *ngIf="updates && !(loading$ | async)">
|
||||||
<ng-container *ngVar="updates | dsObjectValues as updateValues">
|
<ng-container *ngVar="updates | dsObjectValues as updateValues">
|
||||||
<ds-edit-relationship *ngFor="let updateValue of updateValues; trackBy: trackUpdate"
|
<ds-pagination
|
||||||
class="relationship-row d-block alert"
|
[paginationOptions]="paginationConfig"
|
||||||
[fieldUpdate]="updateValue || {}"
|
[pageInfoState]="(relationshipsRd$ | async)?.payload?.pageInfo"
|
||||||
[url]="url"
|
[collectionSize]="(relationshipsRd$ | async)?.payload?.totalElements + (this.nbAddedFields$ | async)"
|
||||||
[editItem]="item"
|
[hideGear]="true"
|
||||||
[ngClass]="{
|
[hidePagerWhenSinglePage]="true">
|
||||||
|
<div class="my-2">
|
||||||
|
<ds-edit-relationship *ngFor="let updateValue of updateValues; trackBy: trackUpdate"
|
||||||
|
class="relationship-row d-block alert"
|
||||||
|
[fieldUpdate]="updateValue || {}"
|
||||||
|
[url]="url"
|
||||||
|
[editItem]="item"
|
||||||
|
[ngClass]="{
|
||||||
'alert-success': updateValue.changeType === 1,
|
'alert-success': updateValue.changeType === 1,
|
||||||
'alert-warning': updateValue.changeType === 0,
|
'alert-warning': updateValue.changeType === 0,
|
||||||
'alert-danger': updateValue.changeType === 2
|
'alert-danger': updateValue.changeType === 2
|
||||||
}">
|
}">
|
||||||
</ds-edit-relationship>
|
</ds-edit-relationship>
|
||||||
<div *ngIf="updateValues.length === 0">{{"item.edit.relationships.no-relationships" | translate}}</div>
|
</div>
|
||||||
|
</ds-pagination>
|
||||||
|
<div *ngIf="updateValues.length === 0">{{"item.edit.relationships.no-relationships" | translate}}</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ds-loading *ngIf="!updates"></ds-loading>
|
<ds-loading *ngIf="loading$ | async"></ds-loading>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -16,6 +16,12 @@ import { SharedModule } from '../../../../shared/shared.module';
|
|||||||
import { EditRelationshipListComponent } from './edit-relationship-list.component';
|
import { EditRelationshipListComponent } from './edit-relationship-list.component';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
||||||
import { createPaginatedList } from '../../../../shared/testing/utils.test';
|
import { createPaginatedList } from '../../../../shared/testing/utils.test';
|
||||||
|
import { PaginationService } from '../../../../core/pagination/pagination.service';
|
||||||
|
import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub';
|
||||||
|
import { HostWindowService } from '../../../../shared/host-window.service';
|
||||||
|
import { HostWindowServiceStub } from '../../../../shared/testing/host-window-service.stub';
|
||||||
|
import { PaginationComponent } from '../../../../shared/pagination/pagination.component';
|
||||||
|
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
|
||||||
|
|
||||||
let comp: EditRelationshipListComponent;
|
let comp: EditRelationshipListComponent;
|
||||||
let fixture: ComponentFixture<EditRelationshipListComponent>;
|
let fixture: ComponentFixture<EditRelationshipListComponent>;
|
||||||
@@ -25,6 +31,8 @@ let linkService;
|
|||||||
let objectUpdatesService;
|
let objectUpdatesService;
|
||||||
let relationshipService;
|
let relationshipService;
|
||||||
let selectableListService;
|
let selectableListService;
|
||||||
|
let paginationService;
|
||||||
|
let hostWindowService;
|
||||||
|
|
||||||
const url = 'http://test-url.com/test-url';
|
const url = 'http://test-url.com/test-url';
|
||||||
|
|
||||||
@@ -37,9 +45,21 @@ let fieldUpdate1;
|
|||||||
let fieldUpdate2;
|
let fieldUpdate2;
|
||||||
let relationships;
|
let relationships;
|
||||||
let relationshipType;
|
let relationshipType;
|
||||||
|
let paginationOptions;
|
||||||
|
|
||||||
describe('EditRelationshipListComponent', () => {
|
describe('EditRelationshipListComponent', () => {
|
||||||
|
|
||||||
|
const resetComponent = () => {
|
||||||
|
fixture = TestBed.createComponent(EditRelationshipListComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
de = fixture.debugElement;
|
||||||
|
comp.item = item;
|
||||||
|
comp.itemType = entityType;
|
||||||
|
comp.url = url;
|
||||||
|
comp.relationshipType = relationshipType;
|
||||||
|
fixture.detectChanges();
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
|
|
||||||
entityType = Object.assign(new ItemType(), {
|
entityType = Object.assign(new ItemType(), {
|
||||||
@@ -63,6 +83,12 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
rightwardType: 'isPublicationOfAuthor',
|
rightwardType: 'isPublicationOfAuthor',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
paginationOptions = Object.assign(new PaginationComponentOptions(), {
|
||||||
|
id: `er${relationshipType.id}`,
|
||||||
|
pageSize: 5,
|
||||||
|
currentPage: 1,
|
||||||
|
});
|
||||||
|
|
||||||
author1 = Object.assign(new Item(), {
|
author1 = Object.assign(new Item(), {
|
||||||
id: 'author1',
|
id: 'author1',
|
||||||
uuid: 'author1'
|
uuid: 'author1'
|
||||||
@@ -141,6 +167,10 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
resolveLinks: () => null,
|
resolveLinks: () => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
paginationService = new PaginationServiceStub(paginationOptions);
|
||||||
|
|
||||||
|
hostWindowService = new HostWindowServiceStub(1200);
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [SharedModule, TranslateModule.forRoot()],
|
imports: [SharedModule, TranslateModule.forRoot()],
|
||||||
declarations: [EditRelationshipListComponent],
|
declarations: [EditRelationshipListComponent],
|
||||||
@@ -149,22 +179,15 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
{ provide: RelationshipService, useValue: relationshipService },
|
{ provide: RelationshipService, useValue: relationshipService },
|
||||||
{ provide: SelectableListService, useValue: selectableListService },
|
{ provide: SelectableListService, useValue: selectableListService },
|
||||||
{ provide: LinkService, useValue: linkService },
|
{ provide: LinkService, useValue: linkService },
|
||||||
|
{ provide: PaginationService, useValue: paginationService },
|
||||||
|
{ provide: HostWindowService, useValue: hostWindowService },
|
||||||
], schemas: [
|
], schemas: [
|
||||||
NO_ERRORS_SCHEMA
|
NO_ERRORS_SCHEMA
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
resetComponent();
|
||||||
fixture = TestBed.createComponent(EditRelationshipListComponent);
|
}));
|
||||||
comp = fixture.componentInstance;
|
|
||||||
de = fixture.debugElement;
|
|
||||||
comp.item = item;
|
|
||||||
comp.itemType = entityType;
|
|
||||||
comp.url = url;
|
|
||||||
comp.relationshipType = relationshipType;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('changeType is REMOVE', () => {
|
describe('changeType is REMOVE', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -176,4 +199,82 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
expect(element.classList).toContain('alert-danger');
|
expect(element.classList).toContain('alert-danger');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('pagination component', () => {
|
||||||
|
let paginationComp: PaginationComponent;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
paginationComp = de.query(By.css('ds-pagination')).componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should receive the correct pagination config', () => {
|
||||||
|
expect(paginationComp.paginationOptions).toEqual(paginationOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should receive correct collection size', () => {
|
||||||
|
expect(paginationComp.collectionSize).toEqual(relationships.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('relationshipService.getItemRelationshipsByLabel', () => {
|
||||||
|
it('should receive the correct pagination info', () => {
|
||||||
|
expect(relationshipService.getItemRelationshipsByLabel).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
const callArgs = relationshipService.getItemRelationshipsByLabel.calls.mostRecent().args;
|
||||||
|
const findListOptions = callArgs[2];
|
||||||
|
|
||||||
|
expect(findListOptions.elementsPerPage).toEqual(paginationOptions.pageSize);
|
||||||
|
expect(findListOptions.currentPage).toEqual(paginationOptions.currentPage);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the publication is on the left side of the relationship', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
relationshipType = Object.assign(new RelationshipType(), {
|
||||||
|
id: '1',
|
||||||
|
uuid: '1',
|
||||||
|
leftType: createSuccessfulRemoteDataObject$(entityType), // publication
|
||||||
|
rightType: createSuccessfulRemoteDataObject$(relatedEntityType), // author
|
||||||
|
leftwardType: 'isAuthorOfPublication',
|
||||||
|
rightwardType: 'isPublicationOfAuthor',
|
||||||
|
});
|
||||||
|
relationshipService.getItemRelationshipsByLabel.calls.reset();
|
||||||
|
resetComponent();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch isAuthorOfPublication', () => {
|
||||||
|
expect(relationshipService.getItemRelationshipsByLabel).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
const callArgs = relationshipService.getItemRelationshipsByLabel.calls.mostRecent().args;
|
||||||
|
const label = callArgs[1];
|
||||||
|
|
||||||
|
expect(label).toEqual('isAuthorOfPublication');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the publication is on the right side of the relationship', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
relationshipType = Object.assign(new RelationshipType(), {
|
||||||
|
id: '1',
|
||||||
|
uuid: '1',
|
||||||
|
leftType: createSuccessfulRemoteDataObject$(relatedEntityType), // author
|
||||||
|
rightType: createSuccessfulRemoteDataObject$(entityType), // publication
|
||||||
|
leftwardType: 'isPublicationOfAuthor',
|
||||||
|
rightwardType: 'isAuthorOfPublication',
|
||||||
|
});
|
||||||
|
relationshipService.getItemRelationshipsByLabel.calls.reset();
|
||||||
|
resetComponent();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch isAuthorOfPublication', () => {
|
||||||
|
expect(relationshipService.getItemRelationshipsByLabel).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
const callArgs = relationshipService.getItemRelationshipsByLabel.calls.mostRecent().args;
|
||||||
|
const label = callArgs[1];
|
||||||
|
|
||||||
|
expect(label).toEqual('isAuthorOfPublication');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,9 +1,14 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { LinkService } from '../../../../core/cache/builders/link.service';
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
|
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
|
||||||
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
||||||
import { combineLatest as observableCombineLatest, Observable, of } from 'rxjs';
|
import {
|
||||||
|
combineLatest as observableCombineLatest,
|
||||||
|
Observable,
|
||||||
|
of as observableOf,
|
||||||
|
from as observableFrom
|
||||||
|
} from 'rxjs';
|
||||||
import {
|
import {
|
||||||
FieldUpdate,
|
FieldUpdate,
|
||||||
FieldUpdates,
|
FieldUpdates,
|
||||||
@@ -11,14 +16,24 @@ import {
|
|||||||
} from '../../../../core/data/object-updates/object-updates.reducer';
|
} from '../../../../core/data/object-updates/object-updates.reducer';
|
||||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { defaultIfEmpty, map, mergeMap, switchMap, take, startWith } from 'rxjs/operators';
|
import {
|
||||||
import { hasValue, hasValueOperator } from '../../../../shared/empty.util';
|
defaultIfEmpty,
|
||||||
|
map,
|
||||||
|
mergeMap,
|
||||||
|
switchMap,
|
||||||
|
take,
|
||||||
|
startWith,
|
||||||
|
toArray,
|
||||||
|
tap
|
||||||
|
} from 'rxjs/operators';
|
||||||
|
import { hasValue, hasValueOperator, hasNoValue } from '../../../../shared/empty.util';
|
||||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||||
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||||
import {
|
import {
|
||||||
getAllSucceededRemoteData,
|
|
||||||
getRemoteDataPayload,
|
getRemoteDataPayload,
|
||||||
getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload,
|
getFirstSucceededRemoteData,
|
||||||
|
getFirstSucceededRemoteDataPayload,
|
||||||
|
getAllSucceededRemoteData,
|
||||||
} from '../../../../core/shared/operators';
|
} from '../../../../core/shared/operators';
|
||||||
import { ItemType } from '../../../../core/shared/item-relationships/item-type.model';
|
import { ItemType } from '../../../../core/shared/item-relationships/item-type.model';
|
||||||
import { DsDynamicLookupRelationModalComponent } from '../../../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component';
|
import { DsDynamicLookupRelationModalComponent } from '../../../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component';
|
||||||
@@ -30,6 +45,10 @@ import { followLink } from '../../../../shared/utils/follow-link-config.model';
|
|||||||
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
import { Collection } from '../../../../core/shared/collection.model';
|
import { Collection } from '../../../../core/shared/collection.model';
|
||||||
|
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||||
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
|
||||||
|
import { PaginationService } from '../../../../core/pagination/pagination.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-edit-relationship-list',
|
selector: 'ds-edit-relationship-list',
|
||||||
@@ -40,7 +59,7 @@ import { Collection } from '../../../../core/shared/collection.model';
|
|||||||
* A component creating a list of editable relationships of a certain type
|
* A component creating a list of editable relationships of a certain type
|
||||||
* The relationships are rendered as a list of related items
|
* The relationships are rendered as a list of related items
|
||||||
*/
|
*/
|
||||||
export class EditRelationshipListComponent implements OnInit {
|
export class EditRelationshipListComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The item to display related items for
|
* The item to display related items for
|
||||||
@@ -60,6 +79,17 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() relationshipType: RelationshipType;
|
@Input() relationshipType: RelationshipType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable that emits the left and right item type of {@link relationshipType} simultaneously.
|
||||||
|
*/
|
||||||
|
private relationshipLeftAndRightType$: Observable<[ItemType, ItemType]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable that emits true if {@link itemType} is on the left-hand side of {@link relationshipType},
|
||||||
|
* false if it is on the right-hand side and undefined in the rare case that it is on neither side.
|
||||||
|
*/
|
||||||
|
private currentItemIsLeftItem$: Observable<boolean>;
|
||||||
|
|
||||||
private relatedEntityType$: Observable<ItemType>;
|
private relatedEntityType$: Observable<ItemType>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,7 +100,38 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* The FieldUpdates for the relationships in question
|
* The FieldUpdates for the relationships in question
|
||||||
*/
|
*/
|
||||||
updates$: Observable<FieldUpdates>;
|
updates$: BehaviorSubject<FieldUpdates> = new BehaviorSubject(undefined);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The RemoteData for the relationships
|
||||||
|
*/
|
||||||
|
relationshipsRd$: BehaviorSubject<RemoteData<PaginatedList<Relationship>>> = new BehaviorSubject(undefined);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the current page is the last page
|
||||||
|
*/
|
||||||
|
isLastPage$: BehaviorSubject<boolean> = new BehaviorSubject(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether we're loading
|
||||||
|
*/
|
||||||
|
loading$: BehaviorSubject<boolean> = new BehaviorSubject(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of added fields that haven't been saved yet
|
||||||
|
*/
|
||||||
|
nbAddedFields$: BehaviorSubject<number> = new BehaviorSubject(0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pagination config
|
||||||
|
*/
|
||||||
|
paginationConfig: PaginationComponentOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to the lookup window
|
* A reference to the lookup window
|
||||||
@@ -82,6 +143,7 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
protected linkService: LinkService,
|
protected linkService: LinkService,
|
||||||
protected relationshipService: RelationshipService,
|
protected relationshipService: RelationshipService,
|
||||||
protected modalService: NgbModal,
|
protected modalService: NgbModal,
|
||||||
|
protected paginationService: PaginationService,
|
||||||
protected selectableListService: SelectableListService,
|
protected selectableListService: SelectableListService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@@ -172,6 +234,10 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
this.objectUpdatesService.saveAddFieldUpdate(this.url, update);
|
this.objectUpdatesService.saveAddFieldUpdate(this.url, update);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.loading$.next(true);
|
||||||
|
// emit the last page again to trigger a fieldupdates refresh
|
||||||
|
this.relationshipsRd$.next(this.relationshipsRd$.getValue());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -186,6 +252,10 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.loading$.next(true);
|
||||||
|
// emit the last page again to trigger a fieldupdates refresh
|
||||||
|
this.relationshipsRd$.next(this.relationshipsRd$.getValue());
|
||||||
};
|
};
|
||||||
this.relatedEntityType$
|
this.relatedEntityType$
|
||||||
.pipe(take(1))
|
.pipe(take(1))
|
||||||
@@ -212,10 +282,10 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
if (field.relationship) {
|
if (field.relationship) {
|
||||||
return this.getRelatedItem(field.relationship);
|
return this.getRelatedItem(field.relationship);
|
||||||
} else {
|
} else {
|
||||||
return of(field.relatedItem);
|
return observableOf(field.relatedItem);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
) : of([])
|
) : observableOf([])
|
||||||
),
|
),
|
||||||
take(1),
|
take(1),
|
||||||
map((items) => items.map((item) => {
|
map((items) => items.map((item) => {
|
||||||
@@ -267,18 +337,19 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// store the left and right type of the relationship in a single observable
|
||||||
|
this.relationshipLeftAndRightType$ = observableCombineLatest([
|
||||||
|
this.relationshipType.leftType,
|
||||||
|
this.relationshipType.rightType,
|
||||||
|
].map((type) => type.pipe(
|
||||||
|
getFirstSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload(),
|
||||||
|
))) as Observable<[ItemType, ItemType]>;
|
||||||
|
|
||||||
this.relatedEntityType$ =
|
this.relatedEntityType$ = this.relationshipLeftAndRightType$.pipe(
|
||||||
observableCombineLatest([
|
map((relatedTypes: ItemType[]) => relatedTypes.find((relatedType) => relatedType.uuid !== this.itemType.uuid)),
|
||||||
this.relationshipType.leftType,
|
hasValueOperator()
|
||||||
this.relationshipType.rightType,
|
);
|
||||||
].map((type) => type.pipe(
|
|
||||||
getFirstSucceededRemoteData(),
|
|
||||||
getRemoteDataPayload(),
|
|
||||||
))).pipe(
|
|
||||||
map((relatedTypes: ItemType[]) => relatedTypes.find((relatedType) => relatedType.uuid !== this.itemType.uuid)),
|
|
||||||
hasValueOperator()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.relatedEntityType$.pipe(
|
this.relatedEntityType$.pipe(
|
||||||
take(1)
|
take(1)
|
||||||
@@ -286,65 +357,142 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
(relatedEntityType) => this.listId = `edit-relationship-${this.itemType.id}-${relatedEntityType.id}`
|
(relatedEntityType) => this.listId = `edit-relationship-${this.itemType.id}-${relatedEntityType.id}`
|
||||||
);
|
);
|
||||||
|
|
||||||
this.updates$ = this.getItemRelationships().pipe(
|
this.currentItemIsLeftItem$ = this.relationshipLeftAndRightType$.pipe(
|
||||||
switchMap((relationships) =>
|
map(([leftType, rightType]: [ItemType, ItemType]) => {
|
||||||
observableCombineLatest(
|
if (leftType.id === this.itemType.id) {
|
||||||
relationships.map((relationship) => this.relationshipService.isLeftItem(relationship, this.item))
|
return true;
|
||||||
).pipe(
|
}
|
||||||
defaultIfEmpty([]),
|
|
||||||
map((isLeftItemArray) => isLeftItemArray.map((isLeftItem, index) => {
|
if (rightType.id === this.itemType.id) {
|
||||||
const relationship = relationships[index];
|
return false;
|
||||||
const nameVariant = isLeftItem ? relationship.rightwardValue : relationship.leftwardValue;
|
}
|
||||||
|
|
||||||
|
// should never happen...
|
||||||
|
console.warn(`The item ${this.item.uuid} is not on the right or the left side of relationship type ${this.relationshipType.uuid}`);
|
||||||
|
return undefined;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// initialize the pagination options
|
||||||
|
this.paginationConfig = new PaginationComponentOptions();
|
||||||
|
this.paginationConfig.id = `er${this.relationshipType.id}`;
|
||||||
|
this.paginationConfig.pageSize = 5;
|
||||||
|
this.paginationConfig.currentPage = 1;
|
||||||
|
|
||||||
|
// get the pagination params from the route
|
||||||
|
const currentPagination$ = this.paginationService.getCurrentPagination(
|
||||||
|
this.paginationConfig.id,
|
||||||
|
this.paginationConfig
|
||||||
|
).pipe(
|
||||||
|
tap(() => this.loading$.next(true))
|
||||||
|
);
|
||||||
|
|
||||||
|
this.subs.push(
|
||||||
|
observableCombineLatest([
|
||||||
|
currentPagination$,
|
||||||
|
this.currentItemIsLeftItem$,
|
||||||
|
]).pipe(
|
||||||
|
switchMap(([currentPagination, currentItemIsLeftItem]: [PaginationComponentOptions, boolean]) =>
|
||||||
|
// get the relationships for the current item, relationshiptype and page
|
||||||
|
this.relationshipService.getItemRelationshipsByLabel(
|
||||||
|
this.item,
|
||||||
|
currentItemIsLeftItem ? this.relationshipType.leftwardType : this.relationshipType.rightwardType,
|
||||||
|
{
|
||||||
|
elementsPerPage: currentPagination.pageSize,
|
||||||
|
currentPage: currentPagination.currentPage,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
followLink('leftItem'),
|
||||||
|
followLink('rightItem'),
|
||||||
|
)),
|
||||||
|
).subscribe((rd: RemoteData<PaginatedList<Relationship>>) => {
|
||||||
|
this.relationshipsRd$.next(rd);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// keep isLastPage$ up to date based on relationshipsRd$
|
||||||
|
this.subs.push(this.relationshipsRd$.pipe(
|
||||||
|
hasValueOperator(),
|
||||||
|
getAllSucceededRemoteData()
|
||||||
|
).subscribe((rd: RemoteData<PaginatedList<Relationship>>) => {
|
||||||
|
this.isLastPage$.next(hasNoValue(rd.payload._links.next));
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.subs.push(this.relationshipsRd$.pipe(
|
||||||
|
hasValueOperator(),
|
||||||
|
getAllSucceededRemoteData(),
|
||||||
|
switchMap((rd: RemoteData<PaginatedList<Relationship>>) =>
|
||||||
|
// emit each relationship in the page separately
|
||||||
|
observableFrom(rd.payload.page).pipe(
|
||||||
|
mergeMap((relationship: Relationship) =>
|
||||||
|
// check for each relationship whether it's the left item
|
||||||
|
this.relationshipService.isLeftItem(relationship, this.item).pipe(
|
||||||
|
// emit an array containing both the relationship and whether it's the left item,
|
||||||
|
// as we'll need both
|
||||||
|
map((isLeftItem: boolean) => [relationship, isLeftItem])
|
||||||
|
)
|
||||||
|
),
|
||||||
|
map(([relationship, isLeftItem]: [Relationship, boolean]) => {
|
||||||
|
// turn it into a RelationshipIdentifiable, an
|
||||||
|
const nameVariant =
|
||||||
|
isLeftItem ? relationship.rightwardValue : relationship.leftwardValue;
|
||||||
return {
|
return {
|
||||||
uuid: relationship.id,
|
uuid: relationship.id,
|
||||||
type: this.relationshipType,
|
type: this.relationshipType,
|
||||||
relationship,
|
relationship,
|
||||||
nameVariant,
|
nameVariant,
|
||||||
} as RelationshipIdentifiable;
|
} as RelationshipIdentifiable;
|
||||||
})),
|
}),
|
||||||
)),
|
// wait until all relationships have been processed, and emit them all as a single array
|
||||||
switchMap((initialFields) => this.objectUpdatesService.getFieldUpdates(this.url, initialFields).pipe(
|
toArray(),
|
||||||
map((fieldUpdates) => {
|
// if the pipe above completes without emitting anything, emit an empty array instead
|
||||||
const fieldUpdatesFiltered: FieldUpdates = {};
|
defaultIfEmpty([])
|
||||||
Object.keys(fieldUpdates).forEach((uuid) => {
|
|
||||||
if (hasValue(fieldUpdates[uuid])) {
|
|
||||||
const field = fieldUpdates[uuid].field;
|
|
||||||
if ((field as RelationshipIdentifiable).type.id === this.relationshipType.id) {
|
|
||||||
fieldUpdatesFiltered[uuid] = fieldUpdates[uuid];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return fieldUpdatesFiltered;
|
|
||||||
}),
|
|
||||||
)),
|
)),
|
||||||
|
switchMap((nextFields: RelationshipIdentifiable[]) => {
|
||||||
|
// Get a list that contains the unsaved changes for the page, as well as the page of
|
||||||
|
// RelationshipIdentifiables, as a single list of FieldUpdates
|
||||||
|
return this.objectUpdatesService.getFieldUpdates(this.url, nextFields).pipe(
|
||||||
|
map((fieldUpdates: FieldUpdates) => {
|
||||||
|
const fieldUpdatesFiltered: FieldUpdates = {};
|
||||||
|
this.nbAddedFields$.next(0);
|
||||||
|
// iterate over the fieldupdates and filter out the ones that pertain to this
|
||||||
|
// relationshiptype
|
||||||
|
Object.keys(fieldUpdates).forEach((uuid) => {
|
||||||
|
if (hasValue(fieldUpdates[uuid])) {
|
||||||
|
const field = fieldUpdates[uuid].field as RelationshipIdentifiable;
|
||||||
|
// only include fieldupdates regarding this RelationshipType
|
||||||
|
if (field.type.id === this.relationshipType.id) {
|
||||||
|
// if it's a newly added relationship
|
||||||
|
if (fieldUpdates[uuid].changeType === FieldChangeType.ADD) {
|
||||||
|
// increase the counter that tracks new relationships
|
||||||
|
this.nbAddedFields$.next(this.nbAddedFields$.getValue() + 1);
|
||||||
|
if (this.isLastPage$.getValue() === true) {
|
||||||
|
// only include newly added relationships to the output if we're on the last
|
||||||
|
// page
|
||||||
|
fieldUpdatesFiltered[uuid] = fieldUpdates[uuid];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// include all others
|
||||||
|
fieldUpdatesFiltered[uuid] = fieldUpdates[uuid];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return fieldUpdatesFiltered;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}),
|
||||||
startWith({}),
|
startWith({}),
|
||||||
);
|
).subscribe((updates: FieldUpdates) => {
|
||||||
|
this.loading$.next(false);
|
||||||
|
this.updates$.next(updates);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private getItemRelationships() {
|
ngOnDestroy(): void {
|
||||||
this.linkService.resolveLink(this.item,
|
this.subs
|
||||||
followLink('relationships', {},
|
.filter((subscription) => hasValue(subscription))
|
||||||
followLink('relationshipType'),
|
.forEach((subscription) => subscription.unsubscribe());
|
||||||
followLink('leftItem'),
|
|
||||||
followLink('rightItem'),
|
|
||||||
));
|
|
||||||
return this.item.relationships.pipe(
|
|
||||||
getAllSucceededRemoteData(),
|
|
||||||
map((relationships: RemoteData<PaginatedList<Relationship>>) => relationships.payload.page.filter((relationship: Relationship) => hasValue(relationship))),
|
|
||||||
switchMap((itemRelationships: Relationship[]) =>
|
|
||||||
observableCombineLatest(
|
|
||||||
itemRelationships
|
|
||||||
.map((relationship) => relationship.relationshipType.pipe(
|
|
||||||
getFirstSucceededRemoteData(),
|
|
||||||
getRemoteDataPayload(),
|
|
||||||
))
|
|
||||||
).pipe(
|
|
||||||
defaultIfEmpty([]),
|
|
||||||
map((relationshipTypes) => itemRelationships.filter(
|
|
||||||
(relationship, index) => relationshipTypes[index].id === this.relationshipType.id)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -227,7 +227,6 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent {
|
|||||||
* Sends all initial values of this item to the object updates service
|
* Sends all initial values of this item to the object updates service
|
||||||
*/
|
*/
|
||||||
public initializeOriginalFields() {
|
public initializeOriginalFields() {
|
||||||
console.log('init');
|
|
||||||
return this.relationshipService.getRelatedItems(this.item).pipe(
|
return this.relationshipService.getRelatedItems(this.item).pipe(
|
||||||
take(1),
|
take(1),
|
||||||
).subscribe((items: Item[]) => {
|
).subscribe((items: Item[]) => {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { autoserialize, autoserializeAs, deserialize, inheritSerialization } from 'cerialize';
|
import { autoserialize, autoserializeAs, deserialize, deserializeAs, inheritSerialization } from 'cerialize';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { isEmpty } from '../../shared/empty.util';
|
import { isEmpty } from '../../shared/empty.util';
|
||||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||||
@@ -39,7 +39,7 @@ export class Item extends DSpaceObject implements ChildHALResource {
|
|||||||
/**
|
/**
|
||||||
* The Date of the last modification of this Item
|
* The Date of the last modification of this Item
|
||||||
*/
|
*/
|
||||||
@deserialize
|
@deserializeAs(Date)
|
||||||
lastModified: Date;
|
lastModified: Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -261,9 +261,10 @@ describe('SubmissionFormCollectionComponent Component', () => {
|
|||||||
expect(comp.toggled).toHaveBeenCalled();
|
expect(comp.toggled).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ', () => {
|
it('should change collection properly', () => {
|
||||||
spyOn(comp.collectionChange, 'emit').and.callThrough();
|
spyOn(comp.collectionChange, 'emit').and.callThrough();
|
||||||
jsonPatchOpServiceStub.jsonPatchByResourceID.and.returnValue(of(submissionRestResponse));
|
jsonPatchOpServiceStub.jsonPatchByResourceID.and.returnValue(of(submissionRestResponse));
|
||||||
|
submissionServiceStub.retrieveSubmission.and.returnValue(createSuccessfulRemoteDataObject$(submissionRestResponse[0]));
|
||||||
comp.ngOnInit();
|
comp.ngOnInit();
|
||||||
comp.onSelect(mockCollectionList[1]);
|
comp.onSelect(mockCollectionList[1]);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@@ -13,7 +13,7 @@ import {
|
|||||||
import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs';
|
import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
find,
|
find,
|
||||||
map
|
map, mergeMap
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
@@ -27,6 +27,7 @@ import { SubmissionJsonPatchOperationsService } from '../../../core/submission/s
|
|||||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
import { CollectionDropdownComponent } from '../../../shared/collection-dropdown/collection-dropdown.component';
|
import { CollectionDropdownComponent } from '../../../shared/collection-dropdown/collection-dropdown.component';
|
||||||
import { SectionsService } from '../../sections/sections.service';
|
import { SectionsService } from '../../sections/sections.service';
|
||||||
|
import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component allows to show the current collection the submission belonging to and to change it.
|
* This component allows to show the current collection the submission belonging to and to change it.
|
||||||
@@ -164,11 +165,17 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
this.submissionService.getSubmissionObjectLinkName(),
|
this.submissionService.getSubmissionObjectLinkName(),
|
||||||
this.submissionId,
|
this.submissionId,
|
||||||
'sections',
|
'sections',
|
||||||
'collection')
|
'collection').pipe(
|
||||||
.subscribe((submissionObject: SubmissionObject[]) => {
|
mergeMap((submissionObject: SubmissionObject[]) => {
|
||||||
|
// retrieve the full submission object with embeds
|
||||||
|
return this.submissionService.retrieveSubmission(submissionObject[0].id).pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
).subscribe((submissionObject: SubmissionObject) => {
|
||||||
this.selectedCollectionId = event.collection.id;
|
this.selectedCollectionId = event.collection.id;
|
||||||
this.selectedCollectionName$ = observableOf(event.collection.name);
|
this.selectedCollectionName$ = observableOf(event.collection.name);
|
||||||
this.collectionChange.emit(submissionObject[0]);
|
this.collectionChange.emit(submissionObject);
|
||||||
this.submissionService.changeSubmissionCollection(this.submissionId, event.collection.id);
|
this.submissionService.changeSubmissionCollection(this.submissionId, event.collection.id);
|
||||||
this.processingChange$.next(false);
|
this.processingChange$.next(false);
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
|
Reference in New Issue
Block a user