65711: name variants in store

This commit is contained in:
lotte
2019-10-23 16:25:01 +02:00
parent 9d5f1b09bc
commit f53ebba096
11 changed files with 182 additions and 13 deletions

View File

@@ -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<AppState> = {
@@ -64,7 +66,8 @@ export const appReducers: ActionReducerMap<AppState> = {
cssVariables: cssVariablesReducer,
menus: menusReducer,
objectSelection: objectSelectionReducer,
selectableLists: selectableListReducer
selectableLists: selectableListReducer,
relationshipLists: relationshipListReducer
};
export const routerStateSelector = (state: AppState) => state.router;

View File

@@ -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<AppState, RelationshipListState> => {
return keySelector<RelationshipListState>(listID, relationshipListsStateSelector);
};
const relationshipStateSelector = (listID: string, itemID: string): MemoizedSelector<AppState, RelationshipState> => {
return keySelector<RelationshipState>(itemID, relationshipListStateSelector(listID));
};
/**
* The service handling all relationship requests
@@ -44,7 +58,8 @@ export class RelationshipService extends DataService<Relationship> {
protected objectCache: ObjectCacheService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<Relationship>) {
protected comparator: DefaultChangeAnalyzer<Relationship>,
protected appStore: Store<AppState>) {
super();
}
@@ -272,4 +287,17 @@ export class RelationshipService extends DataService<Relationship> {
);
}
public setNameVariant(listID: string, itemID: string, nameVariant: string) {
this.appStore.dispatch(new SetNameVariantAction(listID, itemID, nameVariant));
}
public getNameVariant(listID: string, itemID: string): Observable<string> {
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));
}
}

View File

@@ -1,6 +1,6 @@
<ds-item-type-badge [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<ds-person-input-suggestions [suggestions]="suggestions" (typeSuggestion)="filter($event)"></ds-person-input-suggestions>
<ds-person-input-suggestions [suggestions]="suggestions" (typeSuggestion)="filter($event)" [(ngModel)]="selected" (clickSuggestion)="select($event)" (submitSuggestion)="select($event)"></ds-person-input-suggestions>
<!-- <select>-->
<!-- <option [value]="firstMetadataValue('person.familyName') + ', ' + firstMetadataValue('person.givenName')" [innerHTML]="firstMetadataValue('person.familyName') + ', ' + firstMetadataValue('person.givenName')"></option>-->
<!-- <option *ngFor="let value of allMetadataValues('dc.title.alternative')" [value]="value" [innerHTML]="value"></option>-->

View File

@@ -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<ItemSearchResult, Item> 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);
}
}

View File

@@ -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<Item>) => this.relationshipService.getItemRelationshipsByLabel(itemRD.payload, this.relationship.relationshipType)),
getSucceededRemoteData(),
getRemoteDataPayload(),
map((relationships: PaginatedList<Relationship>) => relationships.page.)
);
combineLatest(this.itemRD$, this.selection$)
.pipe(
take(1),
switchMap(([itemRD, objects]: [RemoteData<Item>, 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() {

View File

@@ -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
*/

View File

@@ -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(

View File

@@ -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 } });
}

View File

@@ -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;
}
/**

View File

@@ -17,6 +17,11 @@ export class AbstractListableElementComponent<T extends ListableObject> {
*/
@Input() linkType: CollectionElementLinkType;
/**
* The identifier of the list this element resides in
*/
@Input() listID: string;
/**
* The available link types
*/

View File

@@ -26,7 +26,7 @@
(click)="selectRadio(!checked, object)">
</ng-container>
</span>
<ds-listable-object-component-loader [object]="object" [viewMode]="viewMode" [index]="i" [context]="context" [linkType]="linkType"></ds-listable-object-component-loader>
<ds-listable-object-component-loader [object]="object" [viewMode]="viewMode" [index]="i" [context]="context" [linkType]="linkType" [listID]="selectionConfig?.listId"></ds-listable-object-component-loader>
</li>
</ul>
</ds-pagination>