mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
63184: Edit-Relationships left/right Item refactoring
This commit is contained in:
@@ -21,6 +21,7 @@ import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
|||||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||||
import { RequestService } from '../../../core/data/request.service';
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
import { Subscription } from 'rxjs/internal/Subscription';
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
import { getRelationsByRelatedItemIds } from '../../simple/item-types/shared/item-relationships-utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-relationships',
|
selector: 'ds-item-relationships',
|
||||||
@@ -94,7 +95,7 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the currently selected related items back to relationships and send a delete request
|
* Resolve the currently selected related items back to relationships and send a delete request for each of the relationships found
|
||||||
* Make sure the lists are refreshed afterwards and notifications are sent for success and errors
|
* Make sure the lists are refreshed afterwards and notifications are sent for success and errors
|
||||||
*/
|
*/
|
||||||
public submit(): void {
|
public submit(): void {
|
||||||
@@ -105,26 +106,31 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
|||||||
map((fieldUpdates: FieldUpdate[]) => fieldUpdates.map((fieldUpdate: FieldUpdate) => fieldUpdate.field.uuid) as string[]),
|
map((fieldUpdates: FieldUpdate[]) => fieldUpdates.map((fieldUpdate: FieldUpdate) => fieldUpdate.field.uuid) as string[]),
|
||||||
isNotEmptyOperator()
|
isNotEmptyOperator()
|
||||||
);
|
);
|
||||||
const allRelationshipsAndRemovedItemIds$ = observableCombineLatest(
|
// Get all the relationships that should be removed
|
||||||
this.relationshipService.getItemRelationshipsArray(this.item),
|
const removedRelationships$ = removedItemIds$.pipe(
|
||||||
removedItemIds$
|
getRelationsByRelatedItemIds(this.item, this.relationshipService)
|
||||||
);
|
|
||||||
// Get all IDs of the relationships that should be removed
|
|
||||||
const removedRelationshipIds$ = allRelationshipsAndRemovedItemIds$.pipe(
|
|
||||||
map(([relationships, itemIds]) =>
|
|
||||||
relationships
|
|
||||||
.filter((relationship: Relationship) => itemIds.indexOf(relationship.leftId) > -1 || itemIds.indexOf(relationship.rightId) > -1)
|
|
||||||
.map((relationship: Relationship) => relationship.id))
|
|
||||||
);
|
);
|
||||||
// Request a delete for every relationship found in the observable created above
|
// Request a delete for every relationship found in the observable created above
|
||||||
removedRelationshipIds$.pipe(
|
removedRelationships$.pipe(
|
||||||
take(1),
|
take(1),
|
||||||
|
map((removedRelationships: Relationship[]) => removedRelationships.map((rel: Relationship) => rel.id)),
|
||||||
switchMap((removedIds: string[]) => observableZip(...removedIds.map((uuid: string) => this.relationshipService.deleteRelationship(uuid))))
|
switchMap((removedIds: string[]) => observableZip(...removedIds.map((uuid: string) => this.relationshipService.deleteRelationship(uuid))))
|
||||||
).subscribe((responses: RestResponse[]) => {
|
).subscribe((responses: RestResponse[]) => {
|
||||||
|
this.displayNotifications(responses);
|
||||||
|
this.reset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display notifications
|
||||||
|
* - Error notification for each failed response with their message
|
||||||
|
* - Success notification in case there's at least one successful response
|
||||||
|
* @param responses
|
||||||
|
*/
|
||||||
|
displayNotifications(responses: RestResponse[]) {
|
||||||
const failedResponses = responses.filter((response: RestResponse) => !response.isSuccessful);
|
const failedResponses = responses.filter((response: RestResponse) => !response.isSuccessful);
|
||||||
const successfulResponses = responses.filter((response: RestResponse) => response.isSuccessful);
|
const successfulResponses = responses.filter((response: RestResponse) => response.isSuccessful);
|
||||||
|
|
||||||
// Display an error notification for each failed request
|
|
||||||
failedResponses.forEach((response: ErrorResponse) => {
|
failedResponses.forEach((response: ErrorResponse) => {
|
||||||
this.notificationsService.error(this.getNotificationTitle('failed'), response.errorMessage);
|
this.notificationsService.error(this.getNotificationTitle('failed'), response.errorMessage);
|
||||||
});
|
});
|
||||||
@@ -135,10 +141,14 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
|||||||
// Send a notification that the removal was successful
|
// Send a notification that the removal was successful
|
||||||
this.notificationsService.success(this.getNotificationTitle('saved'), this.getNotificationContent('saved'));
|
this.notificationsService.success(this.getNotificationTitle('saved'), this.getNotificationContent('saved'));
|
||||||
}
|
}
|
||||||
// Reset the state of editing relationships
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the state of editing relationships
|
||||||
|
*/
|
||||||
|
reset() {
|
||||||
this.initializeOriginalFields();
|
this.initializeOriginalFields();
|
||||||
this.initializeUpdates();
|
this.initializeUpdates();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -7,11 +7,12 @@ import { hasValue } from '../../../../shared/empty.util';
|
|||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
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 { distinctUntilChanged, flatMap, map } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, flatMap, map, tap } from 'rxjs/operators';
|
||||||
import { of as observableOf, zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs';
|
import { of as observableOf, zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Operator for comparing arrays using a mapping function
|
* Operator for comparing arrays using a mapping function
|
||||||
@@ -120,3 +121,17 @@ export const relationsToRepresentations = (parentId: string, itemType: string, m
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operator for fetching an item's relationships, but filtered by related item IDs (essentially performing a reverse lookup)
|
||||||
|
* Only relationships where leftItem or rightItem's ID is present in the list provided will be returned
|
||||||
|
* @param item
|
||||||
|
* @param relationshipService
|
||||||
|
*/
|
||||||
|
export const getRelationsByRelatedItemIds = (item: Item, relationshipService: RelationshipService) =>
|
||||||
|
(source: Observable<string[]>): Observable<Relationship[]> =>
|
||||||
|
source.pipe(
|
||||||
|
flatMap((relatedItemIds: string[]) => relationshipService.getItemResolvedRelatedItemsAndRelationships(item).pipe(
|
||||||
|
map(([leftItems, rightItems, rels]) => rels.filter((rel: Relationship, index: number) => relatedItemIds.indexOf(leftItems[index].uuid) > -1 || relatedItemIds.indexOf(rightItems[index].uuid) > -1))
|
||||||
|
))
|
||||||
|
);
|
||||||
|
@@ -3,7 +3,7 @@ import { RequestService } from './request.service';
|
|||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { hasValue, hasValueOperator, isNotEmptyOperator } from '../../shared/empty.util';
|
import { hasValue, hasValueOperator, isNotEmptyOperator } from '../../shared/empty.util';
|
||||||
import { distinctUntilChanged, flatMap, map, switchMap, take, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, flatMap, map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
configureRequest,
|
configureRequest,
|
||||||
filterSuccessfulResponses,
|
filterSuccessfulResponses,
|
||||||
@@ -70,20 +70,35 @@ export class RelationshipService {
|
|||||||
* @param item
|
* @param item
|
||||||
*/
|
*/
|
||||||
getItemResolvedRelsAndTypes(item: Item): Observable<[Relationship[], RelationshipType[]]> {
|
getItemResolvedRelsAndTypes(item: Item): Observable<[Relationship[], RelationshipType[]]> {
|
||||||
const relationships$ = this.getItemRelationshipsArray(item);
|
|
||||||
|
|
||||||
const relationshipTypes$ = relationships$.pipe(
|
|
||||||
flatMap((rels: Relationship[]) =>
|
|
||||||
observableZip(...rels.map((rel: Relationship) => rel.relationshipType)).pipe(
|
|
||||||
map(([...arr]: Array<RemoteData<RelationshipType>>) => arr.map((d: RemoteData<RelationshipType>) => d.payload).filter((type) => hasValue(type)))
|
|
||||||
)
|
|
||||||
),
|
|
||||||
distinctUntilChanged(compareArraysUsingIds())
|
|
||||||
);
|
|
||||||
|
|
||||||
return observableCombineLatest(
|
return observableCombineLatest(
|
||||||
relationships$,
|
this.getItemRelationshipsArray(item),
|
||||||
relationshipTypes$
|
this.getItemRelationshipTypesArray(item)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a combined observable containing an array of all the item's relationship's left- and right-side items, as well as an array of the relationships their types
|
||||||
|
* This is used for easier access of a relationship's type and left and right items because they exist as observables
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
getItemResolvedRelatedItemsAndTypes(item: Item): Observable<[Item[], Item[], RelationshipType[]]> {
|
||||||
|
return observableCombineLatest(
|
||||||
|
this.getItemLeftRelatedItemArray(item),
|
||||||
|
this.getItemRightRelatedItemArray(item),
|
||||||
|
this.getItemRelationshipTypesArray(item)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a combined observable containing an array of all the item's relationship's left- and right-side items, as well as an array of the relationships themselves
|
||||||
|
* This is used for easier access of the relationship and their left and right items because they exist as observables
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
getItemResolvedRelatedItemsAndRelationships(item: Item): Observable<[Item[], Item[], Relationship[]]> {
|
||||||
|
return observableCombineLatest(
|
||||||
|
this.getItemLeftRelatedItemArray(item),
|
||||||
|
this.getItemRightRelatedItemArray(item),
|
||||||
|
this.getItemRelationshipsArray(item)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,17 +116,60 @@ export class RelationshipService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an item their relationship types in the form of an array
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
getItemRelationshipTypesArray(item: Item): Observable<RelationshipType[]> {
|
||||||
|
return this.getItemRelationshipsArray(item).pipe(
|
||||||
|
flatMap((rels: Relationship[]) =>
|
||||||
|
observableZip(...rels.map((rel: Relationship) => rel.relationshipType)).pipe(
|
||||||
|
map(([...arr]: Array<RemoteData<RelationshipType>>) => arr.map((d: RemoteData<RelationshipType>) => d.payload).filter((type) => hasValue(type))),
|
||||||
|
filter((arr) => arr.length === rels.length)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
distinctUntilChanged(compareArraysUsingIds())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an item his relationship's left-side related items in the form of an array
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
getItemLeftRelatedItemArray(item: Item): Observable<Item[]> {
|
||||||
|
return this.getItemRelationshipsArray(item).pipe(
|
||||||
|
flatMap((rels: Relationship[]) => observableZip(...rels.map((rel: Relationship) => rel.leftItem)).pipe(
|
||||||
|
map(([...arr]: Array<RemoteData<Item>>) => arr.map((rd: RemoteData<Item>) => rd.payload).filter((i) => hasValue(i))),
|
||||||
|
filter((arr) => arr.length === rels.length)
|
||||||
|
)),
|
||||||
|
distinctUntilChanged(compareArraysUsingIds())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an item his relationship's right-side related items in the form of an array
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
getItemRightRelatedItemArray(item: Item): Observable<Item[]> {
|
||||||
|
return this.getItemRelationshipsArray(item).pipe(
|
||||||
|
flatMap((rels: Relationship[]) => observableZip(...rels.map((rel: Relationship) => rel.rightItem)).pipe(
|
||||||
|
map(([...arr]: Array<RemoteData<Item>>) => arr.map((rd: RemoteData<Item>) => rd.payload).filter((i) => hasValue(i))),
|
||||||
|
filter((arr) => arr.length === rels.length)
|
||||||
|
)),
|
||||||
|
distinctUntilChanged(compareArraysUsingIds())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an array of an item their unique relationship type's labels
|
* Get an array of an item their unique relationship type's labels
|
||||||
* The array doesn't contain any duplicate labels
|
* The array doesn't contain any duplicate labels
|
||||||
* @param item
|
* @param item
|
||||||
*/
|
*/
|
||||||
getItemRelationshipLabels(item: Item): Observable<string[]> {
|
getItemRelationshipLabels(item: Item): Observable<string[]> {
|
||||||
return this.getItemResolvedRelsAndTypes(item).pipe(
|
return this.getItemResolvedRelatedItemsAndTypes(item).pipe(
|
||||||
map(([relsCurrentPage, relTypesCurrentPage]) => {
|
map(([leftItems, rightItems, relTypesCurrentPage]) => {
|
||||||
return relTypesCurrentPage.map((type, index) => {
|
return relTypesCurrentPage.map((type, index) => {
|
||||||
const relationship = relsCurrentPage[index];
|
if (leftItems[index].uuid === item.uuid) {
|
||||||
if (relationship.leftId === item.uuid) {
|
|
||||||
return type.leftLabel;
|
return type.leftLabel;
|
||||||
} else {
|
} else {
|
||||||
return type.rightLabel;
|
return type.rightLabel;
|
||||||
@@ -128,7 +186,7 @@ export class RelationshipService {
|
|||||||
*/
|
*/
|
||||||
getRelatedItems(item: Item): Observable<Item[]> {
|
getRelatedItems(item: Item): Observable<Item[]> {
|
||||||
return this.getItemRelationshipsArray(item).pipe(
|
return this.getItemRelationshipsArray(item).pipe(
|
||||||
relationsToItems(item.uuid, this.itemService)
|
relationsToItems(item.uuid)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +199,7 @@ export class RelationshipService {
|
|||||||
getRelatedItemsByLabel(item: Item, label: string): Observable<Item[]> {
|
getRelatedItemsByLabel(item: Item, label: string): Observable<Item[]> {
|
||||||
return this.getItemResolvedRelsAndTypes(item).pipe(
|
return this.getItemResolvedRelsAndTypes(item).pipe(
|
||||||
filterRelationsByTypeLabel(label),
|
filterRelationsByTypeLabel(label),
|
||||||
relationsToItems(item.uuid, this.itemService)
|
relationsToItems(item.uuid)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user