diff --git a/src/app/core/cache/builders/normalized-object-build.service.ts b/src/app/core/cache/builders/normalized-object-build.service.ts index 936548cdd4..69d7454d2d 100644 --- a/src/app/core/cache/builders/normalized-object-build.service.ts +++ b/src/app/core/cache/builders/normalized-object-build.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { NormalizedObject } from '../models/normalized-object.model'; import { getMapsToType, getRelationships } from './build-decorators'; import { hasValue, isNotEmpty } from '../../../shared/empty.util'; -import { TypedObject } from '../object-cache.reducer'; +import { CacheableObject, TypedObject } from '../object-cache.reducer'; /** * Return true if halObj has a value for `_links.self` @@ -34,14 +34,13 @@ export class NormalizedObjectBuildService { * * @param {TDomain} domainModel a domain model */ - normalize(domainModel: T): NormalizedObject { + normalize(domainModel: T): NormalizedObject { const normalizedConstructor = getMapsToType((domainModel as any).type); const relationships = getRelationships(normalizedConstructor) || []; - const normalizedModel = Object.assign({}, domainModel) as any; relationships.forEach((key: string) => { - if (hasValue(domainModel[key])) { - domainModel[key] = undefined; + if (hasValue(normalizedModel[key])) { + normalizedModel[key] = normalizedModel._links[key]; } }); return normalizedModel; diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index 0a1ba0b9e0..48c5090102 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -116,7 +116,7 @@ export class RemoteDataBuildService { const requestEntry$ = href$.pipe(getRequestFromRequestHref(this.requestService)); const tDomainList$ = requestEntry$.pipe( getResourceLinksFromResponse(), - flatMap((resourceUUIDs: string[]) => { + switchMap((resourceUUIDs: string[]) => { return this.objectCache.getList(resourceUUIDs).pipe( map((normList: Array>) => { return normList.map((normalized: NormalizedObject) => { diff --git a/src/app/core/cache/object-cache.service.ts b/src/app/core/cache/object-cache.service.ts index 6a07b8074d..598fb3937d 100644 --- a/src/app/core/cache/object-cache.service.ts +++ b/src/app/core/cache/object-cache.service.ts @@ -99,13 +99,13 @@ export class ObjectCacheService { getObjectBySelfLink(selfLink: string): Observable> { return this.getBySelfLink(selfLink).pipe( map((entry: ObjectCacheEntry) => { - if (isNotEmpty(entry.patches)) { - const flatPatch: Operation[] = [].concat(...entry.patches.map((patch) => patch.operations)); - const patchedData = applyPatch(entry.data, flatPatch, undefined, false).newDocument; - return Object.assign({}, entry, { data: patchedData }); - } else { + // if (isNotEmpty(entry.patches)) { + // const flatPatch: Operation[] = [].concat(...entry.patches.map((patch) => patch.operations)); + // const patchedData = applyPatch(entry.data, flatPatch, undefined, false).newDocument; + // return Object.assign({}, entry, { data: patchedData }); + // } else { return entry; - } + // } } ), map((entry: ObjectCacheEntry) => { diff --git a/src/app/core/data/default-change-analyzer.service.ts b/src/app/core/data/default-change-analyzer.service.ts index cd30479f6d..862c0e5b85 100644 --- a/src/app/core/data/default-change-analyzer.service.ts +++ b/src/app/core/data/default-change-analyzer.service.ts @@ -4,6 +4,7 @@ import { ChangeAnalyzer } from './change-analyzer'; import { Injectable } from '@angular/core'; import { CacheableObject } from '../cache/object-cache.reducer'; import { NormalizedObject } from '../cache/models/normalized-object.model'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; /** * A class to determine what differs between two @@ -11,6 +12,8 @@ import { NormalizedObject } from '../cache/models/normalized-object.model'; */ @Injectable() export class DefaultChangeAnalyzer implements ChangeAnalyzer { + constructor(private normalizeService: NormalizedObjectBuildService) { + } /** * Compare the metadata of two CacheableObject and return the differences as @@ -22,6 +25,6 @@ export class DefaultChangeAnalyzer implements ChangeA * The second object to compare */ diff(object1: T | NormalizedObject, object2: T | NormalizedObject): Operation[] { - return compare(object1, object2); + return compare(this.normalizeService.normalize(object1), this.normalizeService.normalize(object2)); } } diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index 39c117d383..8281d29cca 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -264,7 +264,6 @@ export class RelationshipService extends DataService { .pipe( getSucceededRemoteData(), isNotEmptyOperator(), - tap((t) => console.log(t)), map((relationshipListRD: RemoteData>) => relationshipListRD.payload.page), mergeMap((relationships: Relationship[]) => { return observableCombineLatest(...relationships.map((relationship: Relationship) => { diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-suggestions/person-input-suggestions.component.ts b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-suggestions/person-input-suggestions.component.ts index 4975d5d2ff..5154c168df 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-suggestions/person-input-suggestions.component.ts +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-suggestions/person-input-suggestions.component.ts @@ -46,5 +46,4 @@ export class PersonInputSuggestionsComponent extends InputSuggestionsComponent i this.queryInput.nativeElement.focus(); return false; } - } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts index 25cfc971e4..4eb58688cf 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts @@ -57,7 +57,7 @@ import { DYNAMIC_FORM_CONTROL_TYPE_DSDATEPICKER } from './models/date-picker/dat import { DYNAMIC_FORM_CONTROL_TYPE_LOOKUP } from './models/lookup/dynamic-lookup.model'; import { DynamicListCheckboxGroupModel } from './models/list/dynamic-list-checkbox-group.model'; import { DynamicListRadioGroupModel } from './models/list/dynamic-list-radio-group.model'; -import { hasValue, isNotEmpty, isNotEmptyOperator, isNotUndefined } from '../../../empty.util'; +import { hasValue, isNotEmpty, isNotUndefined } from '../../../empty.util'; import { DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_NAME } from './models/lookup/dynamic-lookup-name.model'; import { DsDynamicTagComponent } from './models/tag/dynamic-tag.component'; import { DsDatePickerComponent } from './models/date-picker/date-picker.component'; @@ -70,7 +70,7 @@ import { DsDynamicFormArrayComponent } from './models/array-group/dynamic-form-a import { DsDynamicRelationGroupComponent } from './models/relation-group/dynamic-relation-group.components'; import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './models/relation-group/dynamic-relation-group.model'; import { DsDatePickerInlineComponent } from './models/date-picker-inline/dynamic-date-picker-inline.component'; -import { map, switchMap, take, tap } from 'rxjs/operators'; +import { map, switchMap, tap } from 'rxjs/operators'; import { SelectableListState } from '../../../object-list/selectable-list/selectable-list.reducer'; import { Observable } from 'rxjs'; import { SearchResult } from '../../../search/search-result.model'; @@ -81,7 +81,6 @@ import { SelectableListService } from '../../../object-list/selectable-list/sele import { DsDynamicDisabledComponent } from './models/disabled/dynamic-disabled.component'; import { DYNAMIC_FORM_CONTROL_TYPE_DISABLED } from './models/disabled/dynamic-disabled.model'; import { DsDynamicLookupRelationModalComponent } from './relation-lookup-modal/dynamic-lookup-relation-modal.component'; -import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators'; import { RemoteData } from '../../../../core/data/remote-data'; import { Item } from '../../../../core/shared/item.model'; @@ -92,6 +91,7 @@ import { AppState } from '../../../../app.reducer'; import { SubmissionObjectDataService } from '../../../../core/submission/submission-object-data.service'; import { SubmissionObject } from '../../../../core/submission/models/submission-object.model'; import { PaginatedList } from '../../../../core/data/paginated-list'; +import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type | null { switch (model.type) { @@ -224,7 +224,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo this.item$.pipe( switchMap((item: Item) => this.relationService.getRelatedItemsByLabel(item, this.model.relationship.relationshipType)), - map((items: RemoteData>) => items.payload.page.map((item) => Object.assign(new SearchResult(), { indexableObject: item }))), + map((items: RemoteData>) => items.payload.page.map((item) => Object.assign(new ItemSearchResult(), { indexableObject: item }))), ).subscribe((relatedItems: SearchResult[]) => this.selectableListService.select(this.listId, relatedItems)); this.relationships$ = this.selectableListService.getSelectableList(this.listId).pipe( 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 25ff9d8d50..e008f839e6 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 @@ -12,7 +12,7 @@ import { RelationshipOptions } from '../../models/relationship-options.model'; import { SearchResult } from '../../../../search/search-result.model'; import { Item } from '../../../../../core/shared/item.model'; import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../../core/shared/operators'; -import { AddRelationshipAction, RemoveRelationshipAction } from './relationship.actions'; +import { AddRelationshipAction, RemoveRelationshipAction, UpdateRelationshipAction } from './relationship.actions'; import { RelationshipService } from '../../../../../core/data/relationship.service'; import { RelationshipTypeService } from '../../../../../core/data/relationship-type.service'; import { Store } from '@ngrx/store'; @@ -60,7 +60,7 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy this.selection$ = this.selectableListService .getSelectableList(this.listId) .pipe(map((listState: SelectableListState) => hasValue(listState) && hasValue(listState.selection) ? listState.selection : [])); - this.selection$.subscribe((selection) => + this.selection$.pipe(take(1)).subscribe((selection) => selection.map((s: SearchResult) => this.addNameVariantSubscription(s)) ); if (this.relationshipOptions.nameVariants) { @@ -76,13 +76,8 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy this.zone.runOutsideAngular( () => { const obs: Observable = combineLatest(...selectableObjects.map((sri: SearchResult) => { - const nameVariant$ = this.relationshipService.getNameVariant(this.listId, sri.indexableObject.uuid); - this.subMap[sri.indexableObject.uuid] = nameVariant$ - .pipe(skip(1)) - .subscribe((nameVariant: string) => - this.relationshipService.updateNameVariant(this.item, sri.indexableObject, this.relationshipOptions.relationshipType, nameVariant).subscribe() - ); - return nameVariant$ + this.addNameVariantSubscription(sri); + return this.relationshipService.getNameVariant(this.listId, sri.indexableObject.uuid) .pipe( take(1), map((nameVariant: string) => { @@ -106,9 +101,9 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy addNameVariantSubscription(sri: SearchResult) { const nameVariant$ = this.relationshipService.getNameVariant(this.listId, sri.indexableObject.uuid); - this.subMap[sri.indexableObject.uuid] = nameVariant$.pipe(switchMap((nameVariant: string) => - this.relationshipService.updateNameVariant(this.item, sri.indexableObject, this.relationshipOptions.relationshipType, nameVariant) - )).subscribe() + this.subMap[sri.indexableObject.uuid] = nameVariant$.pipe( + skip(1), + ).subscribe((nameVariant: string) => this.store.dispatch(new UpdateRelationshipAction(this.item, sri.indexableObject, this.relationshipOptions.relationshipType, nameVariant))) } deselect(...selectableObjects: SearchResult[]) { @@ -117,8 +112,7 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy this.subMap[object.indexableObject.uuid].unsubscribe(); this.store.dispatch(new RemoveRelationshipAction(this.item, object.indexableObject, this.relationshipOptions.relationshipType)); }) - ) - ; + ); } setExistingNameVariants() { 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 a988b0bbea..57375bd380 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,6 +8,7 @@ import { Item } from '../../../../../core/shared/item.model'; export const RelationshipActionTypes = { ADD_RELATIONSHIP: type('dspace/relationship/ADD_RELATIONSHIP'), REMOVE_RELATIONSHIP: type('dspace/relationship/REMOVE_RELATIONSHIP'), + UPDATE_RELATIONSHIP: type('dspace/relationship/UPDATE_RELATIONSHIP'), }; /* tslint:disable:max-classes-per-file */ @@ -42,6 +43,34 @@ export class AddRelationshipAction implements Action { } } +export class UpdateRelationshipAction implements Action { + type = RelationshipActionTypes.UPDATE_RELATIONSHIP; + + payload: { + item1: Item; + item2: Item; + relationshipType: string; + nameVariant: string; + }; + + /** + * Create a new UpdateRelationshipAction + * + * @param item1 The first item in the relationship + * @param item2 The second item in the relationship + * @param relationshipType The label of the relationshipType + * @param nameVariant The nameVariant of the relationshipType + */ + constructor( + item1: Item, + item2: Item, + relationshipType: string, + nameVariant?: string + ) { + this.payload = { item1, item2, relationshipType, nameVariant }; + } +} + /** * An ngrx action to remove an existing 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 a3b2b2989c..9ac8e93f58 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,9 +3,9 @@ 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, RemoveRelationshipAction } from './relationship.actions'; +import { AddRelationshipAction, RelationshipAction, RelationshipActionTypes, RemoveRelationshipAction, UpdateRelationshipAction } from './relationship.actions'; import { Item } from '../../../../../core/shared/item.model'; -import { hasNoValue, hasValueOperator } from '../../../../empty.util'; +import { hasNoValue, hasValue, hasValueOperator } from '../../../../empty.util'; import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model'; import { RelationshipType } from '../../../../../core/shared/item-relationships/relationship-type.model'; import { RelationshipTypeService } from '../../../../../core/data/relationship-type.service'; @@ -24,6 +24,11 @@ export class RelationshipEffects { [identifier: string]: BehaviorSubject } = {}; + private nameVariantUpdates: { + [identifier: string]: string + } = {}; + + private initialActionMap: { [identifier: string]: string } = {}; @@ -47,7 +52,16 @@ export class RelationshipEffects { ).subscribe( (type) => { if (this.initialActionMap[identifier] === type) { - type === RelationshipActionTypes.ADD_RELATIONSHIP ? this.addRelationship(item1, item2, relationshipType, (action as AddRelationshipAction).payload.nameVariant) : this.removeRelationship(item1, item2, relationshipType); + if (type === RelationshipActionTypes.ADD_RELATIONSHIP) { + let nameVariant = (action as AddRelationshipAction).payload.nameVariant; + if (hasValue(this.nameVariantUpdates[identifier])) { + nameVariant = this.nameVariantUpdates[identifier]; + delete this.nameVariantUpdates[identifier]; + } + this.addRelationship(item1, item2, relationshipType, nameVariant) + } else { + this.removeRelationship(item1, item2, relationshipType); + } } delete this.debounceMap[identifier]; delete this.initialActionMap[identifier]; @@ -60,6 +74,25 @@ export class RelationshipEffects { ) ); + @Effect({ dispatch: false }) updateNameVariantsActions$ = this.actions$ + .pipe( + ofType(RelationshipActionTypes.UPDATE_RELATIONSHIP), + map((action: UpdateRelationshipAction) => { + const { item1, item2, relationshipType, nameVariant } = action.payload; + const identifier: string = this.createIdentifier(item1, item2, relationshipType); + const inProgress = hasValue(this.debounceMap[identifier]); + if (inProgress) { + this.nameVariantUpdates[identifier] = nameVariant; + } else { + this.relationshipService.updateNameVariant(item1, item2, relationshipType, nameVariant) + .pipe(take(1)) + .subscribe(); + } + } + ) + ); + + constructor(private actions$: Actions, private relationshipService: RelationshipService, private relationshipTypeService: RelationshipTypeService, diff --git a/src/app/shared/search/search-labels/search-labels.component.html b/src/app/shared/search/search-labels/search-labels.component.html index d5ee8d5832..f6af072c51 100644 --- a/src/app/shared/search/search-labels/search-labels.component.html +++ b/src/app/shared/search/search-labels/search-labels.component.html @@ -1,9 +1,5 @@ - - {{('search.filters.applied.' + key) | translate}}: {{normalizeFilterValue(values)}} - × - +
+ + + +