mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
604 lines
22 KiB
TypeScript
604 lines
22 KiB
TypeScript
import { Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
|
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
|
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
|
import {
|
|
BehaviorSubject,
|
|
combineLatest as observableCombineLatest,
|
|
EMPTY,
|
|
from as observableFrom,
|
|
Observable,
|
|
Subscription
|
|
} from 'rxjs';
|
|
import {
|
|
RelationshipIdentifiable
|
|
} from '../../../../core/data/object-updates/object-updates.reducer';
|
|
import { RelationshipDataService } from '../../../../core/data/relationship-data.service';
|
|
import { Item } from '../../../../core/shared/item.model';
|
|
import {
|
|
defaultIfEmpty,
|
|
map,
|
|
mergeMap,
|
|
startWith,
|
|
switchMap,
|
|
take,
|
|
tap,
|
|
toArray,
|
|
concatMap
|
|
} from 'rxjs/operators';
|
|
import { hasNoValue, hasValue, hasValueOperator, isNotEmpty } from '../../../../shared/empty.util';
|
|
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
|
import {
|
|
RelationshipType
|
|
} from '../../../../core/shared/item-relationships/relationship-type.model';
|
|
import {
|
|
getAllSucceededRemoteData,
|
|
getFirstCompletedRemoteData,
|
|
getFirstSucceededRemoteData,
|
|
getFirstSucceededRemoteDataPayload,
|
|
getRemoteDataPayload,
|
|
} from '../../../../core/shared/operators';
|
|
import { ItemType } from '../../../../core/shared/item-relationships/item-type.model';
|
|
import {
|
|
DsDynamicLookupRelationModalComponent
|
|
} from '../../../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component';
|
|
import {
|
|
RelationshipOptions
|
|
} from '../../../../shared/form/builder/models/relationship-options.model';
|
|
import {
|
|
SelectableListService
|
|
} from '../../../../shared/object-list/selectable-list/selectable-list.service';
|
|
import {
|
|
ItemSearchResult
|
|
} from '../../../../shared/object-collection/shared/item-search-result.model';
|
|
import { FollowLinkConfig } from '../../../../shared/utils/follow-link-config.model';
|
|
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
|
import { RemoteData } from '../../../../core/data/remote-data';
|
|
import { Collection } from '../../../../core/shared/collection.model';
|
|
import {
|
|
PaginationComponentOptions
|
|
} from '../../../../shared/pagination/pagination-component-options.model';
|
|
import { PaginationService } from '../../../../core/pagination/pagination.service';
|
|
import { FieldUpdate } from '../../../../core/data/object-updates/field-update.model';
|
|
import { FieldUpdates } from '../../../../core/data/object-updates/field-updates.model';
|
|
import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model';
|
|
import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface';
|
|
import { itemLinksToFollow } from '../../../../shared/utils/relation-query.utils';
|
|
import { EditItemRelationshipsService } from '../edit-item-relationships.service';
|
|
import { RequestParam } from '../../../../core/cache/models/request-param.model';
|
|
|
|
@Component({
|
|
selector: 'ds-edit-relationship-list',
|
|
styleUrls: ['./edit-relationship-list.component.scss'],
|
|
templateUrl: './edit-relationship-list.component.html',
|
|
})
|
|
/**
|
|
* A component creating a list of editable relationships of a certain type
|
|
* The relationships are rendered as a list of related items
|
|
*/
|
|
export class EditRelationshipListComponent implements OnInit, OnDestroy {
|
|
|
|
/**
|
|
* The item to display related items for
|
|
*/
|
|
@Input() item: Item;
|
|
|
|
@Input() itemType: ItemType;
|
|
|
|
/**
|
|
* The URL to the current page
|
|
* Used to fetch updates for the current item from the store
|
|
*/
|
|
@Input() url: string;
|
|
|
|
/**
|
|
* The label of the relationship-type we're rendering a list for
|
|
*/
|
|
@Input() relationshipType: RelationshipType;
|
|
|
|
/**
|
|
* If updated information has changed
|
|
*/
|
|
@Input() hasChanges!: Observable<boolean>;
|
|
|
|
/**
|
|
* The event emmiter to submit the new information
|
|
*/
|
|
@Output() submitModal: EventEmitter<void> = new EventEmitter();
|
|
|
|
/**
|
|
* Observable that emits the left and right item type of {@link relationshipType} simultaneously.
|
|
*/
|
|
private relationshipLeftAndRightType$: Observable<[ItemType, ItemType]>;
|
|
|
|
/**
|
|
* 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 it is on neither side.
|
|
*/
|
|
@Input() currentItemIsLeftItem$: BehaviorSubject<boolean> = new BehaviorSubject(undefined);
|
|
|
|
relatedEntityType$: Observable<ItemType>;
|
|
|
|
/**
|
|
* The translation key for the entity type
|
|
*/
|
|
relationshipMessageKey$: Observable<string>;
|
|
|
|
/**
|
|
* The list ID to save selected entities under
|
|
*/
|
|
listId: string;
|
|
|
|
/**
|
|
* The FieldUpdates for the relationships in question
|
|
*/
|
|
updates$: BehaviorSubject<FieldUpdates> = new BehaviorSubject(undefined);
|
|
|
|
/**
|
|
* The RemoteData for the relationships
|
|
*/
|
|
relationshipsRd$: BehaviorSubject<RemoteData<PaginatedList<Relationship>>> = new BehaviorSubject(undefined);
|
|
|
|
/**
|
|
* Whether the current page is the last page
|
|
*/
|
|
isLastPage$: BehaviorSubject<boolean> = new BehaviorSubject(true);
|
|
|
|
/**
|
|
* Whether we're loading
|
|
*/
|
|
loading$: BehaviorSubject<boolean> = new BehaviorSubject(true);
|
|
|
|
/**
|
|
* The number of added fields that haven't been saved yet
|
|
*/
|
|
nbAddedFields$: BehaviorSubject<number> = new BehaviorSubject(0);
|
|
|
|
/**
|
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
|
* @type {Array}
|
|
*/
|
|
private subs: Subscription[] = [];
|
|
|
|
/**
|
|
* The pagination config
|
|
*/
|
|
paginationConfig: PaginationComponentOptions;
|
|
|
|
/**
|
|
* A reference to the lookup window
|
|
*/
|
|
modalRef: NgbModalRef;
|
|
|
|
/**
|
|
* Determines whether to ask for the embedded item thumbnail.
|
|
*/
|
|
fetchThumbnail: boolean;
|
|
|
|
constructor(
|
|
protected objectUpdatesService: ObjectUpdatesService,
|
|
protected linkService: LinkService,
|
|
protected relationshipService: RelationshipDataService,
|
|
protected modalService: NgbModal,
|
|
protected paginationService: PaginationService,
|
|
protected selectableListService: SelectableListService,
|
|
protected editItemRelationshipsService: EditItemRelationshipsService,
|
|
@Inject(APP_CONFIG) protected appConfig: AppConfig
|
|
) {
|
|
this.fetchThumbnail = this.appConfig.browseBy.showThumbnails;
|
|
}
|
|
|
|
/**
|
|
* Get the i18n message key for this relationship type
|
|
*/
|
|
public getRelationshipMessageKey(): Observable<string> {
|
|
return observableCombineLatest([
|
|
this.getLabel(),
|
|
this.relatedEntityType$,
|
|
]).pipe(
|
|
map(([label, relatedEntityType]) => {
|
|
if (hasValue(label) && label.indexOf('is') > -1 && label.indexOf('Of') > -1) {
|
|
const relationshipLabel = `${label.substring(2, label.indexOf('Of'))}`;
|
|
if (relationshipLabel !== relatedEntityType.label) {
|
|
return `relationships.is${relationshipLabel}Of.${relatedEntityType.label}`;
|
|
} else {
|
|
return `relationships.is${relationshipLabel}Of`;
|
|
}
|
|
} else {
|
|
return label;
|
|
}
|
|
}),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the relevant label for this relationship type
|
|
*/
|
|
private getLabel(): Observable<string> {
|
|
return this.currentItemIsLeftItem$.pipe(
|
|
map((currentItemIsLeftItem) => {
|
|
if (currentItemIsLeftItem) {
|
|
return this.relationshipType.leftwardType;
|
|
} else {
|
|
return this.relationshipType.rightwardType;
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Prevent unnecessary rerendering so fields don't lose focus
|
|
*/
|
|
trackUpdate(index, update: FieldUpdate) {
|
|
return update && update.field ? update.field.uuid : undefined;
|
|
}
|
|
|
|
/**
|
|
* Check whether the current entity can have multiple relationships of this type
|
|
* This is based on the max cardinality of the relationship
|
|
* @private
|
|
*/
|
|
private isRepeatable(): boolean {
|
|
const isLeft = this.currentItemIsLeftItem$.getValue();
|
|
if (isLeft) {
|
|
const leftMaxCardinality = this.relationshipType.leftMaxCardinality;
|
|
return hasNoValue(leftMaxCardinality) || leftMaxCardinality > 1;
|
|
} else {
|
|
const rightMaxCardinality = this.relationshipType.rightMaxCardinality;
|
|
return hasNoValue(rightMaxCardinality) || rightMaxCardinality > 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Open the dynamic lookup modal to search for items to add as relationships
|
|
*/
|
|
openLookup() {
|
|
this.modalRef = this.modalService.open(DsDynamicLookupRelationModalComponent, {
|
|
size: 'lg'
|
|
});
|
|
const modalComp: DsDynamicLookupRelationModalComponent = this.modalRef.componentInstance;
|
|
modalComp.repeatable = true;
|
|
modalComp.isEditRelationship = true;
|
|
modalComp.listId = this.listId;
|
|
modalComp.item = this.item;
|
|
modalComp.relationshipType = this.relationshipType;
|
|
modalComp.currentItemIsLeftItem$ = this.currentItemIsLeftItem$;
|
|
modalComp.toAdd = [];
|
|
modalComp.toRemove = [];
|
|
modalComp.isPending = false;
|
|
modalComp.repeatable = this.isRepeatable();
|
|
modalComp.hiddenQuery = '-search.resourceid:' + this.item.uuid;
|
|
|
|
this.item.owningCollection.pipe(
|
|
getFirstSucceededRemoteDataPayload()
|
|
).subscribe((collection: Collection) => {
|
|
modalComp.collection = collection;
|
|
});
|
|
|
|
modalComp.select = (...selectableObjects: ItemSearchResult[]) => {
|
|
selectableObjects.forEach((searchResult) => {
|
|
const relatedItem: Item = searchResult.indexableObject;
|
|
|
|
const foundIndex = modalComp.toRemove.findIndex((itemSearchResult: ItemSearchResult) => itemSearchResult.indexableObject.uuid === relatedItem.uuid);
|
|
|
|
if (foundIndex !== -1) {
|
|
modalComp.toRemove.splice(foundIndex,1);
|
|
} else {
|
|
|
|
this.getRelationFromId(relatedItem)
|
|
.subscribe((relationship: Relationship) => {
|
|
if (!relationship ) {
|
|
modalComp.toAdd.push(searchResult);
|
|
} else {
|
|
const foundIndexRemove = modalComp.toRemove.findIndex( el => el.indexableObject.uuid === relatedItem.uuid);
|
|
if (foundIndexRemove !== -1) {
|
|
modalComp.toRemove.splice(foundIndexRemove,1);
|
|
}
|
|
}
|
|
|
|
this.loading$.next(isNotEmpty(modalComp.toAdd) || isNotEmpty(modalComp.toRemove));
|
|
// emit the last page again to trigger a fieldupdates refresh
|
|
this.relationshipsRd$.next(this.relationshipsRd$.getValue());
|
|
});
|
|
}
|
|
});
|
|
};
|
|
modalComp.deselect = (...selectableObjects: ItemSearchResult[]) => {
|
|
selectableObjects.forEach((searchResult) => {
|
|
const relatedItem: Item = searchResult.indexableObject;
|
|
|
|
const foundIndex = modalComp.toAdd.findIndex( el => el.indexableObject.uuid === relatedItem.uuid);
|
|
|
|
if (foundIndex !== -1) {
|
|
modalComp.toAdd.splice(foundIndex,1);
|
|
} else {
|
|
modalComp.toRemove.push(searchResult);
|
|
}
|
|
this.loading$.next(isNotEmpty(modalComp.toAdd) || isNotEmpty(modalComp.toRemove));
|
|
});
|
|
};
|
|
|
|
|
|
|
|
modalComp.submitEv = () => {
|
|
modalComp.isPending = true;
|
|
const isLeft = this.currentItemIsLeftItem$.getValue();
|
|
const addOperations = modalComp.toAdd.map((searchResult: ItemSearchResult) => ({ type: 'add', searchResult }));
|
|
const removeOperations = modalComp.toRemove.map((searchResult: ItemSearchResult) => ({ type: 'remove', searchResult }));
|
|
observableFrom([...addOperations, ...removeOperations]).pipe(
|
|
concatMap(({ type, searchResult }: { type: string, searchResult: ItemSearchResult }) => {
|
|
const relatedItem: Item = searchResult.indexableObject;
|
|
if (type === 'add') {
|
|
return this.relationshipService.getNameVariant(this.listId, relatedItem.uuid).pipe(
|
|
switchMap((nameVariant) => {
|
|
const update = {
|
|
uuid: `${this.relationshipType.id}-${relatedItem.uuid}`,
|
|
nameVariant,
|
|
type: this.relationshipType,
|
|
originalIsLeft: isLeft,
|
|
originalItem: this.item,
|
|
relatedItem,
|
|
} as RelationshipIdentifiable;
|
|
return this.objectUpdatesService.saveAddFieldUpdate(this.url, update);
|
|
}),
|
|
take(1)
|
|
);
|
|
} else if (type === 'remove') {
|
|
return this.relationshipService.getNameVariant(this.listId, relatedItem.uuid).pipe(
|
|
switchMap((nameVariant) => {
|
|
return this.getRelationFromId(searchResult.indexableObject).pipe(
|
|
map( (relationship: Relationship) => {
|
|
const update = {
|
|
uuid: relationship.id,
|
|
nameVariant,
|
|
type: this.relationshipType,
|
|
originalIsLeft: isLeft,
|
|
originalItem: this.item,
|
|
relatedItem,
|
|
relationship,
|
|
} as RelationshipIdentifiable;
|
|
return this.objectUpdatesService.saveRemoveFieldUpdate(this.url,update);
|
|
}),
|
|
);
|
|
}),
|
|
take(1)
|
|
);
|
|
} else {
|
|
return EMPTY;
|
|
}
|
|
}),
|
|
toArray(),
|
|
).subscribe({
|
|
complete: () => {
|
|
this.editItemRelationshipsService.submit(this.item, this.url);
|
|
this.submitModal.emit();
|
|
}
|
|
});
|
|
};
|
|
|
|
|
|
modalComp.discardEv = () => {
|
|
modalComp.toAdd.forEach( (searchResult) => {
|
|
this.selectableListService.deselectSingle(this.listId,searchResult);
|
|
});
|
|
|
|
modalComp.toRemove.forEach( (searchResult) => {
|
|
this.selectableListService.selectSingle(this.listId,searchResult);
|
|
});
|
|
|
|
modalComp.toAdd = [];
|
|
modalComp.toRemove = [];
|
|
this.loading$.next(false);
|
|
};
|
|
|
|
modalComp.closeEv = () => {
|
|
this.loading$.next(false);
|
|
};
|
|
|
|
this.relatedEntityType$
|
|
.pipe(take(1))
|
|
.subscribe((relatedEntityType) => {
|
|
modalComp.relationshipOptions = Object.assign(
|
|
new RelationshipOptions(), {
|
|
relationshipType: relatedEntityType.label,
|
|
searchConfiguration: relatedEntityType.label.toLowerCase(),
|
|
nameVariants: 'true',
|
|
}
|
|
);
|
|
});
|
|
|
|
this.selectableListService.deselectAll(this.listId);
|
|
}
|
|
|
|
getRelationFromId(relatedItem) {
|
|
const relationshipLabel = this.currentItemIsLeftItem$.getValue() ? this.relationshipType.leftwardType : this.relationshipType.rightwardType;
|
|
return this.relationshipService.searchByItemsAndType( this.relationshipType.id, this.item.uuid, relationshipLabel ,[relatedItem.id] ).pipe(
|
|
getFirstSucceededRemoteData(),
|
|
getRemoteDataPayload(),
|
|
map( (res: PaginatedList<Relationship>) => res.page[0])
|
|
);
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
// store the left and right type of the relationship in a single observable
|
|
this.relationshipLeftAndRightType$ = observableCombineLatest([
|
|
this.relationshipType.leftType,
|
|
this.relationshipType.rightType,
|
|
].map((type) => type.pipe(
|
|
getFirstSucceededRemoteData(),
|
|
getRemoteDataPayload(),
|
|
))) as Observable<[ItemType, ItemType]>;
|
|
|
|
this.relatedEntityType$ = this.relationshipLeftAndRightType$.pipe(
|
|
map(([leftType, rightType]: [ItemType, ItemType]) => {
|
|
if (leftType.uuid !== this.itemType.uuid) {
|
|
return leftType;
|
|
} else {
|
|
return rightType;
|
|
}
|
|
}),
|
|
hasValueOperator(),
|
|
);
|
|
|
|
this.relatedEntityType$.pipe(
|
|
take(1)
|
|
).subscribe(
|
|
(relatedEntityType) => this.listId = `edit-relationship-${this.itemType.id}-${relatedEntityType.id}`
|
|
);
|
|
|
|
this.relationshipMessageKey$ = this.getRelationshipMessageKey();
|
|
|
|
|
|
// initialize the pagination options
|
|
this.paginationConfig = new PaginationComponentOptions();
|
|
this.paginationConfig.id = `er${this.relationshipType.id}`;
|
|
this.paginationConfig.pageSize = 5;
|
|
this.paginationConfig.currentPage = 1;
|
|
|
|
// get the pagination params from the route
|
|
const currentPagination$ = this.paginationService.getCurrentPagination(
|
|
this.paginationConfig.id,
|
|
this.paginationConfig
|
|
).pipe(
|
|
tap(() => this.loading$.next(true))
|
|
);
|
|
|
|
// this adds thumbnail images when required by configuration
|
|
let linksToFollow: FollowLinkConfig<Relationship>[] = itemLinksToFollow(this.fetchThumbnail, this.appConfig.item.showAccessStatuses);
|
|
|
|
this.subs.push(
|
|
observableCombineLatest([
|
|
currentPagination$,
|
|
this.currentItemIsLeftItem$,
|
|
this.relatedEntityType$,
|
|
]).pipe(
|
|
switchMap(([currentPagination, currentItemIsLeftItem, relatedEntityType]: [PaginationComponentOptions, boolean, ItemType]) => {
|
|
// get the relationships for the current page, item, relationship type and related entity type
|
|
return this.relationshipService.getItemRelationshipsByLabel(
|
|
this.item,
|
|
currentItemIsLeftItem ? this.relationshipType.leftwardType : this.relationshipType.rightwardType,
|
|
{
|
|
elementsPerPage: currentPagination.pageSize,
|
|
currentPage: currentPagination.currentPage,
|
|
searchParams: [
|
|
new RequestParam('relatedEntityType', relatedEntityType.label),
|
|
],
|
|
},
|
|
true,
|
|
true,
|
|
...linksToFollow
|
|
);
|
|
}),
|
|
tap((rd: RemoteData<PaginatedList<Relationship>>) => {
|
|
this.relationshipsRd$.next(rd);
|
|
}),
|
|
getAllSucceededRemoteData(),
|
|
getRemoteDataPayload(),
|
|
).subscribe((relationshipPaginatedList: PaginatedList<Relationship>) => {
|
|
this.objectUpdatesService.initialize(this.url, relationshipPaginatedList.page, new Date());
|
|
}),
|
|
);
|
|
|
|
// keep isLastPage$ up to date based on relationshipsRd$
|
|
this.subs.push(this.relationshipsRd$.pipe(
|
|
hasValueOperator(),
|
|
getAllSucceededRemoteData()
|
|
).subscribe((rd: RemoteData<PaginatedList<Relationship>>) => {
|
|
this.isLastPage$.next(hasNoValue(rd.payload._links.next));
|
|
}));
|
|
|
|
this.subs.push(this.relationshipsRd$.pipe(
|
|
hasValueOperator(),
|
|
getAllSucceededRemoteData(),
|
|
switchMap((rd: RemoteData<PaginatedList<Relationship>>) =>
|
|
// emit each relationship in the page separately
|
|
observableFrom(rd.payload.page).pipe(
|
|
mergeMap((relationship: Relationship) =>
|
|
// check for each relationship whether it's the left item
|
|
this.relationshipService.isLeftItem(relationship, this.item).pipe(
|
|
// emit an array containing both the relationship and whether it's the left item,
|
|
// as we'll need both
|
|
switchMap((isLeftItem: boolean) => {
|
|
if (isLeftItem) {
|
|
return relationship.rightItem.pipe(
|
|
getFirstCompletedRemoteData(),
|
|
getRemoteDataPayload(),
|
|
map((relatedItem: Item) => [relationship, isLeftItem, relatedItem]),
|
|
);
|
|
} else {
|
|
return relationship.leftItem.pipe(
|
|
getFirstCompletedRemoteData(),
|
|
getRemoteDataPayload(),
|
|
map((relatedItem: Item) => [relationship, isLeftItem, relatedItem]),
|
|
);
|
|
}
|
|
}),
|
|
),
|
|
),
|
|
map(([relationship, isLeftItem, relatedItem]: [Relationship, boolean, Item]) => {
|
|
// turn it into a RelationshipIdentifiable, an
|
|
const nameVariant =
|
|
isLeftItem ? relationship.rightwardValue : relationship.leftwardValue;
|
|
return {
|
|
uuid: relationship.id,
|
|
type: this.relationshipType,
|
|
relationship,
|
|
originalIsLeft: isLeftItem,
|
|
originalItem: this.item,
|
|
relatedItem: relatedItem,
|
|
nameVariant,
|
|
} as RelationshipIdentifiable;
|
|
}),
|
|
// wait until all relationships have been processed, and emit them all as a single array
|
|
toArray(),
|
|
// if the pipe above completes without emitting anything, emit an empty array instead
|
|
defaultIfEmpty([])
|
|
)),
|
|
switchMap((nextFields: RelationshipIdentifiable[]) => {
|
|
// Get a list that contains the unsaved changes for the page, as well as the page of
|
|
// RelationshipIdentifiables, as a single list of FieldUpdates
|
|
return this.objectUpdatesService.getFieldUpdates(this.url, nextFields).pipe(
|
|
map((fieldUpdates: FieldUpdates) => {
|
|
const fieldUpdatesFiltered: FieldUpdates = {};
|
|
this.nbAddedFields$.next(0);
|
|
// iterate over the fieldupdates and filter out the ones that pertain to this
|
|
// relationshiptype
|
|
Object.keys(fieldUpdates).forEach((uuid) => {
|
|
if (hasValue(fieldUpdates[uuid])) {
|
|
const field = fieldUpdates[uuid].field as RelationshipIdentifiable;
|
|
// only include fieldupdates regarding this RelationshipType
|
|
if (field.type.id === this.relationshipType.id) {
|
|
// if it's a newly added relationship
|
|
if (fieldUpdates[uuid].changeType === FieldChangeType.ADD) {
|
|
// increase the counter that tracks new relationships
|
|
this.nbAddedFields$.next(this.nbAddedFields$.getValue() + 1);
|
|
if (this.isLastPage$.getValue() === true) {
|
|
// only include newly added relationships to the output if we're on the last
|
|
// page
|
|
fieldUpdatesFiltered[uuid] = fieldUpdates[uuid];
|
|
}
|
|
} else {
|
|
// include all others
|
|
fieldUpdatesFiltered[uuid] = fieldUpdates[uuid];
|
|
}
|
|
}
|
|
}
|
|
});
|
|
return fieldUpdatesFiltered;
|
|
}),
|
|
);
|
|
}),
|
|
startWith({}),
|
|
).subscribe((updates: FieldUpdates) => {
|
|
this.loading$.next(false);
|
|
this.updates$.next(updates);
|
|
}));
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
this.subs
|
|
.filter((subscription) => hasValue(subscription))
|
|
.forEach((subscription) => subscription.unsubscribe());
|
|
}
|
|
}
|