diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5 index 9afe280fa3..8d956d1a0e 100644 --- a/resources/i18n/en.json5 +++ b/resources/i18n/en.json5 @@ -853,7 +853,7 @@ "item.edit.tabs.relationships.head": "Item Relationships", - "item.edit.tabs.relationships.title": "Item Edit - Relationships", + "item.edit.tabs.relationships.title": "Item Edit - Relationships", "item.edit.tabs.status.buttons.authorizations.button": "Authorizations...", @@ -1974,12 +1974,16 @@ "uploader.drag-message": "Drag & Drop your files here", - "uploader.or": ", or ", + "uploader.or": ", or", "uploader.processing": "Processing", "uploader.queue-length": "Queue length", + "virtual-metadata.delete-item.info": "Select the types for which you want to save the virtual metadata as real metadata", + + "virtual-metadata.delete-item.modal-head": "The virtual metadata of this relation", + "virtual-metadata.delete-relationship.modal-head": "Select the items for which you want to save the virtual metadata as real metadata", } diff --git a/src/app/+item-page/edit-item-page/item-delete/item-delete.component.html b/src/app/+item-page/edit-item-page/item-delete/item-delete.component.html new file mode 100644 index 0000000000..5e7297409b --- /dev/null +++ b/src/app/+item-page/edit-item-page/item-delete/item-delete.component.html @@ -0,0 +1,98 @@ +
+
+
+ +

{{headerMessage | translate: {id: item.handle} }}

+

{{descriptionMessage | translate}}

