diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts index de1f9fb3ae..d13b304b8d 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; -import { auditTime, combineLatest } from 'rxjs'; +import { auditTime, combineLatest, filter, iif, map, NEVER, startWith, switchMap } from 'rxjs'; import { Permission } from 'src/app/domain/definitions/permission'; import { PollData } from 'src/app/domain/models/poll/generic-poll'; import { @@ -9,13 +9,12 @@ import { PollTableData, VotingResult } from 'src/app/domain/models/poll/poll-constants'; -import { MeetingUserRepositoryService } from 'src/app/gateways/repositories/meeting_user'; import { ChartData } from 'src/app/site/pages/meetings/modules/poll/components/chart/chart.component'; import { PollService } from 'src/app/site/pages/meetings/modules/poll/services/poll.service'; import { OperatorService } from 'src/app/site/services/operator.service'; import { ThemeService } from 'src/app/site/services/theme.service'; -import { UserControllerService } from 'src/app/site/services/user-controller.service'; +import { ViewPoll } from '../../../../../polls'; import { ViewAssignment } from '../../../../view-models'; import { AssignmentPollService } from '../../services/assignment-poll.service'; @@ -132,20 +131,31 @@ export class AssignmentPollDetailContentComponent implements OnInit { private pollService: AssignmentPollService, private cd: ChangeDetectorRef, private operator: OperatorService, - private themeService: ThemeService, - private userController: UserControllerService, - private meetingUserRepo: MeetingUserRepositoryService + private themeService: ThemeService ) {} public ngOnInit(): void { combineLatest([ this.poll.options_as_observable, - this.themeService.currentGeneralColorsSubject, - this.userController.getViewModelListObservable(), - this.meetingUserRepo.getViewModelListObservable() - ]) - .pipe(auditTime(1)) - .subscribe(() => this.setupTableData()); + iif( + () => this.poll instanceof ViewPoll, + (this.poll as ViewPoll).options_as_observable.pipe( + map(options => + options.filter(option => !!option.content_object_id && !!option.content_object_as_observable) + ), + filter(options => !!options.length), + switchMap(options => + combineLatest( + options.map(option => + option.content_object_as_observable.pipe(filter(content_object => !!content_object)) + ) + ).pipe(auditTime(1)) + ) + ), + NEVER + ).pipe(startWith(null)), + this.themeService.currentGeneralColorsSubject + ]).subscribe(() => this.setupTableData()); } private setupTableData(): void { diff --git a/client/src/app/site/pages/meetings/pages/polls/view-models/view-option.ts b/client/src/app/site/pages/meetings/pages/polls/view-models/view-option.ts index 498be3073c..865f8ade05 100644 --- a/client/src/app/site/pages/meetings/pages/polls/view-models/view-option.ts +++ b/client/src/app/site/pages/meetings/pages/polls/view-models/view-option.ts @@ -1,4 +1,5 @@ import { marker as _ } from '@colsen1991/ngx-translate-extract-marker'; +import { Observable } from 'rxjs'; import { OptionData, OptionTitle } from '../../../../../../domain/models/poll/generic-poll'; import { Option } from '../../../../../../domain/models/poll/option'; @@ -59,6 +60,7 @@ export class ViewOption extends BaseViewModel { content_object?: C; + content_object_as_observable?: Observable; votes: ViewVote[]; poll: ViewPoll; } diff --git a/client/src/app/site/services/relation-manager.service.ts b/client/src/app/site/services/relation-manager.service.ts index 6923d6d444..82d8323eca 100644 --- a/client/src/app/site/services/relation-manager.service.ts +++ b/client/src/app/site/services/relation-manager.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core'; -import { filter, map, Observable } from 'rxjs'; +import { filter, map, merge, Observable } from 'rxjs'; import { Fqid } from '../../domain/definitions/key-types'; import { BaseModel } from '../../domain/models/base/base-model'; import { Relation, RELATIONS } from '../../infrastructure/definitions/relations'; -import { collectionIdFromFqid } from '../../infrastructure/utils/transform-functions'; +import { collectionIdFromFqid, idFromFqid } from '../../infrastructure/utils/transform-functions'; import { BaseViewModel } from '../base/base-view-model'; import { CollectionMapperService } from './collection-mapper.service'; import { ViewModelStoreService } from './view-model-store.service'; @@ -70,13 +70,18 @@ export class RelationManagerService { } public getObservableForRelation(model: M, relation: Relation): Observable { + if (relation.generic) { + return this.getObservableForGenericRelation(model, relation); + } else { + return this.getObservableForNormalRelation(model, relation); + } + } + + private getObservableForNormalRelation(model: M, relation: Relation): Observable { const foreignRepo = this.collectionMapper.getRepository(relation.foreignViewModel as any); if (!foreignRepo) { throw new Error(`Could not find foreignRepo for ${relation}`); } - if (relation.generic) { - throw new Error(`Generic relations are not yet implemented for detection of changing relations.`); - } return foreignRepo.getViewModelMapObservable().pipe( map(modelMap => { if (!model[relation.ownIdField as keyof BaseModel]) { @@ -98,6 +103,56 @@ export class RelationManagerService { ); } + private getObservableForGenericRelation(model: M, relation: Relation): Observable { + if (!relation.foreignViewModelPossibilities) { + throw new Error( + `Generic relations without foreignViewModelPossibilities are not implemented for detection of changing relations.` + ); + } + const foreignRepos = relation.foreignViewModelPossibilities.mapToObject(foreignModel => ({ + [foreignModel.COLLECTION]: this.collectionMapper.getRepository(foreignModel as any) + })); + if (Object.values(foreignRepos).some(repo => !repo)) { + throw new Error( + `Could not find at least one of the foreignRepos for ${relation}: ${Object.keys(foreignRepos) + .filter(collection => !foreignRepos[collection]) + .join(`, `)}` + ); + } + const otherModelIds: { [collection: string]: string[] } = {}; + if (relation.many) { + (model[relation.ownIdField as keyof BaseModel] as unknown as string[]) + .map(data => collectionIdFromFqid(data)) + .forEach(([collection, id]) => { + if (!otherModelIds[collection]) { + otherModelIds[collection] = []; + } + otherModelIds[collection].push(id.toString()); + }); + } + return merge( + ...Object.entries(foreignRepos).map(([collection, repo]) => + repo.getViewModelMapObservable().pipe( + map(modelMap => { + if (!model[relation.ownIdField as keyof BaseModel]) { + return true; + } + if (relation.many) { + const modelIds = Object.keys(modelMap); + const modelIdsFromModelMap = modelIds.map(data => data.toString()); + return otherModelIds[collection]?.intersect(modelIdsFromModelMap).length > 0; + } else { + return !!modelMap[idFromFqid(model[relation.ownIdField as keyof BaseModel] as string)]; + } + }) + ) + ) + ).pipe( + filter(hasChanges => hasChanges), + map(() => this.handleGenericRelation(model, relation)) + ); + } + private loadRelations(): void { for (const relation of RELATIONS) { relation.ownIdField = this.ensureIdField(