edit-relationship-list.component.ts (dspace-angular-dspace-7.0) | : | edit-relationship-list.component.ts (dspace-angular-dspace-7.1) | ||
---|---|---|---|---|
import { Component, Input, OnInit, OnDestroy } from '@angular/core'; | import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angu lar/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-upd ates.actions'; | import { FieldChangeType } from '../../../../core/data/object-updates/object-upd ates.actions'; | |||
import { ObjectUpdatesService } from '../../../../core/data/object-updates/objec t-updates.service'; | import { ObjectUpdatesService } from '../../../../core/data/object-updates/objec t-updates.service'; | |||
import { | import { combineLatest as observableCombineLatest, from as observableFrom, Obser | |||
combineLatest as observableCombineLatest, | vable } from 'rxjs'; | |||
Observable, | ||||
of as observableOf, | ||||
from as observableFrom | ||||
} from 'rxjs'; | ||||
import { | import { | |||
FieldUpdate, | FieldUpdate, | |||
FieldUpdates, | FieldUpdates, | |||
RelationshipIdentifiable | RelationshipIdentifiable | |||
} 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 { | import { defaultIfEmpty, map, mergeMap, startWith, switchMap, take, tap, toArray | |||
defaultIfEmpty, | } from 'rxjs/operators'; | |||
map, | import { hasNoValue, hasValue, hasValueOperator } from '../../../../shared/empty | |||
mergeMap, | .util'; | |||
switchMap, | ||||
take, | ||||
startWith, | ||||
toArray, | ||||
tap | ||||
} from 'rxjs/operators'; | ||||
import { hasValue, hasValueOperator, hasNoValue } from '../../../../shared/empty | ||||
.util'; | ||||
import { Relationship } from '../../../../core/shared/item-relationships/relatio nship.model'; | import { Relationship } from '../../../../core/shared/item-relationships/relatio nship.model'; | |||
import { RelationshipType } from '../../../../core/shared/item-relationships/rel ationship-type.model'; | import { RelationshipType } from '../../../../core/shared/item-relationships/rel ationship-type.model'; | |||
import { | import { | |||
getRemoteDataPayload, | getAllSucceededRemoteData, | |||
getFirstSucceededRemoteData, | getFirstSucceededRemoteData, | |||
getFirstSucceededRemoteDataPayload, | getFirstSucceededRemoteDataPayload, | |||
getAllSucceededRemoteData, | getRemoteDataPayload, | |||
} from '../../../../core/shared/operators'; | } from '../../../../core/shared/operators'; | |||
import { ItemType } from '../../../../core/shared/item-relationships/item-type.m odel'; | import { ItemType } from '../../../../core/shared/item-relationships/item-type.m odel'; | |||
import { DsDynamicLookupRelationModalComponent } from '../../../../shared/form/b uilder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.co mponent'; | import { DsDynamicLookupRelationModalComponent } from '../../../../shared/form/b uilder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.co mponent'; | |||
import { RelationshipOptions } from '../../../../shared/form/builder/models/rela tionship-options.model'; | import { RelationshipOptions } from '../../../../shared/form/builder/models/rela tionship-options.model'; | |||
import { ItemSearchResult } from '../../../../shared/object-collection/shared/it em-search-result.model'; | ||||
import { SelectableListService } from '../../../../shared/object-list/selectable -list/selectable-list.service'; | import { SelectableListService } from '../../../../shared/object-list/selectable -list/selectable-list.service'; | |||
import { SearchResult } from '../../../../shared/search/search-result.model'; | import { SearchResult } from '../../../../shared/search/search-result.model'; | |||
import { followLink } from '../../../../shared/utils/follow-link-config.model'; | 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 { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; | |||
import { Subscription } from 'rxjs/internal/Subscription'; | import { Subscription } from 'rxjs/internal/Subscription'; | |||
import { PaginationComponentOptions } from '../../../../shared/pagination/pagina tion-component-options.model'; | import { PaginationComponentOptions } from '../../../../shared/pagination/pagina tion-component-options.model'; | |||
import { PaginationService } from '../../../../core/pagination/pagination.servic e'; | import { PaginationService } from '../../../../core/pagination/pagination.servic e'; | |||
import { RelationshipTypeService } from '../../../../core/data/relationship-type .service'; | ||||
@Component({ | @Component({ | |||
selector: 'ds-edit-relationship-list', | selector: 'ds-edit-relationship-list', | |||
styleUrls: ['./edit-relationship-list.component.scss'], | styleUrls: ['./edit-relationship-list.component.scss'], | |||
templateUrl: './edit-relationship-list.component.html', | templateUrl: './edit-relationship-list.component.html', | |||
}) | }) | |||
/** | /** | |||
* 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 | |||
*/ | */ | |||
skipping to change at line 83 | skipping to change at line 69 | |||
* Used to fetch updates for the current item from the store | * Used to fetch updates for the current item from the store | |||
*/ | */ | |||
@Input() url: string; | @Input() url: string; | |||
/** | /** | |||
* The label of the relationship-type we're rendering a list for | * The label of the relationship-type we're rendering a list for | |||
*/ | */ | |||
@Input() relationshipType: RelationshipType; | @Input() relationshipType: RelationshipType; | |||
/** | /** | |||
* If updated information has changed | ||||
*/ | ||||
@Input() hasChanges!: Observable<boolean>; | ||||
/** | ||||
* The event emmiter to submit the new information | ||||
*/ | ||||
@Output() submit: EventEmitter<any> = new EventEmitter(); | ||||
/** | ||||
* Observable that emits the left and right item type of {@link relationshipTy pe} simultaneously. | * Observable that emits the left and right item type of {@link relationshipTy pe} simultaneously. | |||
*/ | */ | |||
private relationshipLeftAndRightType$: Observable<[ItemType, ItemType]>; | private relationshipLeftAndRightType$: Observable<[ItemType, ItemType]>; | |||
/** | /** | |||
* Observable that emits true if {@link itemType} is on the left-hand side of {@link relationshipType}, | * 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 i t is on neither side. | * false if it is on the right-hand side and undefined in the rare case that i t is on neither side. | |||
*/ | */ | |||
private currentItemIsLeftItem$: Observable<boolean>; | private currentItemIsLeftItem$: Observable<boolean>; | |||
skipping to change at line 145 | skipping to change at line 141 | |||
/** | /** | |||
* A reference to the lookup window | * A reference to the lookup window | |||
*/ | */ | |||
modalRef: NgbModalRef; | modalRef: NgbModalRef; | |||
constructor( | constructor( | |||
protected objectUpdatesService: ObjectUpdatesService, | protected objectUpdatesService: ObjectUpdatesService, | |||
protected linkService: LinkService, | protected linkService: LinkService, | |||
protected relationshipService: RelationshipService, | protected relationshipService: RelationshipService, | |||
protected relationshipTypeService: RelationshipTypeService, | ||||
protected modalService: NgbModal, | protected modalService: NgbModal, | |||
protected paginationService: PaginationService, | protected paginationService: PaginationService, | |||
protected selectableListService: SelectableListService, | protected selectableListService: SelectableListService, | |||
) { | ) { | |||
} | } | |||
/** | /** | |||
* Get the i18n message key for this relationship type | * Get the i18n message key for this relationship type | |||
*/ | */ | |||
public getRelationshipMessageKey(): Observable<string> { | public getRelationshipMessageKey(): Observable<string> { | |||
skipping to change at line 210 | skipping to change at line 207 | |||
/** | /** | |||
* Open the dynamic lookup modal to search for items to add as relationships | * Open the dynamic lookup modal to search for items to add as relationships | |||
*/ | */ | |||
openLookup() { | openLookup() { | |||
this.modalRef = this.modalService.open(DsDynamicLookupRelationModalComponent , { | this.modalRef = this.modalService.open(DsDynamicLookupRelationModalComponent , { | |||
size: 'lg' | size: 'lg' | |||
}); | }); | |||
const modalComp: DsDynamicLookupRelationModalComponent = this.modalRef.compo nentInstance; | const modalComp: DsDynamicLookupRelationModalComponent = this.modalRef.compo nentInstance; | |||
modalComp.repeatable = true; | modalComp.repeatable = true; | |||
modalComp.isEditRelationship = true; | ||||
modalComp.listId = this.listId; | modalComp.listId = this.listId; | |||
modalComp.item = this.item; | modalComp.item = this.item; | |||
modalComp.relationshipType = this.relationshipType; | ||||
modalComp.currentItemIsLeftItem$ = this.currentItemIsLeftItem$; | ||||
modalComp.toAdd = []; | ||||
modalComp.toRemove = []; | ||||
modalComp.isPending = false; | ||||
this.item.owningCollection.pipe( | this.item.owningCollection.pipe( | |||
getFirstSucceededRemoteDataPayload() | getFirstSucceededRemoteDataPayload() | |||
).subscribe((collection: Collection) => { | ).subscribe((collection: Collection) => { | |||
modalComp.collection = collection; | modalComp.collection = collection; | |||
}); | }); | |||
modalComp.select = (...selectableObjects: SearchResult<Item>[]) => { | modalComp.select = (...selectableObjects: SearchResult<Item>[]) => { | |||
selectableObjects.forEach((searchResult) => { | selectableObjects.forEach((searchResult) => { | |||
const relatedItem: Item = searchResult.indexableObject; | const relatedItem: Item = searchResult.indexableObject; | |||
this.getFieldUpdatesForRelatedItem(relatedItem) | ||||
.subscribe((identifiables) => { | const foundIndex = modalComp.toRemove.findIndex( el => el.uuid === relat | |||
identifiables.forEach((identifiable) => | edItem.uuid); | |||
this.objectUpdatesService.removeSingleFieldUpdate(this.url, identi | ||||
fiable.uuid) | if (foundIndex !== -1) { | |||
); | modalComp.toRemove.splice(foundIndex,1); | |||
if (identifiables.length === 0) { | } else { | |||
this.relationshipService.getNameVariant(this.listId, relatedItem.u | ||||
uid) | this.getRelationFromId(relatedItem) | |||
.subscribe((nameVariant) => { | .subscribe((relationship: Relationship) => { | |||
const update = { | if (!relationship ) { | |||
uuid: this.relationshipType.id + '-' + relatedItem.uuid, | modalComp.toAdd.push(searchResult); | |||
nameVariant, | } else { | |||
type: this.relationshipType, | const foundIndexRemove = modalComp.toRemove.findIndex( el => el. | |||
relatedItem, | indexableObject.uuid === relatedItem.uuid); | |||
} as RelationshipIdentifiable; | if (foundIndexRemove !== -1) { | |||
this.objectUpdatesService.saveAddFieldUpdate(this.url, update) | modalComp.toRemove.splice(foundIndexRemove,1); | |||
; | } | |||
}); | } | |||
} | ||||
this.loading$.next(true); | ||||
this.loading$.next(true); | // emit the last page again to trigger a fieldupdates refresh | |||
// emit the last page again to trigger a fieldupdates refresh | this.relationshipsRd$.next(this.relationshipsRd$.getValue()); | |||
this.relationshipsRd$.next(this.relationshipsRd$.getValue()); | }); | |||
}); | } | |||
}); | }); | |||
}; | }; | |||
modalComp.deselect = (...selectableObjects: SearchResult<Item>[]) => { | modalComp.deselect = (...selectableObjects: SearchResult<Item>[]) => { | |||
selectableObjects.forEach((searchResult) => { | selectableObjects.forEach((searchResult) => { | |||
const relatedItem: Item = searchResult.indexableObject; | const relatedItem: Item = searchResult.indexableObject; | |||
this.objectUpdatesService.removeSingleFieldUpdate(this.url, this.relatio | ||||
nshipType.id + '-' + relatedItem.uuid); | const foundIndex = modalComp.toAdd.findIndex( el => el.indexableObject.u | |||
this.getFieldUpdatesForRelatedItem(relatedItem) | uid === relatedItem.uuid); | |||
.subscribe((identifiables) => | ||||
identifiables.forEach((identifiable) => | if (foundIndex !== -1) { | |||
this.objectUpdatesService.saveRemoveFieldUpdate(this.url, identifi | modalComp.toAdd.splice(foundIndex,1); | |||
able) | } else { | |||
) | modalComp.toRemove.push(searchResult); | |||
); | } | |||
}); | ||||
}; | ||||
modalComp.submitEv = () => { | ||||
const subscriptions = []; | ||||
modalComp.toAdd.forEach((searchResult: SearchResult<Item>) => { | ||||
const relatedItem = searchResult.indexableObject; | ||||
subscriptions.push(this.relationshipService.getNameVariant(this.listId, | ||||
relatedItem.uuid).pipe( | ||||
map((nameVariant) => { | ||||
const update = { | ||||
uuid: this.relationshipType.id + '-' + searchResult.indexableObject. | ||||
uuid, | ||||
nameVariant, | ||||
type: this.relationshipType, | ||||
relatedItem, | ||||
} as RelationshipIdentifiable; | ||||
this.objectUpdatesService.saveAddFieldUpdate(this.url, update); | ||||
return update; | ||||
}) | ||||
)); | ||||
}); | ||||
modalComp.toRemove.forEach( (searchResult) => { | ||||
subscriptions.push(this.relationshipService.getNameVariant(this.listId, | ||||
searchResult.indexableObjectuuid).pipe( | ||||
switchMap((nameVariant) => { | ||||
return this.getRelationFromId(searchResult.indexableObject).pipe( | ||||
map( (relationship: Relationship) => { | ||||
const update = { | ||||
uuid: relationship.id, | ||||
nameVariant, | ||||
type: this.relationshipType, | ||||
relationship, | ||||
} as RelationshipIdentifiable; | ||||
this.objectUpdatesService.saveRemoveFieldUpdate(this.url,update) | ||||
; | ||||
return update; | ||||
}) | ||||
); | ||||
}) | ||||
)); | ||||
}); | ||||
observableCombineLatest(subscriptions).subscribe( (res) => { | ||||
// Wait until the states changes since there are multiple items | ||||
setTimeout( () => { | ||||
this.submit.emit(); | ||||
},1000); | ||||
modalComp.isPending = true; | ||||
}); | ||||
}; | ||||
modalComp.discardEv = () => { | ||||
modalComp.toAdd.forEach( (searchResult) => { | ||||
this.selectableListService.deselectSingle(this.listId,searchResult); | ||||
}); | ||||
modalComp.toRemove.forEach( (searchResult) => { | ||||
this.selectableListService.selectSingle(this.listId,searchResult); | ||||
}); | }); | |||
this.loading$.next(true); | modalComp.toAdd = []; | |||
// emit the last page again to trigger a fieldupdates refresh | modalComp.toRemove = []; | |||
this.relationshipsRd$.next(this.relationshipsRd$.getValue()); | ||||
}; | }; | |||
this.relatedEntityType$ | this.relatedEntityType$ | |||
.pipe(take(1)) | .pipe(take(1)) | |||
.subscribe((relatedEntityType) => { | .subscribe((relatedEntityType) => { | |||
modalComp.relationshipOptions = Object.assign( | modalComp.relationshipOptions = Object.assign( | |||
new RelationshipOptions(), { | new RelationshipOptions(), { | |||
relationshipType: relatedEntityType.label, | relationshipType: relatedEntityType.label, | |||
// filter: this.getRelationshipMessageKey(), | ||||
searchConfiguration: relatedEntityType.label.toLowerCase(), | searchConfiguration: relatedEntityType.label.toLowerCase(), | |||
nameVariants: true, | nameVariants: 'true', | |||
} | } | |||
); | ); | |||
}); | }); | |||
this.selectableListService.deselectAll(this.listId); | this.selectableListService.deselectAll(this.listId); | |||
this.updates$.pipe( | } | |||
switchMap((updates) => | ||||
Object.values(updates).length > 0 ? | getRelationFromId(relatedItem) { | |||
observableCombineLatest( | return this.currentItemIsLeftItem$.pipe( | |||
Object.values(updates) | ||||
.filter((update) => update.changeType !== FieldChangeType.REMOVE) | ||||
.map((update) => { | ||||
const field = update.field as RelationshipIdentifiable; | ||||
if (field.relationship) { | ||||
return this.getRelatedItem(field.relationship); | ||||
} else { | ||||
return observableOf(field.relatedItem); | ||||
} | ||||
}) | ||||
) : observableOf([]) | ||||
), | ||||
take(1), | take(1), | |||
map((items) => items.map((item) => { | switchMap( isLeft => { | |||
const searchResult = new ItemSearchResult(); | let apiCall; | |||
searchResult.indexableObject = item; | if (isLeft) { | |||
searchResult.hitHighlights = {}; | apiCall = this.relationshipService.searchByItemsAndType( this.relation | |||
return searchResult; | shipType.id, this.item.uuid, this.relationshipType.leftwardType ,[relatedItem.id | |||
})), | ] ).pipe( | |||
).subscribe((items) => { | getFirstSucceededRemoteData(), | |||
this.selectableListService.select(this.listId, items); | getRemoteDataPayload(), | |||
}); | ); | |||
} else { | ||||
apiCall = this.relationshipService.searchByItemsAndType( this.relation | ||||
shipType.id, this.item.uuid, this.relationshipType.rightwardType ,[relatedItem.i | ||||
d] ).pipe( | ||||
getFirstSucceededRemoteData(), | ||||
getRemoteDataPayload(), | ||||
); | ||||
} | ||||
return apiCall.pipe( | ||||
map( (res: PaginatedList<Relationship>) => res.page[0]) | ||||
); | ||||
} | ||||
)); | ||||
} | } | |||
/** | /** | |||
* Get the existing field updates regarding a relationship with a given item | * Get the existing field updates regarding a relationship with a given item | |||
* @param relatedItem The item for which to get the existing field updates | * @param relatedItem The item for which to get the existing field updates | |||
*/ | */ | |||
private getFieldUpdatesForRelatedItem(relatedItem: Item): Observable<Relations hipIdentifiable[]> { | private getFieldUpdatesForRelatedItem(relatedItem: Item): Observable<Relations hipIdentifiable[]> { | |||
return this.updates$.pipe( | return this.updates$.pipe( | |||
take(1), | take(1), | |||
map((updates) => Object.values(updates) | map((updates) => Object.values(updates) | |||
.map((update) => update.field as RelationshipIdentifiable) | .map((update) => update.field as RelationshipIdentifiable) | |||
.filter((field) => field.relationship) | .filter((field) => field.relationship) | |||
), | ), | |||
mergeMap((identifiables) => | mergeMap((identifiables) => | |||
observableCombineLatest( | observableCombineLatest( | |||
identifiables.map((identifiable) => this.getRelatedItem(identifiable.r elationship)) | identifiables.map((identifiable) => this.getRelatedItem(identifiable.r elationship)) | |||
).pipe( | ).pipe( | |||
defaultIfEmpty([]), | defaultIfEmpty([]), | |||
map((relatedItems) => | map((relatedItems) => { | |||
identifiables.filter((identifiable, index) => relatedItems[index].uu | return identifiables.filter( (identifiable, index) => { | |||
id === relatedItem.uuid) | return relatedItems[index].uuid === relatedItem.uuid; | |||
}); | ||||
} | ||||
), | ), | |||
) | ) | |||
), | ) | |||
); | ||||
} | ||||
/** | ||||
* Check if the given item is related with the item we are editing relationshi | ||||
ps | ||||
* @param relatedItem The item for which to get the existing field updates | ||||
*/ | ||||
private getIsRelatedItem(relatedItem: Item): Observable<boolean> { | ||||
return this.currentItemIsLeftItem$.pipe( | ||||
take(1), | ||||
map( isLeft => { | ||||
if (isLeft) { | ||||
const listOfRelatedItems = this.item.allMetadataValues( 'relation.' + | ||||
this.relationshipType.leftwardType ); | ||||
return !!listOfRelatedItems.find( (uuid) => uuid === relatedItem.uuid | ||||
); | ||||
} else { | ||||
const listOfRelatedItems = this.item.allMetadataValues( 'relation.' + | ||||
this.relationshipType.rightwardType ); | ||||
return !!listOfRelatedItems.find( (uuid) => uuid === relatedItem.uuid | ||||
); | ||||
} | ||||
}) | ||||
); | ); | |||
} | } | |||
/** | /** | |||
* Get the related item for a given relationship | * Get the related item for a given relationship | |||
* @param relationship The relationship for which to get the related item | * @param relationship The relationship for which to get the related item | |||
*/ | */ | |||
private getRelatedItem(relationship: Relationship): Observable<Item> { | private getRelatedItem(relationship: Relationship): Observable<Item> { | |||
return this.relationshipService.isLeftItem(relationship, this.item).pipe( | return this.relationshipService.isLeftItem(relationship, this.item).pipe( | |||
switchMap((isLeftItem) => isLeftItem ? relationship.rightItem : relationsh ip.leftItem), | switchMap((isLeftItem) => isLeftItem ? relationship.rightItem : relationsh ip.leftItem), | |||
getFirstSucceededRemoteData(), | getFirstSucceededRemoteData(), | |||
getRemoteDataPayload(), | getRemoteDataPayload(), | |||
) as Observable<Item>; | ) as Observable<Item>; | |||
} | } | |||
ngOnInit(): void { | ngOnInit(): void { | |||
// store the left and right type of the relationship in a single observable | // store the left and right type of the relationship in a single observable | |||
this.relationshipLeftAndRightType$ = observableCombineLatest([ | this.relationshipLeftAndRightType$ = observableCombineLatest([ | |||
this.relationshipType.leftType, | this.relationshipType.leftType, | |||
this.relationshipType.rightType, | this.relationshipType.rightType, | |||
].map((type) => type.pipe( | ].map((type) => type.pipe( | |||
getFirstSucceededRemoteData(), | getFirstSucceededRemoteData(), | |||
getRemoteDataPayload(), | getRemoteDataPayload(), | |||
))) as Observable<[ItemType, ItemType]>; | ))) as Observable<[ItemType, ItemType]>; | |||
this.relatedEntityType$ = this.relationshipLeftAndRightType$.pipe( | this.relatedEntityType$ = this.relationshipLeftAndRightType$.pipe( | |||
End of changes. 24 change blocks. | ||||
91 lines changed or deleted | 189 lines changed or added |