+ + + + +
+ + {{'virtual-metadata.delete-item.info' | translate}} + +
+ +
+ +
+ +
+ +
+
+ {{getRelationshipMessageKey(getLabel(type) | async) | translate}} +
+
+ + + + +
+
+ +
+
+ + +
+ + +
+
+ +
+
+
+ +
+ +
+ +
+ +
+ + + + +
+
+
diff --git a/src/app/+item-page/edit-item-page/item-delete/item-delete.component.spec.ts b/src/app/+item-page/edit-item-page/item-delete/item-delete.component.spec.ts index 82d03f1f1b..00ae038dae 100644 --- a/src/app/+item-page/edit-item-page/item-delete/item-delete.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-delete/item-delete.component.spec.ts @@ -1,44 +1,132 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { Item } from '../../../core/shared/item.model'; -import { RouterStub } from '../../../shared/testing/router-stub'; -import { of as observableOf } from 'rxjs'; -import { RemoteData } from '../../../core/data/remote-data'; -import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { RouterTestingModule } from '@angular/router/testing'; -import { TranslateModule } from '@ngx-translate/core'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { ActivatedRoute, Router } from '@angular/router'; -import { ItemDataService } from '../../../core/data/item-data.service'; -import { NotificationsService } from '../../../shared/notifications/notifications.service'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { By } from '@angular/platform-browser'; -import { ItemDeleteComponent } from './item-delete.component'; -import { getItemEditPath } from '../../item-page-routing.module'; -import { RestResponse } from '../../../core/cache/response.models'; -import { createSuccessfulRemoteDataObject } from '../../../shared/testing/utils'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import { ItemType } from '../../../core/shared/item-relationships/item-type.model'; +import { Relationship } from '../../../core/shared/item-relationships/relationship.model'; +import {Item} from '../../../core/shared/item.model'; +import {RouterStub} from '../../../shared/testing/router-stub'; +import {of as observableOf} from 'rxjs'; +import {NotificationsServiceStub} from '../../../shared/testing/notifications-service-stub'; +import {CommonModule} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {RouterTestingModule} from '@angular/router/testing'; +import {TranslateModule} from '@ngx-translate/core'; +import {NgbModule} from '@ng-bootstrap/ng-bootstrap'; +import {ActivatedRoute, Router} from '@angular/router'; +import {ItemDataService} from '../../../core/data/item-data.service'; +import {NotificationsService} from '../../../shared/notifications/notifications.service'; +import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import {By} from '@angular/platform-browser'; +import {ItemDeleteComponent} from './item-delete.component'; +import {getItemEditPath} from '../../item-page-routing.module'; +import {createSuccessfulRemoteDataObject} from '../../../shared/testing/utils'; +import {VarDirective} from '../../../shared/utils/var.directive'; +import {ObjectUpdatesService} from '../../../core/data/object-updates/object-updates.service'; +import {RelationshipService} from '../../../core/data/relationship.service'; +import {RelationshipType} from '../../../core/shared/item-relationships/relationship-type.model'; +import {RemoteData} from '../../../core/data/remote-data'; +import {PaginatedList} from '../../../core/data/paginated-list'; +import {PageInfo} from '../../../core/shared/page-info.model'; +import {EntityTypeService} from '../../../core/data/entity-type.service'; let comp: ItemDeleteComponent; let fixture: ComponentFixture; let mockItem; +let itemType; +let type1; +let type2; +let types; +let relationships; let itemPageUrl; let routerStub; let mockItemDataService: ItemDataService; let routeStub; +let objectUpdatesServiceStub; +let relationshipService; +let entityTypeService; let notificationsServiceStub; +let typesSelection; describe('ItemDeleteComponent', () => { beforeEach(async(() => { mockItem = Object.assign(new Item(), { id: 'fake-id', + uuid: 'fake-uuid', handle: 'fake/handle', lastModified: '2018', isWithdrawn: true }); + itemType = Object.assign(new ItemType(), { + id: 'itemType', + uuid: 'itemType', + }); + + type1 = Object.assign(new RelationshipType(), { + id: '1', + uuid: 'type-1', + }); + + type2 = Object.assign(new RelationshipType(), { + id: '2', + uuid: 'type-2', + }); + + types = [type1, type2]; + + relationships = [ + Object.assign(new Relationship(), { + id: '1', + uuid: 'relationship-1', + relationshipType: observableOf(new RemoteData( + false, + false, + true, + null, + type1 + )), + leftItem: observableOf(new RemoteData( + false, + false, + true, + null, + mockItem, + )), + rightItem: observableOf(new RemoteData( + false, + false, + true, + null, + Object.assign(new Item(), {}) + )), + }), + Object.assign(new Relationship(), { + id: '2', + uuid: 'relationship-2', + relationshipType: observableOf(new RemoteData( + false, + false, + true, + null, + type2 + )), + leftItem: observableOf(new RemoteData( + false, + false, + true, + null, + mockItem, + )), + rightItem: observableOf(new RemoteData( + false, + false, + true, + null, + Object.assign(new Item(), {}) + )), + }), + ]; + itemPageUrl = `fake-url/${mockItem.id}`; routerStub = Object.assign(new RouterStub(), { url: `${itemPageUrl}/edit` @@ -54,16 +142,56 @@ describe('ItemDeleteComponent', () => { }) }; + typesSelection = { + type1: false, + type2: true, + }; + + entityTypeService = jasmine.createSpyObj('entityTypeService', + { + getEntityTypeByLabel: observableOf(new RemoteData( + false, + false, + true, + null, + itemType, + )), + getEntityTypeRelationships: observableOf(new RemoteData( + false, + false, + true, + null, + new PaginatedList(new PageInfo(), types), + )), + } + ); + + objectUpdatesServiceStub = { + initialize: () => { + // do nothing + }, + isSelectedVirtualMetadata: (type) => observableOf(typesSelection[type]), + }; + + relationshipService = jasmine.createSpyObj('relationshipService', + { + getItemRelationshipsArray: observableOf(relationships), + } + ); + notificationsServiceStub = new NotificationsServiceStub(); TestBed.configureTestingModule({ imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], - declarations: [ItemDeleteComponent], + declarations: [ItemDeleteComponent, VarDirective], providers: [ { provide: ActivatedRoute, useValue: routeStub }, { provide: Router, useValue: routerStub }, { provide: ItemDataService, useValue: mockItemDataService }, { provide: NotificationsService, useValue: notificationsServiceStub }, + { provide: ObjectUpdatesService, useValue: objectUpdatesServiceStub }, + { provide: RelationshipService, useValue: relationshipService }, + { provide: EntityTypeService, useValue: entityTypeService }, ], schemas: [ CUSTOM_ELEMENTS_SCHEMA ] @@ -91,7 +219,8 @@ describe('ItemDeleteComponent', () => { it('should call delete function from the ItemDataService', () => { spyOn(comp, 'notify'); comp.performAction(); - expect(mockItemDataService.delete).toHaveBeenCalledWith(mockItem); + expect(mockItemDataService.delete) + .toHaveBeenCalledWith(mockItem, types.filter((type) => typesSelection[type]).map((type) => type.id)); expect(comp.notify).toHaveBeenCalled(); }); }); diff --git a/src/app/+item-page/edit-item-page/item-delete/item-delete.component.ts b/src/app/+item-page/edit-item-page/item-delete/item-delete.component.ts index 2700b45475..6fe44c109b 100644 --- a/src/app/+item-page/edit-item-page/item-delete/item-delete.component.ts +++ b/src/app/+item-page/edit-item-page/item-delete/item-delete.component.ts @@ -1,29 +1,323 @@ -import { Component } from '@angular/core'; -import { first } from 'rxjs/operators'; -import { AbstractSimpleItemActionComponent } from '../simple-item-action/abstract-simple-item-action.component'; -import { getItemEditPath } from '../../item-page-routing.module'; -import { RestResponse } from '../../../core/cache/response.models'; +import {Component, Input, OnInit} from '@angular/core'; +import {filter, first, map, switchMap, take} from 'rxjs/operators'; +import {AbstractSimpleItemActionComponent} from '../simple-item-action/abstract-simple-item-action.component'; +import {getItemEditPath} from '../../item-page-routing.module'; +import {NgbModal, NgbModalRef} from '@ng-bootstrap/ng-bootstrap'; +import {combineLatest as observableCombineLatest, combineLatest, Observable} from 'rxjs'; +import {RelationshipType} from '../../../core/shared/item-relationships/relationship-type.model'; +import {VirtualMetadata} from '../virtual-metadata/virtual-metadata.component'; +import {Relationship} from '../../../core/shared/item-relationships/relationship.model'; +import {getRemoteDataPayload, getSucceededRemoteData} from '../../../core/shared/operators'; +import {hasValue, isNotEmpty} from '../../../shared/empty.util'; +import {Item} from '../../../core/shared/item.model'; +import {MetadataValue} from '../../../core/shared/metadata.models'; +import {ViewMode} from '../../../core/shared/view-mode.model'; +import {ActivatedRoute, Router} from '@angular/router'; +import {NotificationsService} from '../../../shared/notifications/notifications.service'; +import {ItemDataService} from '../../../core/data/item-data.service'; +import {TranslateService} from '@ngx-translate/core'; +import {ObjectUpdatesService} from '../../../core/data/object-updates/object-updates.service'; +import {RelationshipService} from '../../../core/data/relationship.service'; +import {EntityTypeService} from '../../../core/data/entity-type.service'; @Component({ selector: 'ds-item-delete', - templateUrl: '../simple-item-action/abstract-simple-item-action.component.html' + templateUrl: '../item-delete/item-delete.component.html' }) /** * Component responsible for rendering the item delete page */ -export class ItemDeleteComponent extends AbstractSimpleItemActionComponent { +export class ItemDeleteComponent + extends AbstractSimpleItemActionComponent + implements OnInit { + + /** + * The current url of this page + */ + @Input() url: string; protected messageKey = 'delete'; /** - * Perform the delete action to the item + * The view-mode we're currently on + */ + viewMode = ViewMode.ListElement; + + /** + * A list of the relationship types for which this item has relations as an observable. + * The list doesn't contain duplicates. + */ + types$: Observable; + + /** + * A map which stores the relationships of this item for each type as observable lists + */ + relationships$: Map> + = new Map>(); + + /** + * A map which stores the related item of each relationship of this item as an observable + */ + relatedItems$: Map> = new Map>(); + + /** + * A map which stores the virtual metadata (of the related) item corresponding to each relationship of this item + * as an observable list + */ + virtualMetadata$: Map> = new Map>(); + + /** + * Reference to NgbModal + */ + public modalRef: NgbModalRef; + + constructor(protected route: ActivatedRoute, + protected router: Router, + protected notificationsService: NotificationsService, + protected itemDataService: ItemDataService, + protected translateService: TranslateService, + protected modalService: NgbModal, + protected objectUpdatesService: ObjectUpdatesService, + protected relationshipService: RelationshipService, + protected entityTypeService: EntityTypeService, + ) { + super( + route, + router, + notificationsService, + itemDataService, + translateService, + ); + } + + /** + * Set up and initialize all fields + */ + ngOnInit() { + + super.ngOnInit(); + this.url = this.router.url; + + this.types$ = this.entityTypeService.getEntityTypeByLabel( + this.item.firstMetadataValue('relationship.type') + ).pipe( + getSucceededRemoteData(), + getRemoteDataPayload(), + switchMap((entityType) => this.entityTypeService.getEntityTypeRelationships(entityType.id)), + getSucceededRemoteData(), + getRemoteDataPayload(), + map((relationshipTypes) => relationshipTypes.page), + switchMap((types) => + combineLatest(types.map((type) => this.getRelationships(type))).pipe( + map((relationships) => + types.reduce((includedTypes, type, index) => { + if (!includedTypes.some((includedType) => includedType.id === type.id) + && !(relationships[index].length === 0)) { + return [...includedTypes, type]; + } else { + return includedTypes; + } + }, []) + ), + ) + ), + ); + + this.types$.pipe( + take(1), + ).subscribe((types) => + this.objectUpdatesService.initialize(this.url, types, this.item.lastModified) + ); + } + + /** + * Open the modal which lists the virtual metadata of a relation + * @param content the html content of the modal + */ + openVirtualMetadataModal(content: any) { + this.modalRef = this.modalService.open(content); + } + + /** + * Close the modal which lists the virtual metadata of a relation + */ + closeVirtualMetadataModal() { + this.modalRef.close(); + } + + /** + * Get the i18n message key for a relationship + * @param label The relationship type's label + */ + getRelationshipMessageKey(label: string): string { + if (hasValue(label) && label.indexOf('Of') > -1) { + return `relationships.${label.substring(0, label.indexOf('Of') + 2)}` + } else { + return label; + } + } + + /** + * Get the relationship type label relevant for this item as an observable + * @param relationshipType the relationship type to get the label for + */ + getLabel(relationshipType: RelationshipType): Observable { + + return this.getRelationships(relationshipType).pipe( + switchMap((relationships) => + this.isLeftItem(relationships[0]).pipe( + map((isLeftItem) => isLeftItem ? relationshipType.leftwardType : relationshipType.rightwardType), + ) + ), + ) + } + + /** + * Get the relationships of this item with a given type as an observable + * @param relationshipType the relationship type to filter the item's relationships on + */ + getRelationships(relationshipType: RelationshipType): Observable { + + if (!this.relationships$.has(relationshipType)) { + this.relationships$.set( + relationshipType, + this.relationshipService.getItemRelationshipsArray(this.item).pipe( + // filter on type + switchMap((relationships) => + observableCombineLatest( + relationships.map((relationship) => this.getRelationshipType(relationship)) + ).pipe( + map((types) => relationships.filter( + (relationship, index) => relationshipType.id === types[index].id + )), + ) + ), + ) + ); + } + + return this.relationships$.get(relationshipType); + } + + /** + * Get the type of a given relationship as an observable + * @param relationship the relationship to get the type for + */ + private getRelationshipType(relationship: Relationship): Observable { + + return relationship.relationshipType.pipe( + getSucceededRemoteData(), + getRemoteDataPayload(), + filter((relationshipType: RelationshipType) => hasValue(relationshipType) && isNotEmpty(relationshipType.uuid)) + ); + } + + /** + * Get the item this item is related to through a given relationship as an observable + * @param relationship the relationship to get the other item for + */ + getRelatedItem(relationship: Relationship): Observable { + + if (!this.relatedItems$.has(relationship)) { + + this.relatedItems$.set( + relationship, + this.isLeftItem(relationship).pipe( + switchMap((isLeftItem) => isLeftItem ? relationship.rightItem : relationship.leftItem), + getSucceededRemoteData(), + getRemoteDataPayload(), + ), + ); + } + + return this.relatedItems$.get(relationship); + } + + /** + * Get the virtual metadata for a given relationship of the related item. + * @param relationship the relationship to get the virtual metadata for + */ + getVirtualMetadata(relationship: Relationship): Observable { + + if (!this.virtualMetadata$.has(relationship)) { + + this.virtualMetadata$.set( + relationship, + this.getRelatedItem(relationship).pipe( + map((relatedItem) => + Object.entries(relatedItem.metadata) + .map(([key, value]) => value + .filter((metadata: MetadataValue) => + metadata.authority && metadata.authority.endsWith(relationship.id)) + .map((metadata: MetadataValue) => { + return { + metadataField: key, + metadataValue: metadata, + } + })) + .reduce((previous, current) => previous.concat(current)) + ), + ) + ); + } + + return this.virtualMetadata$.get(relationship); + } + + /** + * Check whether this item is the left item of a given relationship, as an observable boolean + * @param relationship the relationship for which to check whether this item is the left item + */ + private isLeftItem(relationship: Relationship): Observable { + + return relationship.leftItem.pipe( + getSucceededRemoteData(), + getRemoteDataPayload(), + filter((item: Item) => hasValue(item) && isNotEmpty(item.uuid)), + map((leftItem) => leftItem.uuid === this.item.uuid) + ); + } + + /** + * Check whether a given relationship type is selected to save the corresponding virtual metadata + * @param type the relationship type for which to check whether it is selected + */ + isSelected(type: RelationshipType): Observable { + return this.objectUpdatesService.isSelectedVirtualMetadata(this.url, this.item.uuid, type.uuid); + } + + /** + * Select/deselect a given relationship type to save the corresponding virtual metadata + * @param type the relationship type to select/deselect + * @param selected whether the type should be selected + */ + setSelected(type: RelationshipType, selected: boolean): void { + this.objectUpdatesService.setSelectedVirtualMetadata(this.url, this.item.uuid, type.uuid, selected); + } + + /** + * Perform the delete operation */ performAction() { - this.itemDataService.delete(this.item).pipe(first()).subscribe( - (succeeded: boolean) => { - this.notify(succeeded); - } - ); + + this.types$.pipe( + switchMap((types) => + combineLatest( + types.map((type) => this.isSelected(type)) + ).pipe( + map((selection) => types.filter( + (type, index) => selection[index] + )), + map((selectedTypes) => selectedTypes.map((type) => type.id)), + ) + ), + ).subscribe((types) => { + this.itemDataService.delete(this.item, types).pipe(first()).subscribe( + (succeeded: boolean) => { + this.notify(succeeded); + } + ); + }); } /** diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts index d55b7353eb..82fdb82008 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/data.service.ts @@ -318,9 +318,11 @@ export abstract class DataService { /** * Delete an existing DSpace Object on the server * @param dso The DSpace Object to be removed - * Return an observable that emits true when the deletion was successful, false when it failed + * @param copyVirtualMetadata (optional parameter) the identifiers of the relationship types for which the virtual + * metadata should be saved as real metadata + * @return an observable that emits true when the deletion was successful, false when it failed */ - delete(dso: T): Observable { + delete(dso: T, copyVirtualMetadata?: string[]): Observable { const requestId = this.requestService.generateRequestId(); const hrefObs = this.halService.getEndpoint(this.linkPath).pipe( @@ -329,6 +331,13 @@ export abstract class DataService { hrefObs.pipe( find((href: string) => hasValue(href)), map((href: string) => { + if (copyVirtualMetadata) { + copyVirtualMetadata.forEach((id) => + href += (href.includes('?') ? '&' : '?') + + 'copyVirtualMetadata=' + + id + ); + } const request = new DeleteByIDRequest(requestId, href, dso.uuid); this.requestService.configure(request); }) diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index db8f401687..0448c18ec6 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -1,35 +1,47 @@ -import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { MemoizedSelector, select, Store } from '@ngrx/store'; -import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs'; -import { distinctUntilChanged, filter, map, mergeMap, startWith, switchMap, take, tap } from 'rxjs/operators'; -import { compareArraysUsingIds, paginatedRelationsToItems, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils'; -import { AppState, keySelector } from '../../app.reducer'; -import { hasValue, hasValueOperator, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util'; import { ReorderableRelationship } from '../../shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component'; -import { RemoveNameVariantAction, SetNameVariantAction } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.actions'; -import { NameVariantListState } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer'; -import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; -import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; -import { configureRequest, getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators'; -import { SearchParam } from '../cache/models/search-param.model'; -import { ObjectCacheService } from '../cache/object-cache.service'; -import { DeleteRequest, FindListOptions, PostRequest, RestRequest } from './request.models'; -import { RestResponse } from '../cache/response.models'; -import { CoreState } from '../core.reducers'; -import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; +import { RequestService } from './request.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { hasValue, hasValueOperator, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util'; +import { distinctUntilChanged, filter, map, mergeMap, startWith, switchMap, take, tap } from 'rxjs/operators'; +import { + configureRequest, + getRemoteDataPayload, + getResponseFromEntry, + getSucceededRemoteData +} from '../shared/operators'; +import { DeleteRequest, FindListOptions, PostRequest, RestRequest } from './request.models'; +import { Observable } from 'rxjs/internal/Observable'; +import { RestResponse } from '../cache/response.models'; +import { Item } from '../shared/item.model'; +import { Relationship } from '../shared/item-relationships/relationship.model'; import { RelationshipType } from '../shared/item-relationships/relationship-type.model'; import { RemoteData, RemoteDataState } from './remote-data'; +import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs'; import { PaginatedList } from './paginated-list'; import { ItemDataService } from './item-data.service'; -import { Relationship } from '../shared/item-relationships/relationship.model'; -import { Item } from '../shared/item.model'; +import { + compareArraysUsingIds, + paginatedRelationsToItems, + relationsToItems +} from '../../+item-page/simple/item-types/shared/item-relationships-utils'; +import { ObjectCacheService } from '../cache/object-cache.service'; import { DataService } from './data.service'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { MemoizedSelector, select, Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; -import { RequestService } from './request.service'; -import { Observable } from 'rxjs/internal/Observable'; +import { SearchParam } from '../cache/models/search-param.model'; +import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; +import { AppState, keySelector } from '../../app.reducer'; +import { NameVariantListState } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer'; +import { + RemoveNameVariantAction, + SetNameVariantAction +} from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.actions'; const relationshipListsStateSelector = (state: AppState) => state.relationshipLists;