diff --git a/src/app/app.reducer.ts b/src/app/app.reducer.ts index 3622b52e83..ed7e7c559a 100644 --- a/src/app/app.reducer.ts +++ b/src/app/app.reducer.ts @@ -32,6 +32,7 @@ import { BitstreamFormatRegistryState } from './+admin/admin-registries/bitstream-formats/bitstream-format.reducers'; import { ObjectSelectionListState, objectSelectionReducer } from './shared/object-select/object-select.reducer'; +import { relationshipListReducer, RelationshipListsState } from './shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.reducer'; export interface AppState { router: fromRouter.RouterReducerState; @@ -48,6 +49,7 @@ export interface AppState { menus: MenusState; objectSelection: ObjectSelectionListState; selectableLists: SelectableListsState; + relationshipLists: RelationshipListsState; } export const appReducers: ActionReducerMap = { @@ -64,7 +66,8 @@ export const appReducers: ActionReducerMap = { cssVariables: cssVariablesReducer, menus: menusReducer, objectSelection: objectSelectionReducer, - selectableLists: selectableListReducer + selectableLists: selectableListReducer, + relationshipLists: relationshipListReducer }; export const routerStateSelector = (state: AppState) => state.router; diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index dc89f153f2..34ea75708e 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -19,13 +19,27 @@ import { compareArraysUsingIds, paginatedRelationsToItems, relationsToItems } fr import { ObjectCacheService } from '../cache/object-cache.service'; import { DataService } from './data.service'; import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; -import { Store } from '@ngrx/store'; +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 { SearchParam } from '../cache/models/search-param.model'; import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; +import { RemoveNameVariantAction, SetNameVariantAction } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.actions'; +import { AppState, keySelector } from '../../app.reducer'; +import { RelationshipListState, RelationshipState } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.reducer'; + + +const relationshipListsStateSelector = (state: AppState) => state.relationshipLists; + +const relationshipListStateSelector = (listID: string): MemoizedSelector => { + return keySelector(listID, relationshipListsStateSelector); +}; + +const relationshipStateSelector = (listID: string, itemID: string): MemoizedSelector => { + return keySelector(itemID, relationshipListStateSelector(listID)); +}; /** * The service handling all relationship requests @@ -44,7 +58,8 @@ export class RelationshipService extends DataService { protected objectCache: ObjectCacheService, protected notificationsService: NotificationsService, protected http: HttpClient, - protected comparator: DefaultChangeAnalyzer) { + protected comparator: DefaultChangeAnalyzer, + protected appStore: Store) { super(); } @@ -204,7 +219,7 @@ export class RelationshipService extends DataService { if (options) { findAllOptions = Object.assign(new FindAllOptions(), options); } - const searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ]; + const searchParams = [new SearchParam('label', label), new SearchParam('dso', item.id)]; if (findAllOptions.searchParams) { findAllOptions.searchParams = [...findAllOptions.searchParams, ...searchParams]; } else { @@ -272,4 +287,17 @@ export class RelationshipService extends DataService { ); } + public setNameVariant(listID: string, itemID: string, nameVariant: string) { + this.appStore.dispatch(new SetNameVariantAction(listID, itemID, nameVariant)); + } + + public getNameVariant(listID: string, itemID: string): Observable { + return this.appStore.pipe( + select(relationshipStateSelector(listID, itemID)), map((state: RelationshipState) => hasValue(state) ? state.nameVariant : undefined) + ); + } + + public removeNameVariant(listID: string, itemID: string) { + this.appStore.dispatch(new RemoveNameVariantAction(listID, itemID)); + } } diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.html b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.html index 4675f649ac..baa3edcdc5 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.html +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.html @@ -1,6 +1,6 @@ - + diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts index febaf64e0d..1d7b4f42e4 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts @@ -5,6 +5,9 @@ import { listableObjectComponent } from '../../../../../shared/object-collection import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { Item } from '../../../../../core/shared/item.model'; import { Context } from '../../../../../core/shared/context.model'; +import { RelationshipService } from '../../../../../core/data/relationship.service'; +import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; +import { take } from 'rxjs/operators'; @listableObjectComponent('PersonSearchResult', ViewMode.ListElement, Context.Submission) @Component({ @@ -18,16 +21,31 @@ import { Context } from '../../../../../core/shared/context.model'; export class PersonSearchResultListSubmissionElementComponent extends SearchResultListElementComponent implements OnInit { suggestions: string[]; allSuggestions: string[]; + selected: string; + + constructor(protected truncatableService: TruncatableService, private relationshipService: RelationshipService) { + super(truncatableService); + } ngOnInit() { super.ngOnInit(); - const defaultValue = this.firstMetadataValue('person.familyName') + ', ' + this.firstMetadataValue('person.givenName') + const defaultValue = this.firstMetadataValue('person.familyName') + ', ' + this.firstMetadataValue('person.givenName'); const alternatives = this.allMetadataValues('dc.title.alternative'); this.allSuggestions = [defaultValue, ...alternatives]; this.suggestions = this.allSuggestions; + + this.relationshipService.getNameVariant(this.listID, this.dso.uuid) + .pipe(take(1)) + .subscribe((nameVariant: string) => { + this.selected = nameVariant || defaultValue; + }); } filter(query) { this.suggestions = this.allSuggestions.filter((suggestion) => suggestion.includes(query)); } + + select(value) { + this.relationshipService.setNameVariant(this.listID, this.dso.uuid, value); + } } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts index fc0cacfead..2562ede827 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts @@ -1,8 +1,8 @@ import { Component, NgZone, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; +import { combineLatest, Observable } from 'rxjs'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { hasValue } from '../../../../empty.util'; -import { map, tap } from 'rxjs/operators'; +import { map, switchMap, take, tap } from 'rxjs/operators'; import { SEARCH_CONFIG_SERVICE } from '../../../../../+my-dspace-page/my-dspace-page.component'; import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; import { SelectableListService } from '../../../../object-list/selectable-list/selectable-list.service'; @@ -11,7 +11,7 @@ import { ListableObject } from '../../../../object-collection/shared/listable-ob import { RelationshipOptions } from '../../models/relationship-options.model'; import { SearchResult } from '../../../../search/search-result.model'; import { Item } from '../../../../../core/shared/item.model'; -import { getSucceededRemoteData } from '../../../../../core/shared/operators'; +import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../../core/shared/operators'; import { RemoteData } from '../../../../../core/data/remote-data'; import { AddRelationshipAction, RemoveRelationshipAction } from './relationship.actions'; import { RelationshipService } from '../../../../../core/data/relationship.service'; @@ -19,6 +19,8 @@ import { RelationshipTypeService } from '../../../../../core/data/relationship-t import { Store } from '@ngrx/store'; import { AppState } from '../../../../../app.reducer'; import { Context } from '../../../../../core/shared/context.model'; +import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model'; +import { PaginatedList } from '../../../../../core/data/paginated-list'; @Component({ selector: 'ds-dynamic-lookup-relation-modal', @@ -56,6 +58,27 @@ export class DsDynamicLookupRelationModalComponent implements OnInit { if (this.relationship.nameVariants) { this.context = Context.Submission; } + this.itemRD$.pipe( + switchMap((itemRD: RemoteData) => this.relationshipService.getItemRelationshipsByLabel(itemRD.payload, this.relationship.relationshipType)), + getSucceededRemoteData(), + getRemoteDataPayload(), + map((relationships: PaginatedList) => relationships.page.) + ); + combineLatest(this.itemRD$, this.selection$) + .pipe( + take(1), + switchMap(([itemRD, objects]: [RemoteData, ListableObject[]]) => { + return combineLatest(objects.map((obj: Item) => this.relationshipService.getRelationshipsByRelatedItemIds(itemRD.payload, [obj.uuid]) + .pipe(take(1), map((rels: Relationship[]) => [rels[0], obj.uuid] as [Relationship, string]))) + ) + } + ) + ).subscribe((relations: [Relationship, string][]) => { + relations.forEach((([rel, id]: [Relationship, string]) => { + this.relationshipService.setNameVariant(this.listId, id, rel.) + })) + } + ) } close() { diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.actions.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.actions.ts index aebf505cb5..abff6622d7 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.actions.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.actions.ts @@ -8,10 +8,46 @@ import { Item } from '../../../../../core/shared/item.model'; export const RelationshipActionTypes = { ADD_RELATIONSHIP: type('dspace/relationship/ADD_RELATIONSHIP'), REMOVE_RELATIONSHIP: type('dspace/relationship/REMOVE_RELATIONSHIP'), + SET_NAME_VARIANT: type('dspace/relationship/SET_NAME_VARIANT'), + REMOVE_NAME_VARIANT: type('dspace/relationship/REMOVE_NAME_VARIANT'), }; /* tslint:disable:max-classes-per-file */ +export abstract class RelationshipListAction implements Action { + type; + payload: { + listID: string; + itemID: string; + }; + + constructor(listID: string, itemID: string) { + this.payload = { listID, itemID }; + } +} + +export class SetNameVariantAction extends RelationshipListAction { + type = RelationshipActionTypes.SET_NAME_VARIANT; + payload: { + listID: string; + itemID: string; + nameVariant: string; + }; + + constructor(listID: string, itemID: string, nameVariant: string) { + super(listID, itemID); + this.payload.nameVariant = nameVariant; + } +} + +export class RemoveNameVariantAction extends RelationshipListAction { + type = RelationshipActionTypes.REMOVE_NAME_VARIANT; + constructor(listID: string, itemID: string, ) { + super(listID, itemID); + } +} + + /** * An ngrx action to create a new relationship */ diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts index 2cb1a36dc7..a3b2b2989c 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts @@ -3,7 +3,7 @@ import { Actions, Effect, ofType } from '@ngrx/effects'; import { debounceTime, map, mergeMap, take, tap } from 'rxjs/operators'; import { BehaviorSubject } from 'rxjs'; import { RelationshipService } from '../../../../../core/data/relationship.service'; -import { AddRelationshipAction, RelationshipAction, RelationshipActionTypes } from './relationship.actions'; +import { AddRelationshipAction, RelationshipAction, RelationshipActionTypes, RemoveRelationshipAction } from './relationship.actions'; import { Item } from '../../../../../core/shared/item.model'; import { hasNoValue, hasValueOperator } from '../../../../empty.util'; import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model'; @@ -34,7 +34,7 @@ export class RelationshipEffects { */ @Effect({ dispatch: false }) mapLastActions$ = this.actions$ .pipe( - ofType(...Object.values(RelationshipActionTypes)), + ofType(RelationshipActionTypes.ADD_RELATIONSHIP, RelationshipActionTypes.REMOVE_RELATIONSHIP), map((action: RelationshipAction) => { const { item1, item2, relationshipType } = action.payload; const identifier: string = this.createIdentifier(item1, item2, relationshipType); @@ -74,7 +74,6 @@ export class RelationshipEffects { private addRelationship(item1: Item, item2: Item, relationshipType: string, nameVariant?: string) { const type1: string = item1.firstMetadataValue('relationship.type'); - // const type1: string = 'JournalVolume'; const type2: string = item2.firstMetadataValue('relationship.type'); return this.relationshipTypeService.getRelationshipTypeByLabelAndTypes(relationshipType, type1, type2) .pipe( diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.reducer.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.reducer.ts new file mode 100644 index 0000000000..393353d4b7 --- /dev/null +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.reducer.ts @@ -0,0 +1,51 @@ +/** + * Represents the state of all lists containing relationships in the store + */ +import { RelationshipActionTypes, RelationshipListAction, SetNameVariantAction } from './relationship.actions'; + +export type RelationshipListsState = { + [listID: string]: RelationshipListState; +} + +/** + * Represents the state of a single list containing relationships in the store + */ +export type RelationshipListState = { + [itemID: string]: RelationshipState; +} + +/** + * Represents the state of a relationship in a list in the store + */ +export type RelationshipState = { + nameVariant: string; +} + +/** + * Reducer that handles RelationshipListAction to update the RelationshipListsState + * @param {RelationshipListsState} state The initial RelationshipListsState + * @param {RelationshipListAction} action The Action to be performed on the state + * @returns {RelationshipListsState} The new, reduced RelationshipListsState + */ +export function relationshipListReducer(state: RelationshipListsState = {}, action: RelationshipListAction): RelationshipListsState { + switch (action.type) { + case RelationshipActionTypes.SET_NAME_VARIANT: { + const listState: RelationshipListState = state[action.payload.listID] || {}; + const nameVariant = (action as SetNameVariantAction).payload.nameVariant; + const newListState = setNameVariant(listState, action.payload.itemID, nameVariant); + return Object.assign({}, state, { [action.payload.listID]: newListState }); + } + case RelationshipActionTypes.REMOVE_NAME_VARIANT: { + const listState: RelationshipListState = state[action.payload.listID] || {}; + const newListState = setNameVariant(listState, action.payload.itemID, undefined); + return Object.assign({}, state, { [action.payload.listID]: newListState }); + } + default: { + return state; + } + } +} + +function setNameVariant(state: RelationshipListState, itemID: string, nameVariant: string) { + return Object.assign({}, state, { [itemID]: { nameVariant } }); +} diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts index 648298b0fe..25232efa1d 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts @@ -41,6 +41,11 @@ export class ListableObjectComponentLoaderComponent implements OnInit { */ @Input() linkType: CollectionElementLinkType; + /** + * The identifier of the list this element resides in + */ + @Input() listID: string; + /** * Directive hook used to place the dynamic child component */ @@ -62,6 +67,7 @@ export class ListableObjectComponentLoaderComponent implements OnInit { (componentRef.instance as any).object = this.object; (componentRef.instance as any).index = this.index; (componentRef.instance as any).linkType = this.linkType; + (componentRef.instance as any).listID = this.listID; } /** diff --git a/src/app/shared/object-collection/shared/object-collection-element/abstract-listable-element.component.ts b/src/app/shared/object-collection/shared/object-collection-element/abstract-listable-element.component.ts index c09f7df18c..d27fb331de 100644 --- a/src/app/shared/object-collection/shared/object-collection-element/abstract-listable-element.component.ts +++ b/src/app/shared/object-collection/shared/object-collection-element/abstract-listable-element.component.ts @@ -17,6 +17,11 @@ export class AbstractListableElementComponent { */ @Input() linkType: CollectionElementLinkType; + /** + * The identifier of the list this element resides in + */ + @Input() listID: string; + /** * The available link types */ diff --git a/src/app/shared/object-list/object-list.component.html b/src/app/shared/object-list/object-list.component.html index 81243ee0ec..770999b808 100644 --- a/src/app/shared/object-list/object-list.component.html +++ b/src/app/shared/object-list/object-list.component.html @@ -26,7 +26,7 @@ (click)="selectRadio(!checked, object)"> - +