From 979d1961fb761ff6d6456e1a3c2a5e2ba78f28e0 Mon Sep 17 00:00:00 2001 From: "openslides-automation[bot]" <125256978+openslides-automation[bot]@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:14:56 +0000 Subject: [PATCH 01/47] Update meta repository (#3579) --- client/src/meta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/meta b/client/src/meta index f3c481a32a..bed1e5663d 160000 --- a/client/src/meta +++ b/client/src/meta @@ -1 +1 @@ -Subproject commit f3c481a32afff21eecbf14b27e3b5e28db5c92b8 +Subproject commit bed1e5663dc2045c6dd37efc6026f6e28ed6dce1 From 092f5f57cfc380dd9a8e021cec4c1630056a80a9 Mon Sep 17 00:00:00 2001 From: Bastian Rihm Date: Wed, 24 Apr 2024 12:42:53 +0200 Subject: [PATCH 02/47] Fix motion without submitters projection (#3601) --- .../components/motion-slide/motion-slide.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/app/site/pages/meetings/modules/projector/modules/slides/components/motions/modules/motion-slide/components/motion-slide/motion-slide.component.ts b/client/src/app/site/pages/meetings/modules/projector/modules/slides/components/motions/modules/motion-slide/components/motion-slide/motion-slide.component.ts index 539de91f56..697f15c3e6 100644 --- a/client/src/app/site/pages/meetings/modules/projector/modules/slides/components/motions/modules/motion-slide/components/motion-slide/motion-slide.component.ts +++ b/client/src/app/site/pages/meetings/modules/projector/modules/slides/components/motions/modules/motion-slide/components/motion-slide/motion-slide.component.ts @@ -168,7 +168,7 @@ export class MotionSlideComponent protected override setData(value: SlideData): void { super.setData(value); - const submitters = [...value.data.submitters] || []; + const submitters = value.data.submitters ? [...value.data.submitters] : []; if (value.data.additional_submitter) { submitters.push(value.data.additional_submitter); } From 5309f1732c04da0c15e334efe15b9b6b89bfc90f Mon Sep 17 00:00:00 2001 From: "openslides-automation[bot]" <125256978+openslides-automation[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 12:52:46 +0200 Subject: [PATCH 03/47] Update meta repository (#3603) --- client/src/meta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/meta b/client/src/meta index bed1e5663d..ea10c6c670 160000 --- a/client/src/meta +++ b/client/src/meta @@ -1 +1 @@ -Subproject commit bed1e5663dc2045c6dd37efc6026f6e28ed6dce1 +Subproject commit ea10c6c670885febc9b4dd0bd45c12a629d66314 From f33230798c84c878de6a4c0887b696dc5b0327e6 Mon Sep 17 00:00:00 2001 From: reiterl Date: Fri, 26 Apr 2024 13:23:16 +0200 Subject: [PATCH 04/47] In pdf export of attachment trim trailing '/' of url setting (#3551) --- .../export/motion-pdf.service/motion-pdf.service.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/src/app/site/pages/meetings/pages/motions/services/export/motion-pdf.service/motion-pdf.service.ts b/client/src/app/site/pages/meetings/pages/motions/services/export/motion-pdf.service/motion-pdf.service.ts index 61d3808595..097a21845b 100644 --- a/client/src/app/site/pages/meetings/pages/motions/services/export/motion-pdf.service/motion-pdf.service.ts +++ b/client/src/app/site/pages/meetings/pages/motions/services/export/motion-pdf.service/motion-pdf.service.ts @@ -1,3 +1,4 @@ +import { Location } from '@angular/common'; import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { Content, ContentTable, ContentText, TableCell } from 'pdfmake/interfaces'; @@ -743,7 +744,7 @@ export class MotionPdfService { private createAttachments(motion: ViewMotion): object { let width = this.pdfDocumentService.pageSize === `A5` ? PDF_A5_POINTS_WIDTH : PDF_A4_POINTS_WIDTH; width = width - this.pdfDocumentService.pageMarginPointsLeft - this.pdfDocumentService.pageMarginPointsRight; - const instancUrl = this.organizationSettingsService.instant(`url`); + const instanceUrl = this.organizationSettingsService.instant(`url`); const attachments = []; attachments.push({ @@ -763,11 +764,12 @@ export class MotionPdfService { margin: [0, 0, 0, 10] }); } else { + const link = Location.joinWithSlash(instanceUrl, fileUrl); attachments.push({ ul: [ { - text: attachment.getTitle() + `: ` + instancUrl + fileUrl, - link: instancUrl + fileUrl, + text: attachment.getTitle() + `: ` + link, + link: link, margin: [0, 0, 0, 5] } ] From 632545140c50be0239c4dc2d561527466854e9d8 Mon Sep 17 00:00:00 2001 From: Bastian Rihm Date: Fri, 26 Apr 2024 14:37:34 +0200 Subject: [PATCH 05/47] Improve meeting change subscription handling (#3604) --- .../base-model-request-handler.component.ts | 14 +++-- ...meeting-model-request-handler.component.ts | 21 ++++++++ .../agenda-content-object-form.component.ts | 11 ++-- .../agenda-main/agenda-main.component.ts | 12 ++--- .../list-of-speakers-main.component.ts | 38 ++++++++------ .../topic-detail-main.component.ts | 51 ++++++++++++------- .../topic-import-main.component.ts | 12 ++--- .../assignment-main.component.ts | 12 ++--- .../assignment-poll-main.component.ts | 26 ++++++++-- .../autopilot-main.component.ts | 13 ++--- .../history-main/history-main.component.ts | 2 +- .../mediafile-main.component.ts | 12 ++--- ...ng-settings-group-detail-main.component.ts | 10 +++- .../motion-main/motion-main.component.ts | 25 ++++----- .../amendment-list-main.component.ts | 12 ++--- .../motion-detail/motion-detail.component.ts | 50 ++++++++++-------- .../motion-poll-main.component.ts | 23 ++++++++- .../participant-main.component.ts | 26 ++++------ .../participant-detail.component.ts | 8 ++- .../poll-main/poll-main.component.ts | 12 ++--- .../projector-main.component.ts | 12 ++--- .../fullscreen-projector-main.component.ts | 7 +-- .../account-detail-main.component.ts | 7 +++ .../committee-detail.component.ts | 16 +++--- ...committee-detail-meeting-main.component.ts | 5 +- 25 files changed, 272 insertions(+), 165 deletions(-) create mode 100644 client/src/app/site/pages/meetings/base/base-meeting-model-request-handler.component.ts diff --git a/client/src/app/site/base/base-model-request-handler.component/base-model-request-handler.component.ts b/client/src/app/site/base/base-model-request-handler.component/base-model-request-handler.component.ts index 2510c2f4ed..4fe0ed0830 100644 --- a/client/src/app/site/base/base-model-request-handler.component/base-model-request-handler.component.ts +++ b/client/src/app/site/base/base-model-request-handler.component/base-model-request-handler.component.ts @@ -1,6 +1,6 @@ import { Directive, EventEmitter, inject, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { combineLatest, map, Observable, startWith } from 'rxjs'; +import { combineLatest, firstValueFrom, map, Observable, skip, startWith } from 'rxjs'; import { Id } from 'src/app/domain/definitions/key-types'; import { ModelRequestService } from 'src/app/site/services/model-request.service'; import { BaseUiComponent } from 'src/app/ui/base/base-ui-component'; @@ -53,9 +53,13 @@ export class BaseModelRequestHandlerComponent extends BaseUiComponent implements this.openslidesRouter = inject(OpenSlidesRouterService); } - public ngOnInit(): void { + public async ngOnInit(): Promise { + const currentParams = await firstValueFrom(this.openslidesRouter.currentParamMap); + this._currentParams = currentParams; + this._currentMeetingId = Number(currentParams[`meetingId`]) || null; + this.subscriptions.push( - this.openslidesRouter.currentParamMap.subscribe(event => { + this.openslidesRouter.currentParamMap.pipe(skip(1)).subscribe(event => { const nextMeetingId = Number(event[`meetingId`]) || null; if (nextMeetingId !== this._currentMeetingId) { this._currentMeetingId = nextMeetingId; @@ -79,7 +83,7 @@ export class BaseModelRequestHandlerComponent extends BaseUiComponent implements } protected onBeforeModelRequests(): void | Promise {} - protected onShouldCreateModelRequests(): void {} + protected onShouldCreateModelRequests(_params?: any, _meetingId?: Id | null): void {} protected onNextMeetingId(_id: Id | null): void {} protected onParamsChanged(_params: any, _oldParams?: any): void {} @@ -125,7 +129,7 @@ export class BaseModelRequestHandlerComponent extends BaseUiComponent implements private async initModelSubscriptions(): Promise { await this.onBeforeModelRequests(); - this.onShouldCreateModelRequests(); + this.onShouldCreateModelRequests(this._currentParams, this._currentMeetingId); } private createHideWhenObservable( diff --git a/client/src/app/site/pages/meetings/base/base-meeting-model-request-handler.component.ts b/client/src/app/site/pages/meetings/base/base-meeting-model-request-handler.component.ts new file mode 100644 index 0000000000..e91bb3c28e --- /dev/null +++ b/client/src/app/site/pages/meetings/base/base-meeting-model-request-handler.component.ts @@ -0,0 +1,21 @@ +import { Directive } from '@angular/core'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; +import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; + +@Directive() +export abstract class BaseMeetingModelRequestHandler extends BaseModelRequestHandlerComponent { + protected abstract getSubscriptions(meetingId: Id): SubscriptionConfig[]; + + protected override onShouldCreateModelRequests(_params: any, id: number | null): void { + if (id) { + this.subscribeTo(this.getSubscriptions(id), { hideWhenMeetingChanged: true }); + } + } + + protected override onNextMeetingId(id: number | null): void { + if (id) { + this.updateSubscribeTo(this.getSubscriptions(id), { hideWhenMeetingChanged: true }); + } + } +} diff --git a/client/src/app/site/pages/meetings/modules/meetings-component-collector/agenda-content-object-form/components/agenda-content-object-form/agenda-content-object-form.component.ts b/client/src/app/site/pages/meetings/modules/meetings-component-collector/agenda-content-object-form/components/agenda-content-object-form/agenda-content-object-form.component.ts index 3a729bca04..a094f0ca53 100644 --- a/client/src/app/site/pages/meetings/modules/meetings-component-collector/agenda-content-object-form/components/agenda-content-object-form/agenda-content-object-form.component.ts +++ b/client/src/app/site/pages/meetings/modules/meetings-component-collector/agenda-content-object-form/components/agenda-content-object-form/agenda-content-object-form.component.ts @@ -2,8 +2,9 @@ import { AfterViewInit, Component, Input, OnDestroy } from '@angular/core'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { BehaviorSubject, map, Observable, Subscription } from 'rxjs'; import { Permission } from 'src/app/domain/definitions/permission'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; import { ItemTypeChoices } from 'src/app/domain/models/agenda/agenda-item'; -import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; +import { BaseMeetingModelRequestHandler } from 'src/app/site/pages/meetings/base/base-meeting-model-request-handler.component'; import { ViewAgendaItem } from 'src/app/site/pages/meetings/pages/agenda'; import { getAgendaListMinimalSubscriptionConfig } from 'src/app/site/pages/meetings/pages/agenda/agenda.subscription'; import { MeetingSettingsService } from 'src/app/site/pages/meetings/services/meeting-settings.service'; @@ -16,7 +17,7 @@ import { AgendaContentObjectFormService } from '../../services/agenda-content-ob styleUrls: [`./agenda-content-object-form.component.scss`] }) export class AgendaContentObjectFormComponent - extends BaseModelRequestHandlerComponent + extends BaseMeetingModelRequestHandler implements AfterViewInit, OnDestroy { @Input() @@ -92,10 +93,8 @@ export class AgendaContentObjectFormComponent super.ngOnDestroy(); } - protected override onNextMeetingId(id: number | null): void { - if (id) { - this.subscribeTo(getAgendaListMinimalSubscriptionConfig(id), { hideWhenDestroyed: true }); - } + protected getSubscriptions(meetingId: number): SubscriptionConfig[] { + return [getAgendaListMinimalSubscriptionConfig(meetingId)]; } private setupSubscription(): void { diff --git a/client/src/app/site/pages/meetings/pages/agenda/components/agenda-main/agenda-main.component.ts b/client/src/app/site/pages/meetings/pages/agenda/components/agenda-main/agenda-main.component.ts index 41def37345..7f6c3f7471 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/components/agenda-main/agenda-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/agenda/components/agenda-main/agenda-main.component.ts @@ -1,5 +1,7 @@ import { Component } from '@angular/core'; -import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; +import { BaseMeetingModelRequestHandler } from 'src/app/site/pages/meetings/base/base-meeting-model-request-handler.component'; import { getAgendaListSubscriptionConfig } from '../../agenda.subscription'; @@ -8,10 +10,8 @@ import { getAgendaListSubscriptionConfig } from '../../agenda.subscription'; templateUrl: `./agenda-main.component.html`, styleUrls: [`./agenda-main.component.scss`] }) -export class AgendaMainComponent extends BaseModelRequestHandlerComponent { - protected override onNextMeetingId(id: number | null): void { - if (id) { - this.subscribeTo(getAgendaListSubscriptionConfig(id), { hideWhenMeetingChanged: true }); - } +export class AgendaMainComponent extends BaseMeetingModelRequestHandler { + protected getSubscriptions(id: Id): SubscriptionConfig[] { + return [getAgendaListSubscriptionConfig(id)]; } } diff --git a/client/src/app/site/pages/meetings/pages/agenda/modules/list-of-speakers/components/list-of-speakers-main/list-of-speakers-main.component.ts b/client/src/app/site/pages/meetings/pages/agenda/modules/list-of-speakers/components/list-of-speakers-main/list-of-speakers-main.component.ts index b40a596c0f..293c4d9b48 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/modules/list-of-speakers/components/list-of-speakers-main/list-of-speakers-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/agenda/modules/list-of-speakers/components/list-of-speakers-main/list-of-speakers-main.component.ts @@ -20,24 +20,30 @@ export class ListOfSpeakersMainComponent extends BaseModelRequestHandlerComponen protected override onParamsChanged(params: any, oldParams: any): void { if (params[`id`] !== oldParams[`id`] || params[`meetingId`] !== oldParams[`meetingId`]) { - this.sequentialNumberMapping - .getIdBySequentialNumber({ - collection: ListOfSpeakers.COLLECTION, - meetingId: params[`meetingId`], - sequentialNumber: +params[`id`] - }) - .then(id => { - if (id && this._currentLOSId !== id) { - this._currentLOSId = id; - this.loadLOSDetail(); - } - }); + this.loadLOSDetail(+params[`id`], +params[`meetingId`]); } } - private loadLOSDetail(): void { - this.updateSubscribeTo(getListOfSpeakersDetailSubscriptionConfig(this._currentLOSId), { - hideWhenDestroyed: true - }); + protected override onShouldCreateModelRequests(params: any, meetingId: Id): void { + if (params[`id`] && meetingId) { + this.loadLOSDetail(+params[`id`], meetingId); + } + } + + private loadLOSDetail(id: Id, meetingId: Id): void { + this.sequentialNumberMapping + .getIdBySequentialNumber({ + collection: ListOfSpeakers.COLLECTION, + meetingId: meetingId, + sequentialNumber: id + }) + .then(id => { + if (id && this._currentLOSId !== id) { + this._currentLOSId = id; + this.updateSubscribeTo(getListOfSpeakersDetailSubscriptionConfig(this._currentLOSId), { + hideWhenDestroyed: true + }); + } + }); } } diff --git a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/pages/topic-detail/components/topic-detail-main/topic-detail-main.component.ts b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/pages/topic-detail/components/topic-detail-main/topic-detail-main.component.ts index 66263c2d51..15bd5b343e 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/pages/topic-detail/components/topic-detail-main/topic-detail-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/pages/topic-detail/components/topic-detail-main/topic-detail-main.component.ts @@ -22,27 +22,44 @@ export class TopicDetailMainComponent extends BaseModelRequestHandlerComponent { super(); } + protected override onShouldCreateModelRequests(params: any, meetingId: any): void { + if (meetingId) { + if (+params[`id`]) { + this.loadTopicDetail(+params[`id`], +params[`meetingId`]); + } else { + this.loadTopicList(+params[`meetingId`]); + } + } + } + protected override onParamsChanged(params: any, oldParams: any): void { - if (params[`id`] !== oldParams[`id`] || params[`meetingId`] !== oldParams[`meetingId`]) { - this.sequentialNumberMapping - .getIdBySequentialNumber({ - collection: Topic.COLLECTION, - meetingId: params[`meetingId`], - sequentialNumber: +params[`id`] - }) - .then(id => { - if (id && this._currentTopicId !== id) { - this._currentTopicId = id; - this.loadTopicDetail(); - } else if (!id && params[`meetingId`]) { - this.loadTopicList(+params[`meetingId`]); - } - }); + if ( + params[`id`] !== oldParams[`id`] || + (+params[`meetingId`] && params[`meetingId`] !== oldParams[`meetingId`]) + ) { + if (+params[`id`]) { + this.loadTopicDetail(+params[`id`], +params[`meetingId`]); + } else { + this.loadTopicList(+params[`meetingId`]); + } } } - private loadTopicDetail(): void { - this.updateSubscribeTo(getTopicDetailSubscriptionConfig(this._currentTopicId), { hideWhenDestroyed: true }); + private loadTopicDetail(sNr: Id, meetingId: Id): void { + this.sequentialNumberMapping + .getIdBySequentialNumber({ + collection: Topic.COLLECTION, + meetingId, + sequentialNumber: sNr + }) + .then(id => { + if (id && this._currentTopicId !== id) { + this._currentTopicId = id; + this.updateSubscribeTo(getTopicDetailSubscriptionConfig(this._currentTopicId), { + hideWhenDestroyed: true + }); + } + }); } private async loadTopicList(meetingId: number): Promise { diff --git a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/pages/topic-import/components/topic-import-main/topic-import-main.component.ts b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/pages/topic-import/components/topic-import-main/topic-import-main.component.ts index f0b0c2f541..71c0725647 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/pages/topic-import/components/topic-import-main/topic-import-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/pages/topic-import/components/topic-import-main/topic-import-main.component.ts @@ -1,5 +1,7 @@ import { Component } from '@angular/core'; -import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; +import { BaseMeetingModelRequestHandler } from 'src/app/site/pages/meetings/base/base-meeting-model-request-handler.component'; import { getAgendaListSubscriptionConfig } from '../../../../../../agenda.subscription'; @@ -8,10 +10,8 @@ import { getAgendaListSubscriptionConfig } from '../../../../../../agenda.subscr templateUrl: `./topic-import-main.component.html`, styleUrls: [`./topic-import-main.component.scss`] }) -export class TopicImportMainComponent extends BaseModelRequestHandlerComponent { - protected override onNextMeetingId(id: number | null): void { - if (id) { - this.subscribeTo(getAgendaListSubscriptionConfig(id), { hideWhenMeetingChanged: true }); - } +export class TopicImportMainComponent extends BaseMeetingModelRequestHandler { + protected getSubscriptions(id: Id): SubscriptionConfig[] { + return [getAgendaListSubscriptionConfig(id)]; } } diff --git a/client/src/app/site/pages/meetings/pages/assignments/components/assignment-main/assignment-main.component.ts b/client/src/app/site/pages/meetings/pages/assignments/components/assignment-main/assignment-main.component.ts index 9ab2a0c000..cf7056e392 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/components/assignment-main/assignment-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/components/assignment-main/assignment-main.component.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core'; -import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; +import { BaseMeetingModelRequestHandler } from '../../../../base/base-meeting-model-request-handler.component'; import { getAssignmentSubscriptionConfig } from '../../assignments.subscription'; @Component({ @@ -8,10 +10,8 @@ import { getAssignmentSubscriptionConfig } from '../../assignments.subscription' templateUrl: `./assignment-main.component.html`, styleUrls: [`./assignment-main.component.scss`] }) -export class AssignmentMainComponent extends BaseModelRequestHandlerComponent { - protected override onNextMeetingId(id: number | null): void { - if (id) { - this.subscribeTo(getAssignmentSubscriptionConfig(id), { hideWhenMeetingChanged: true }); - } +export class AssignmentMainComponent extends BaseMeetingModelRequestHandler { + protected getSubscriptions(id: Id): SubscriptionConfig[] { + return [getAssignmentSubscriptionConfig(id)]; } } diff --git a/client/src/app/site/pages/meetings/pages/assignments/pages/assignment-polls/components/assignment-poll-main/assignment-poll-main.component.ts b/client/src/app/site/pages/meetings/pages/assignments/pages/assignment-polls/components/assignment-poll-main/assignment-poll-main.component.ts index d2bf9c4afa..d7a4f6d54a 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/pages/assignment-polls/components/assignment-poll-main/assignment-poll-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/pages/assignment-polls/components/assignment-poll-main/assignment-poll-main.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { Id } from 'src/app/domain/definitions/key-types'; import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; import { SequentialNumberMappingService } from 'src/app/site/pages/meetings/services/sequential-number-mapping.service'; @@ -16,12 +17,12 @@ export class AssignmentPollMainComponent extends BaseModelRequestHandlerComponen super(); } - protected override onParamsChanged(params: any, oldParams: any): void { - if (params[`id`] && params[`id`] !== oldParams[`id`]) { + protected override onShouldCreateModelRequests(params: any, meetingId: Id): void { + if (params[`id`]) { this.sequentialNumberMappingService .getIdBySequentialNumber({ collection: ViewPoll.COLLECTION, - meetingId: params[`meetingId`], + meetingId, sequentialNumber: +params[`id`] }) .then(id => { @@ -34,4 +35,23 @@ export class AssignmentPollMainComponent extends BaseModelRequestHandlerComponen }); } } + + protected override onParamsChanged(params: any, oldParams: any): void { + if ((params[`id`] && params[`id`] !== oldParams[`id`]) || params[`meetingId`] !== oldParams[`meetingId`]) { + this.sequentialNumberMappingService + .getIdBySequentialNumber({ + collection: ViewPoll.COLLECTION, + meetingId: params[`meetingId`], + sequentialNumber: +params[`id`] + }) + .then(id => { + if (id) { + this.updateSubscribeTo(getPollDetailSubscriptionConfig(id), { hideWhenDestroyed: true }); + this.updateSubscribeTo(getParticipantMinimalSubscriptionConfig(+params[`meetingId`]), { + hideWhenDestroyed: true + }); + } + }); + } + } } diff --git a/client/src/app/site/pages/meetings/pages/autopilot/components/autopilot-main/autopilot-main.component.ts b/client/src/app/site/pages/meetings/pages/autopilot/components/autopilot-main/autopilot-main.component.ts index ffa52c4854..362bc7dc6d 100644 --- a/client/src/app/site/pages/meetings/pages/autopilot/components/autopilot-main/autopilot-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/autopilot/components/autopilot-main/autopilot-main.component.ts @@ -1,9 +1,10 @@ import { Component } from '@angular/core'; import { distinctUntilChanged, map } from 'rxjs'; import { Id } from 'src/app/domain/definitions/key-types'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; import { ProjectorRepositoryService } from 'src/app/gateways/repositories/projectors/projector-repository.service'; import { collectionFromFqid } from 'src/app/infrastructure/utils/transform-functions'; -import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; +import { BaseMeetingModelRequestHandler } from 'src/app/site/pages/meetings/base/base-meeting-model-request-handler.component'; import { CollectionMapperService } from 'src/app/site/services/collection-mapper.service'; import { @@ -17,7 +18,7 @@ import { templateUrl: `./autopilot-main.component.html`, styleUrls: [`./autopilot-main.component.scss`] }) -export class AutopilotMainComponent extends BaseModelRequestHandlerComponent { +export class AutopilotMainComponent extends BaseMeetingModelRequestHandler { private currentSubscriptions: Id[] = []; public constructor( @@ -83,11 +84,7 @@ export class AutopilotMainComponent extends BaseModelRequestHandlerComponent { ); } - protected override onNextMeetingId(id: number | null): void { - if (id) { - this.subscribeTo(getAutopilotSubscriptionConfig(id), { - hideWhenMeetingChanged: true - }); - } + protected getSubscriptions(id: Id): SubscriptionConfig[] { + return [getAutopilotSubscriptionConfig(id)]; } } diff --git a/client/src/app/site/pages/meetings/pages/history/components/history-main/history-main.component.ts b/client/src/app/site/pages/meetings/pages/history/components/history-main/history-main.component.ts index 91e10f6e1d..a3e596de64 100644 --- a/client/src/app/site/pages/meetings/pages/history/components/history-main/history-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/history/components/history-main/history-main.component.ts @@ -12,7 +12,7 @@ import { getParticipantMinimalSubscriptionConfig } from '../../../participants/p styleUrls: [`./history-main.component.scss`] }) export class HistoryMainComponent extends BaseModelRequestHandlerComponent { - protected override onNextMeetingId(id: Id | null): void { + protected override onShouldCreateModelRequests(_params: any, id: Id | null): void { if (id) { this.subscribeTo(getMotionListMinimalSubscriptionConfig(id), { hideWhenDestroyed: true }); this.subscribeTo(getParticipantMinimalSubscriptionConfig(id), { hideWhenDestroyed: true }); diff --git a/client/src/app/site/pages/meetings/pages/mediafiles/components/mediafile-main/mediafile-main.component.ts b/client/src/app/site/pages/meetings/pages/mediafiles/components/mediafile-main/mediafile-main.component.ts index cdefa683a8..64f09284c8 100644 --- a/client/src/app/site/pages/meetings/pages/mediafiles/components/mediafile-main/mediafile-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/mediafiles/components/mediafile-main/mediafile-main.component.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core'; -import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; +import { BaseMeetingModelRequestHandler } from '../../../../base/base-meeting-model-request-handler.component'; import { getMediafilesSubscriptionConfig } from '../../mediafiles.subscription'; @Component({ @@ -8,10 +10,8 @@ import { getMediafilesSubscriptionConfig } from '../../mediafiles.subscription'; templateUrl: `./mediafile-main.component.html`, styleUrls: [`./mediafile-main.component.scss`] }) -export class MediafileMainComponent extends BaseModelRequestHandlerComponent { - protected override onNextMeetingId(id: number | null): void { - if (id) { - this.subscribeTo(getMediafilesSubscriptionConfig(id), { hideWhenMeetingChanged: true }); - } +export class MediafileMainComponent extends BaseMeetingModelRequestHandler { + protected getSubscriptions(id: Id): SubscriptionConfig[] { + return [getMediafilesSubscriptionConfig(id)]; } } diff --git a/client/src/app/site/pages/meetings/pages/meeting-settings/pages/meeting-settings-group-detail/components/meeting-settings-group-detail-main/meeting-settings-group-detail-main.component.ts b/client/src/app/site/pages/meetings/pages/meeting-settings/pages/meeting-settings-group-detail/components/meeting-settings-group-detail-main/meeting-settings-group-detail-main.component.ts index 90c29b020a..ac5ad20dc5 100644 --- a/client/src/app/site/pages/meetings/pages/meeting-settings/pages/meeting-settings-group-detail/components/meeting-settings-group-detail-main/meeting-settings-group-detail-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/meeting-settings/pages/meeting-settings-group-detail/components/meeting-settings-group-detail-main/meeting-settings-group-detail-main.component.ts @@ -17,7 +17,15 @@ export class MeetingSettingsGroupDetailMainComponent extends BaseModelRequestHan params[`group`] === MOTIONS_SETTINGS_GROUP && params[`meetingId`] !== oldParams[`meetingId`] ) { - this.subscribeTo(getMotionWorkflowSubscriptionConfig(+params[`meetingId`]), { + this.updateSubscribeTo(getMotionWorkflowSubscriptionConfig(+params[`meetingId`]), { + hideWhenMeetingChanged: true + }); + } + } + + protected override onShouldCreateModelRequests(params?: any, meetingId?: number): void { + if (params[`group`] && params[`group`] === MOTIONS_SETTINGS_GROUP && meetingId) { + this.subscribeTo(getMotionWorkflowSubscriptionConfig(meetingId), { hideWhenMeetingChanged: true }); } diff --git a/client/src/app/site/pages/meetings/pages/motions/components/motion-main/motion-main.component.ts b/client/src/app/site/pages/meetings/pages/motions/components/motion-main/motion-main.component.ts index b9fb932736..4717b3644a 100644 --- a/client/src/app/site/pages/meetings/pages/motions/components/motion-main/motion-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/motions/components/motion-main/motion-main.component.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core'; -import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; +import { BaseMeetingModelRequestHandler } from '../../../../base/base-meeting-model-request-handler.component'; import { getMotionBlockSubscriptionConfig, getMotionListSubscriptionConfig, @@ -13,18 +15,13 @@ import { templateUrl: `./motion-main.component.html`, styleUrls: [`./motion-main.component.scss`] }) -export class MotionMainComponent extends BaseModelRequestHandlerComponent { - protected override onNextMeetingId(id: number | null): void { - if (id) { - this.updateSubscribeTo( - [ - getMotionListSubscriptionConfig(id), - getMotionBlockSubscriptionConfig(id), - getMotionWorkflowSubscriptionConfig(id), - getMotionsSubmodelSubscriptionConfig(id) - ], - { hideWhenMeetingChanged: true } - ); - } +export class MotionMainComponent extends BaseMeetingModelRequestHandler { + protected getSubscriptions(id: Id): SubscriptionConfig[] { + return [ + getMotionListSubscriptionConfig(id), + getMotionBlockSubscriptionConfig(id), + getMotionWorkflowSubscriptionConfig(id), + getMotionsSubmodelSubscriptionConfig(id) + ]; } } diff --git a/client/src/app/site/pages/meetings/pages/motions/pages/amendments/components/amendment-list-main/amendment-list-main.component.ts b/client/src/app/site/pages/meetings/pages/motions/pages/amendments/components/amendment-list-main/amendment-list-main.component.ts index 38c5727d71..f67ff1c147 100644 --- a/client/src/app/site/pages/meetings/pages/motions/pages/amendments/components/amendment-list-main/amendment-list-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/motions/pages/amendments/components/amendment-list-main/amendment-list-main.component.ts @@ -1,5 +1,7 @@ import { Component } from '@angular/core'; -import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; +import { BaseMeetingModelRequestHandler } from 'src/app/site/pages/meetings/base/base-meeting-model-request-handler.component'; import { getAmendmentListSubscriptionConfig } from '../../../../motions.subscription'; @@ -8,10 +10,8 @@ import { getAmendmentListSubscriptionConfig } from '../../../../motions.subscrip templateUrl: `./amendment-list-main.component.html`, styleUrls: [`./amendment-list-main.component.scss`] }) -export class AmendmentListMainComponent extends BaseModelRequestHandlerComponent { - protected override onNextMeetingId(id: number | null): void { - if (id) { - this.subscribeTo(getAmendmentListSubscriptionConfig(id), { hideWhenMeetingChanged: true }); - } +export class AmendmentListMainComponent extends BaseMeetingModelRequestHandler { + protected getSubscriptions(id: Id): SubscriptionConfig[] { + return [getAmendmentListSubscriptionConfig(id)]; } } diff --git a/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-detail/motion-detail.component.ts b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-detail/motion-detail.component.ts index a9add29d30..b12c22b455 100644 --- a/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-detail/motion-detail.component.ts +++ b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-detail/motion-detail.component.ts @@ -28,34 +28,40 @@ export class MotionDetailComponent extends BaseModelRequestHandlerComponent { super(); } + protected override onShouldCreateModelRequests(params: any, meetingId: Id): void { + if (params[`id`] && meetingId) { + this.loadMotionDetail(meetingId, +params[`id`]); + } + } + protected override onParamsChanged(params: any, oldParams: any): void { if (params[`id`] !== oldParams[`id`] || params[`meetingId`] !== oldParams[`meetingId`]) { - this.sequentialNumberMapping - .getIdBySequentialNumber({ - collection: Motion.COLLECTION, - meetingId: params[`meetingId`], - sequentialNumber: +params[`id`] - }) - .then(id => { - if (id && this._currentMotionId !== id) { - this._currentMotionId = id; - this._watchingMap = {}; - this.loadMotionDetail(); - } - }); + this.loadMotionDetail(+params[`meetingId`], +params[`id`]); } } - private loadMotionDetail(): void { - this.updateSubscribeTo(getMotionDetailSubscriptionConfig(this._currentMotionId)); - this.updateSubscription( - MOTION_DETAIL_SUBSCRIPTION, - this.repo.getViewModelObservable(this._currentMotionId!).subscribe(motion => { - if (motion) { - this.watchForChanges(motion, `all_origin_ids`, `derived_motion_ids`); - } + private loadMotionDetail(meetingId: Id, motionId: Id): void { + this.sequentialNumberMapping + .getIdBySequentialNumber({ + collection: Motion.COLLECTION, + meetingId, + sequentialNumber: motionId }) - ); + .then(id => { + if (id && this._currentMotionId !== id) { + this._currentMotionId = id; + this._watchingMap = {}; + this.updateSubscribeTo(getMotionDetailSubscriptionConfig(this._currentMotionId)); + this.updateSubscription( + MOTION_DETAIL_SUBSCRIPTION, + this.repo.getViewModelObservable(this._currentMotionId!).subscribe(motion => { + if (motion) { + this.watchForChanges(motion, `all_origin_ids`, `derived_motion_ids`); + } + }) + ); + } + }); } private watchForChanges(motion: ViewMotion, ...fields: (keyof Motion)[]): void { diff --git a/client/src/app/site/pages/meetings/pages/motions/pages/motion-polls/components/motion-poll-main/motion-poll-main.component.ts b/client/src/app/site/pages/meetings/pages/motions/pages/motion-polls/components/motion-poll-main/motion-poll-main.component.ts index 59e1013c7f..0c4b9b339c 100644 --- a/client/src/app/site/pages/meetings/pages/motions/pages/motion-polls/components/motion-poll-main/motion-poll-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/motions/pages/motion-polls/components/motion-poll-main/motion-poll-main.component.ts @@ -16,6 +16,25 @@ export class MotionPollMainComponent extends BaseModelRequestHandlerComponent { super(); } + protected override onShouldCreateModelRequests(params?: any, meetingId?: number): void { + if (params[`id`]) { + this.sequentialNumberMappingService + .getIdBySequentialNumber({ + collection: ViewPoll.COLLECTION, + meetingId, + sequentialNumber: +params[`id`] + }) + .then(id => { + if (id) { + this.subscribeTo(getPollDetailSubscriptionConfig(id), { hideWhenDestroyed: true }); + this.subscribeTo(getParticipantMinimalSubscriptionConfig(+params[`meetingId`]), { + hideWhenDestroyed: true + }); + } + }); + } + } + protected override onParamsChanged(params: any, oldParams: any): void { if (params[`id`] !== oldParams[`id`]) { this.sequentialNumberMappingService @@ -26,8 +45,8 @@ export class MotionPollMainComponent extends BaseModelRequestHandlerComponent { }) .then(id => { if (id) { - this.subscribeTo(getPollDetailSubscriptionConfig(id), { hideWhenDestroyed: true }); - this.subscribeTo(getParticipantMinimalSubscriptionConfig(+params[`meetingId`]), { + this.updateSubscribeTo(getPollDetailSubscriptionConfig(id), { hideWhenDestroyed: true }); + this.updateSubscribeTo(getParticipantMinimalSubscriptionConfig(+params[`meetingId`]), { hideWhenDestroyed: true }); } diff --git a/client/src/app/site/pages/meetings/pages/participants/components/participant-main/participant-main.component.ts b/client/src/app/site/pages/meetings/pages/participants/components/participant-main/participant-main.component.ts index 7cee7fa0a9..358f0ec987 100644 --- a/client/src/app/site/pages/meetings/pages/participants/components/participant-main/participant-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/participants/components/participant-main/participant-main.component.ts @@ -1,5 +1,7 @@ import { Component } from '@angular/core'; -import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; +import { BaseMeetingModelRequestHandler } from 'src/app/site/pages/meetings/base/base-meeting-model-request-handler.component'; import { getParticipantListSubscriptionConfig, @@ -12,20 +14,12 @@ import { getStructureLevelListSubscriptionConfig } from '../../participants.subs templateUrl: `./participant-main.component.html`, styleUrls: [`./participant-main.component.scss`] }) -export class ParticipantMainComponent extends BaseModelRequestHandlerComponent { - protected override onNextMeetingId(id: number | null): void { - if (id) { - // TODO: Do requests for speaker list and structure level list somewhere else - this.subscribeTo( - [ - getParticipantListSubscriptionConfig(id), - getStructureLevelListSubscriptionConfig(id), - getSpeakersListSubscriptionConfig(id) - ], - { - hideWhenMeetingChanged: true - } - ); - } +export class ParticipantMainComponent extends BaseMeetingModelRequestHandler { + protected getSubscriptions(id: Id): SubscriptionConfig[] { + return [ + getParticipantListSubscriptionConfig(id), + getStructureLevelListSubscriptionConfig(id), + getSpeakersListSubscriptionConfig(id) + ]; } } diff --git a/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail/participant-detail.component.ts b/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail/participant-detail.component.ts index 20bfb72de0..4fb19bee8b 100644 --- a/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail/participant-detail.component.ts +++ b/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail/participant-detail.component.ts @@ -9,9 +9,15 @@ import { getParticipantDetailSubscription } from '../../../../participants.subsc styleUrls: [`./participant-detail.component.scss`] }) export class ParticipantDetailComponent extends BaseModelRequestHandlerComponent { + protected override onShouldCreateModelRequests(params?: any): void { + if (params[`id`]) { + this.subscribeTo(getParticipantDetailSubscription(+params[`id`]), { hideWhenDestroyed: true }); + } + } + protected override onParamsChanged(params: any, oldParams: any): void { if (params[`id`] && params[`id`] !== oldParams[`id`]) { - this.subscribeTo(getParticipantDetailSubscription(+params[`id`]), { hideWhenDestroyed: true }); + this.updateSubscribeTo(getParticipantDetailSubscription(+params[`id`]), { hideWhenDestroyed: true }); } } } diff --git a/client/src/app/site/pages/meetings/pages/polls/components/poll-main/poll-main.component.ts b/client/src/app/site/pages/meetings/pages/polls/components/poll-main/poll-main.component.ts index 0cd7cd41a0..93277e43a6 100644 --- a/client/src/app/site/pages/meetings/pages/polls/components/poll-main/poll-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/polls/components/poll-main/poll-main.component.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core'; -import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; +import { BaseMeetingModelRequestHandler } from '../../../../base/base-meeting-model-request-handler.component'; import { getPollListSubscriptionConfig } from '../../polls.subscription'; @Component({ @@ -8,10 +10,8 @@ import { getPollListSubscriptionConfig } from '../../polls.subscription'; templateUrl: `./poll-main.component.html`, styleUrls: [`./poll-main.component.scss`] }) -export class PollMainComponent extends BaseModelRequestHandlerComponent { - protected override onNextMeetingId(id: number | null): void { - if (id) { - this.subscribeTo(getPollListSubscriptionConfig(id), { hideWhenMeetingChanged: true }); - } +export class PollMainComponent extends BaseMeetingModelRequestHandler { + protected override getSubscriptions(id: Id): SubscriptionConfig[] { + return [getPollListSubscriptionConfig(id)]; } } diff --git a/client/src/app/site/pages/meetings/pages/projectors/components/projector-main/projector-main.component.ts b/client/src/app/site/pages/meetings/pages/projectors/components/projector-main/projector-main.component.ts index e38ef4e2b3..ce3a91f104 100644 --- a/client/src/app/site/pages/meetings/pages/projectors/components/projector-main/projector-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/projectors/components/projector-main/projector-main.component.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core'; -import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { SubscriptionConfig } from 'src/app/domain/interfaces/subscription-config'; +import { BaseMeetingModelRequestHandler } from '../../../../base/base-meeting-model-request-handler.component'; import { getProjectorListSubscriptionConfig } from '../../projectors.subscription'; @Component({ @@ -8,10 +10,8 @@ import { getProjectorListSubscriptionConfig } from '../../projectors.subscriptio templateUrl: `./projector-main.component.html`, styleUrls: [`./projector-main.component.scss`] }) -export class ProjectorMainComponent extends BaseModelRequestHandlerComponent { - protected override onNextMeetingId(id: number | null): void { - if (id) { - this.subscribeTo(getProjectorListSubscriptionConfig(id), { hideWhenMeetingChanged: true }); - } +export class ProjectorMainComponent extends BaseMeetingModelRequestHandler { + protected getSubscriptions(id: Id): SubscriptionConfig[] { + return [getProjectorListSubscriptionConfig(id)]; } } diff --git a/client/src/app/site/pages/meetings/pages/projectors/modules/fullscreen-projector/components/fullscreen-projector-main/fullscreen-projector-main.component.ts b/client/src/app/site/pages/meetings/pages/projectors/modules/fullscreen-projector/components/fullscreen-projector-main/fullscreen-projector-main.component.ts index f6339f6117..83aacfd3b3 100644 --- a/client/src/app/site/pages/meetings/pages/projectors/modules/fullscreen-projector/components/fullscreen-projector-main/fullscreen-projector-main.component.ts +++ b/client/src/app/site/pages/meetings/pages/projectors/modules/fullscreen-projector/components/fullscreen-projector-main/fullscreen-projector-main.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { Id } from 'src/app/domain/definitions/key-types'; import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component'; import { SequentialNumberMappingService } from 'src/app/site/pages/meetings/services/sequential-number-mapping.service'; @@ -15,17 +16,17 @@ export class FullscreenProjectorMainComponent extends BaseModelRequestHandlerCom super(); } - protected override onParamsChanged(params: any, _oldParams?: any): void { + protected override onShouldCreateModelRequests(params: any, meetingId: Id): void { if (params[`id`]) { this.sequentialNumberMappingService .getIdBySequentialNumber({ collection: ViewProjector.COLLECTION, - meetingId: params[`meetingId`], + meetingId, sequentialNumber: +params[`id`] }) .then(id => { if (id) { - this.subscribeTo(getProjectorSubscriptionConfig(id), { hideWhenMeetingChanged: true }); + this.subscribeTo(getProjectorSubscriptionConfig(id), { hideWhenDestroyed: true }); } }); } diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/account-detail/components/account-detail-main/account-detail-main.component.ts b/client/src/app/site/pages/organization/pages/accounts/pages/account-detail/components/account-detail-main/account-detail-main.component.ts index 9dca1fcb1b..cc9a4b8a5b 100644 --- a/client/src/app/site/pages/organization/pages/accounts/pages/account-detail/components/account-detail-main/account-detail-main.component.ts +++ b/client/src/app/site/pages/organization/pages/accounts/pages/account-detail/components/account-detail-main/account-detail-main.component.ts @@ -11,6 +11,13 @@ import { getAccountDetailSubscriptionConfig } from '../../../../accounts.subscri export class AccountDetailMainComponent extends BaseModelRequestHandlerComponent { protected override onParamsChanged(params: any, oldParams: any): void { if (params[`id`] !== oldParams[`id`]) { + const id = +params[`id`]; + this.updateSubscribeTo(getAccountDetailSubscriptionConfig(id), { hideWhenDestroyed: true }); + } + } + + protected override onShouldCreateModelRequests(params: any): void { + if (params[`id`]) { const id = +params[`id`]; this.subscribeTo(getAccountDetailSubscriptionConfig(id), { hideWhenDestroyed: true }); } diff --git a/client/src/app/site/pages/organization/pages/committees/pages/committee-detail/components/committee-detail/committee-detail.component.ts b/client/src/app/site/pages/organization/pages/committees/pages/committee-detail/components/committee-detail/committee-detail.component.ts index f34ce30f79..3b671657b5 100644 --- a/client/src/app/site/pages/organization/pages/committees/pages/committee-detail/components/committee-detail/committee-detail.component.ts +++ b/client/src/app/site/pages/organization/pages/committees/pages/committee-detail/components/committee-detail/committee-detail.component.ts @@ -1,5 +1,4 @@ import { Component } from '@angular/core'; -import { Id } from 'src/app/domain/definitions/key-types'; import { BaseModelRequestHandlerComponent } from 'src/app/site/base/base-model-request-handler.component/base-model-request-handler.component'; import { getCommitteeDetailSubscriptionConfig } from '../../../../committees.subscription'; @@ -10,12 +9,17 @@ import { getCommitteeDetailSubscriptionConfig } from '../../../../committees.sub styleUrls: [`./committee-detail.component.scss`] }) export class CommitteeDetailComponent extends BaseModelRequestHandlerComponent { - private committeeId: Id | null = null; - protected override onParamsChanged(params: any, oldParams: any): void { - if (params[`committeeId`] !== oldParams[`committeeId`]) { - this.committeeId = +params[`committeeId`] || null; - this.subscribeTo(getCommitteeDetailSubscriptionConfig(this.committeeId), { hideWhenDestroyed: true }); + if (params[`committeeId`] !== oldParams[`committeeId`] && +params[`committeeId`]) { + this.updateSubscribeTo(getCommitteeDetailSubscriptionConfig(+params[`committeeId`]), { + hideWhenDestroyed: true + }); + } + } + + protected override onShouldCreateModelRequests(params: any): void { + if (+params[`committeeId`]) { + this.subscribeTo(getCommitteeDetailSubscriptionConfig(+params[`committeeId`]), { hideWhenDestroyed: true }); } } } diff --git a/client/src/app/site/pages/organization/pages/committees/pages/committee-detail/modules/committee-detail-meeting/components/committee-detail-meeting-main/committee-detail-meeting-main.component.ts b/client/src/app/site/pages/organization/pages/committees/pages/committee-detail/modules/committee-detail-meeting/components/committee-detail-meeting-main/committee-detail-meeting-main.component.ts index c457071ee0..153c9b71bf 100644 --- a/client/src/app/site/pages/organization/pages/committees/pages/committee-detail/modules/committee-detail-meeting/components/committee-detail-meeting-main/committee-detail-meeting-main.component.ts +++ b/client/src/app/site/pages/organization/pages/committees/pages/committee-detail/modules/committee-detail-meeting/components/committee-detail-meeting-main/committee-detail-meeting-main.component.ts @@ -13,11 +13,12 @@ import { getCommitteeMeetingDetailSubscriptionConfig } from '../../../../../../c export class CommitteeDetailMeetingMainComponent extends BaseModelRequestHandlerComponent { protected override onNextMeetingId(id: Id | null): void { if (id) { - this.subscribeTo(getCommitteeMeetingDetailSubscriptionConfig(id), { hideWhenDestroyed: true }); + this.updateSubscribeTo(getCommitteeMeetingDetailSubscriptionConfig(id), { hideWhenDestroyed: true }); } } - protected override onShouldCreateModelRequests(): void { + protected override onShouldCreateModelRequests(_params: any, meetingId: Id): void { this.subscribeTo(getMeetingCreateSubscriptionConfig()); + this.subscribeTo(getCommitteeMeetingDetailSubscriptionConfig(meetingId), { hideWhenDestroyed: true }); } } From 736e64c0fd319e15149fad4d4e46d9fb4623aea1 Mon Sep 17 00:00:00 2001 From: Elblinator <69210919+Elblinator@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:39:04 +0200 Subject: [PATCH 06/47] add height (#3608) --- .../call-dialog/call-dialog.component.html | 4 ++-- .../call-dialog/call-dialog.component.scss | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/client/src/app/site/pages/meetings/pages/interaction/modules/interaction-container/components/call-dialog/call-dialog.component.html b/client/src/app/site/pages/meetings/pages/interaction/modules/interaction-container/components/call-dialog/call-dialog.component.html index f14f3fdcd5..6916eb570a 100644 --- a/client/src/app/site/pages/meetings/pages/interaction/modules/interaction-container/components/call-dialog/call-dialog.component.html +++ b/client/src/app/site/pages/meetings/pages/interaction/modules/interaction-container/components/call-dialog/call-dialog.component.html @@ -1,8 +1,8 @@
- + -
+
Date: Fri, 26 Apr 2024 16:52:10 +0200 Subject: [PATCH 07/47] Add action for cherry-picking staging changes (#3610) --- .github/workflows/pick-to-staging.yml | 66 +++++++++++++++++++++++++++ .github/workflows/staging-to-main.yml | 54 ---------------------- 2 files changed, 66 insertions(+), 54 deletions(-) create mode 100644 .github/workflows/pick-to-staging.yml delete mode 100644 .github/workflows/staging-to-main.yml diff --git a/.github/workflows/pick-to-staging.yml b/.github/workflows/pick-to-staging.yml new file mode 100644 index 0000000000..076a8764b5 --- /dev/null +++ b/.github/workflows/pick-to-staging.yml @@ -0,0 +1,66 @@ +name: Cherry pick staging PRs merged into main + +on: + pull_request: + types: + - closed + branches: + - 'main' + + +jobs: + create-pr-for-staging: + if: | + github.event.pull_request.merged == true && + contains(github.event.pull_request.labels.*.name, 'staging') + name: Create PR against staging branch + runs-on: ubuntu-latest + + steps: + - name: Checkout main + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 2 + + - name: Fetch and checkout latest staging branch + run: | + branch=$(git ls-remote --heads origin 'staging*' | tail -1 | awk 'gsub(".*refs/heads/","")') + git fetch origin $branch + git checkout $branch + + - name: Set git credentials + run: | + git config --global user.name openslides-automation + git config --global user.email openslides-automation@users.noreply.github.com + + - name: Cherry-pick new commit + id: cherry-pick + run: | + git fetch origin + # -m 1 to also be able to cherry-pick merge commits + git cherry-pick -m 1 ${{ github.sha }} || { + echo "error=1" >> $GITHUB_OUTPUT + git add . + git cherry-pick --continue + } + + - name: Generate access token + uses: tibdex/github-app-token@v2 + id: generate-token + with: + app_id: ${{ secrets.AUTOMATION_APP_ID }} + private_key: ${{ secrets.AUTOMATION_APP_PRIVATE_KEY }} + + - name: Create or update PR + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ steps.generate-token.outputs.token }} + branch: apply/commit-${{ github.sha }} + delete-branch: true + title: "[Cherry-Pick] ${{ github.event.pull_request.title }}" + body: "Triggered by commit [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }})\n\n${{ steps.cherry-pick.outputs.error && 'There were conflicts during the cherry-pick. These were commited without any resolving. Please resolve them manually and push the result to this branch before merging.' || 'The cherry-pick was successful without any conflicts. You should be able to simply merge this PR.' }}" + reviewers: ${{ github.event.pull_request.user.login }} + assignees: ${{ github.event.pull_request.user.login }} + labels: picked-to-staging + milestone: 4 diff --git a/.github/workflows/staging-to-main.yml b/.github/workflows/staging-to-main.yml deleted file mode 100644 index 2bb8525ffc..0000000000 --- a/.github/workflows/staging-to-main.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Copy staging commits to main - -on: - push: - branches: - - 'staging/4*' - - -jobs: - create-pr-for-main: - name: Create PR against main branch - runs-on: ubuntu-latest - - steps: - - name: Checkout main - uses: actions/checkout@v4 - with: - ref: main - - - name: Set git credentials - run: | - git config --global user.name openslides-automation - git config --global user.email openslides-automation@users.noreply.github.com - - - name: Cherry-pick new commit - id: cherry-pick - run: | - git fetch origin - git cherry-pick ${{ github.sha }} || { - echo "error=1" >> $GITHUB_OUTPUT - git add . - git cherry-pick --continue - } - - - name: Generate access token - uses: tibdex/github-app-token@v2 - id: generate-token - with: - app_id: ${{ secrets.AUTOMATION_APP_ID }} - private_key: ${{ secrets.AUTOMATION_APP_PRIVATE_KEY }} - - - name: Create or update PR - uses: peter-evans/create-pull-request@v6 - with: - token: ${{ steps.generate-token.outputs.token }} - commit-message: ${{ github.event.commits[0].message }} - branch: apply/commit-${{ github.sha }} - delete-branch: true - title: ${{ github.event.commits[0].message }} - body: "Triggered by commit [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }})\n\n${{ steps.cherry-pick.outputs.error && 'There were conflicts during the cherry-pick. These were commited without any resolving. Please resolve them manually and push the result to this branch before merging.' || 'The cherry-pick was successful without any conflicts. You should be able to simply merge this PR.' }}" - reviewers: ${{ github.event.commits[0].author.username }} - assignees: ${{ github.event.commits[0].author.username }} - labels: staging-port - milestone: 4 From 37b6f50d51fe8bd5b1f08a9f2539051ca7e5563f Mon Sep 17 00:00:00 2001 From: reicda <165993199+reicda@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:40:01 +0200 Subject: [PATCH 08/47] FIX Navigation Between Motions and Amendments (#3559) --- .../motion-detail-view.component.ts | 46 +++++++++++++++++-- .../index.ts | 1 + .../motion-detail-view-originurl.service.ts | 27 +++++++++++ .../amendment-controller.service.ts | 2 +- 4 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/services/motion-detail-view-originurl.service/index.ts create mode 100644 client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/services/motion-detail-view-originurl.service/motion-detail-view-originurl.service.ts diff --git a/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-detail-view/motion-detail-view.component.ts b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-detail-view/motion-detail-view.component.ts index 788595a15e..13ff98cc9f 100644 --- a/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-detail-view/motion-detail-view.component.ts +++ b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-detail-view/motion-detail-view.component.ts @@ -32,6 +32,7 @@ import { AmendmentListSortService } from '../../../../services/list/amendment-li import { MotionListFilterService } from '../../../../services/list/motion-list-filter.service/motion-list-filter.service'; import { MotionListSortService } from '../../../../services/list/motion-list-sort.service/motion-list-sort.service'; import { MotionDetailViewService } from '../../services/motion-detail-view.service'; +import { MotionDetailViewOriginUrlService } from '../../services/motion-detail-view-originurl.service'; @Component({ selector: `os-motion-detail-view`, @@ -138,6 +139,8 @@ export class MotionDetailViewComponent extends BaseMeetingComponent implements O private _amendmentsInMainList = false; + private _navigatedFromAmendmentList = false; + public constructor( protected override translate: TranslateService, public vp: ViewPortService, @@ -155,7 +158,8 @@ export class MotionDetailViewComponent extends BaseMeetingComponent implements O private amendmentSortService: AmendmentListSortService, private amendmentFilterService: AmendmentListFilterService, private cd: ChangeDetectorRef, - private pdfExport: MotionPdfExportService + private pdfExport: MotionPdfExportService, + private originUrlService: MotionDetailViewOriginUrlService ) { super(); @@ -195,6 +199,21 @@ export class MotionDetailViewComponent extends BaseMeetingComponent implements O return () => this.saveMotion(); } + /** + * Sets @var this._navigatedFromAmendmentList on navigation from either of both lists. + * Does nothing on navigation between two motions. + */ + private isNavigatedFromAmendments() { + const previousUrl = this.originUrlService.getPreviousUrl(); + if (!!previousUrl) { + if (previousUrl.endsWith(`amendments`)) { + this._navigatedFromAmendmentList = true; + } else if (previousUrl.endsWith(`motions`)) { + this._navigatedFromAmendmentList = false; + } + } + } + public goToHistory(): void { this.router.navigate([this.activeMeetingId!, `history`], { queryParams: { fqid: this.motion.fqid } }); } @@ -301,17 +320,37 @@ export class MotionDetailViewComponent extends BaseMeetingComponent implements O public setSurroundingMotions(): void { const indexOfCurrent = this._sortedMotions.findIndex(motion => motion === this.motion); if (indexOfCurrent > 0) { - this.previousMotion = this._sortedMotions[indexOfCurrent - 1]; + this.previousMotion = this.findNextSuitableMotion(indexOfCurrent, -1); } else { this.previousMotion = null; } if (indexOfCurrent > -1 && indexOfCurrent < this._sortedMotions.length - 1) { - this.nextMotion = this._sortedMotions[indexOfCurrent + 1]; + this.nextMotion = this.findNextSuitableMotion(indexOfCurrent, 1); } else { this.nextMotion = null; } } + /** + * Finds the next suitable motion. + * If @var this._amendmentsInMainList as well as @var this._navigatedFromAmendmentList collide + * iterates over the next or previous motions to find the first with lead motion. + * @param indexOfCurrent The index from the active motion. + * @param step Stepwidth to iterate eiter over the previous or next motions. + */ + private findNextSuitableMotion(indexOfCurrent: number, step: number) { + if (!this._amendmentsInMainList || !this._navigatedFromAmendmentList) { + return this._sortedMotions[indexOfCurrent + step]; + } + + for (let i = indexOfCurrent + step; 0 <= i && i <= this._sortedMotions.length - 1; i += step) { + if (!!this._sortedMotions[i].hasLeadMotion) { + return this._sortedMotions[i]; + } + } + return null; + } + /** * Click handler for the pdf button */ @@ -488,6 +527,7 @@ export class MotionDetailViewComponent extends BaseMeetingComponent implements O private init(): void { this.cd.reattach(); + this.isNavigatedFromAmendments(); this.registerSubjects(); // use the filter and the search service to get the current sorting diff --git a/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/services/motion-detail-view-originurl.service/index.ts b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/services/motion-detail-view-originurl.service/index.ts new file mode 100644 index 0000000000..c025ac0b32 --- /dev/null +++ b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/services/motion-detail-view-originurl.service/index.ts @@ -0,0 +1 @@ +export * from './motion-detail-view-originurl.service'; diff --git a/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/services/motion-detail-view-originurl.service/motion-detail-view-originurl.service.ts b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/services/motion-detail-view-originurl.service/motion-detail-view-originurl.service.ts new file mode 100644 index 0000000000..0540339b1f --- /dev/null +++ b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/services/motion-detail-view-originurl.service/motion-detail-view-originurl.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { Router, RoutesRecognized } from '@angular/router'; +import { filter, pairwise } from 'rxjs/operators'; + +import { MotionDetailServiceModule } from '../motion-detail-service.module'; + +@Injectable({ + providedIn: MotionDetailServiceModule +}) +export class MotionDetailViewOriginUrlService { + private previousUrl: string; + + public constructor(private router: Router) { + this.router.events + .pipe( + filter((e: any) => e instanceof RoutesRecognized), + pairwise() + ) + .subscribe((events: RoutesRecognized[]) => { + this.previousUrl = events[0].urlAfterRedirects; + }); + } + + public getPreviousUrl() { + return this.previousUrl; + } +} diff --git a/client/src/app/site/pages/meetings/pages/motions/services/common/amendment-controller.service/amendment-controller.service.ts b/client/src/app/site/pages/meetings/pages/motions/services/common/amendment-controller.service/amendment-controller.service.ts index e138e9a830..a604c25be3 100644 --- a/client/src/app/site/pages/meetings/pages/motions/services/common/amendment-controller.service/amendment-controller.service.ts +++ b/client/src/app/site/pages/meetings/pages/motions/services/common/amendment-controller.service/amendment-controller.service.ts @@ -59,7 +59,7 @@ export class AmendmentControllerService { public getSortedViewModelListObservableFor(motion: Identifiable, key = `default`): Observable { return this.getSortedViewModelListObservable(key).pipe( - map(_motions => _motions.filter(_motion => _motion.lead_motion_id === motion.id)) + map(_motions => _motions.filter(_motion => _motion.hasLeadMotion === true)) ); } From 1d4d35939cd7a67255690b8ee7956a9e0ecb03c4 Mon Sep 17 00:00:00 2001 From: reicda <165993199+reicda@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:49:18 +0200 Subject: [PATCH 09/47] FIX File Icons Escaping To The Left (#3563) --- .../components/file-list/file-list.component.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/src/app/ui/modules/file-list/components/file-list/file-list.component.scss b/client/src/app/ui/modules/file-list/components/file-list/file-list.component.scss index 5963774552..8aae3cd16a 100644 --- a/client/src/app/ui/modules/file-list/components/file-list/file-list.component.scss +++ b/client/src/app/ui/modules/file-list/components/file-list/file-list.component.scss @@ -89,3 +89,10 @@ $navbar-size: 40px; border: 0px solid; background-color: rgba(0, 0, 0, 0.087); } + +.detail-link { + os-icon-container { + width: fit-content; + margin-left: auto; + } +} From 7d2005810eda98824bb368bba38726c3d5f672d2 Mon Sep 17 00:00:00 2001 From: Bastian Rihm Date: Tue, 30 Apr 2024 10:13:37 +0200 Subject: [PATCH 10/47] Improve meeting user relation handling (#3573) --- .../meeting-user-repository.service.ts | 39 +++++++++++++ .../users/user-repository.service.ts | 31 ++++------ .../pages/agenda/agenda.subscription.ts | 36 +----------- .../assignments/assignments.subscription.ts | 21 +------ .../pages/autopilot/autopilot.subscription.ts | 9 +-- .../pages/motions/motions.subscription.ts | 58 ++----------------- .../participants/participants.subscription.ts | 15 ++--- client/src/app/worker/sw-autoupdate.ts | 33 +++++++---- 8 files changed, 87 insertions(+), 155 deletions(-) diff --git a/client/src/app/gateways/repositories/meeting_user/meeting-user-repository.service.ts b/client/src/app/gateways/repositories/meeting_user/meeting-user-repository.service.ts index a4b1f290c0..4498ec3608 100644 --- a/client/src/app/gateways/repositories/meeting_user/meeting-user-repository.service.ts +++ b/client/src/app/gateways/repositories/meeting_user/meeting-user-repository.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { Id } from 'src/app/domain/definitions/key-types'; import { MeetingUser } from 'src/app/domain/models/meeting-users/meeting-user'; import { toDecimal } from 'src/app/infrastructure/utils'; import { ViewMeetingUser } from 'src/app/site/pages/meetings/view-models/view-meeting-user'; @@ -17,6 +18,8 @@ export type MeetingUserPatchFn = export class MeetingUserRepositoryService extends BaseMeetingRelatedRepository { public override readonly resetOnMeetingChange = false; + private meetingUserIdMap: Map> = new Map(); + public constructor(repositoryServiceCollector: RepositoryMeetingServiceCollectorService) { super(repositoryServiceCollector, MeetingUser); } @@ -66,4 +69,40 @@ export class MeetingUserRepositoryService extends BaseMeetingRelatedRepository viewUser.user?.getTitle() ?? `Unknown`; public getVerboseName = (plural = false): string => this.translate.instant(plural ? `Participants` : `Participant`); + + public getMeetingUserId(userId: Id, meetingId: Id): Id | null { + if (this.meetingUserIdMap.has(userId) && this.meetingUserIdMap.get(userId).has(meetingId)) { + return this.meetingUserIdMap.get(userId).get(meetingId); + } + + return null; + } + + public override deleteModels(ids: Id[]): void { + ids.forEach(id => { + const user = this.viewModelStore[id]; + if (user && user.user_id && user.meeting_id) { + const meetingUserId = this.meetingUserIdMap.get(user.user_id)?.get(user.meeting_id); + if (meetingUserId === user.id) { + this.meetingUserIdMap.get(user.user_id).delete(user.meeting_id); + } + } + }); + super.deleteModels(ids); + } + + protected override clearViewModelStore(): void { + super.clearViewModelStore(); + this.meetingUserIdMap = new Map(); + } + + protected override onCreateViewModel(meetingUser: ViewMeetingUser): void { + if (meetingUser.user_id && meetingUser.meeting_id) { + if (!this.meetingUserIdMap.has(meetingUser.user_id)) { + this.meetingUserIdMap.set(meetingUser.user_id, new Map()); + } + + this.meetingUserIdMap.get(meetingUser.user_id).set(meetingUser.meeting_id, meetingUser.id); + } + } } diff --git a/client/src/app/gateways/repositories/users/user-repository.service.ts b/client/src/app/gateways/repositories/users/user-repository.service.ts index 5b8444dd94..d056099961 100644 --- a/client/src/app/gateways/repositories/users/user-repository.service.ts +++ b/client/src/app/gateways/repositories/users/user-repository.service.ts @@ -338,26 +338,17 @@ export class UserRepositoryService extends BaseRepository { protected override createViewModel(model: User): ViewUser { const viewModel = super.createViewModel(model); - const meetingUserIdMap = new Map(); - const getMeetingUserId = (meetingId: Id) => { - if (!meetingUserIdMap.has(meetingId)) { - for (const meetingUser of this.relationManager.handleRelation( - viewModel.getModel(), - this.relationsByKey[`meeting_users`] - )) { - meetingUserIdMap.set(meetingUser.meeting_id, meetingUser.id); - } - } - - return meetingUserIdMap.get(meetingId); - }; - - viewModel.getName = () => this.getName(viewModel); - viewModel.getShortName = () => this.getShortName(viewModel); - viewModel.getFullName = (structureLevel?: ViewStructureLevel) => this.getFullName(viewModel, structureLevel); - viewModel.getMeetingUser = (meetingId: Id) => this.getMeetingUser(getMeetingUserId, meetingId); - viewModel.getLevelAndNumber = () => this.getLevelAndNumber(viewModel); - viewModel.getEnsuredActiveMeetingId = () => this.activeMeetingIdService.meetingId; + viewModel.getName = (): string => this.getName(viewModel); + viewModel.getShortName = (): string => this.getShortName(viewModel); + viewModel.getFullName = (structureLevel?: ViewStructureLevel): string => + this.getFullName(viewModel, structureLevel); + viewModel.getMeetingUser = (meetingId: Id): ViewMeetingUser => + this.getMeetingUser( + (meetingId: Id): Id => this.meetingUserRepo.getMeetingUserId(model.id, meetingId), + meetingId + ); + viewModel.getLevelAndNumber = (): string => this.getLevelAndNumber(viewModel); + viewModel.getEnsuredActiveMeetingId = (): Id => this.activeMeetingIdService.meetingId; return viewModel; } diff --git a/client/src/app/site/pages/meetings/pages/agenda/agenda.subscription.ts b/client/src/app/site/pages/meetings/pages/agenda/agenda.subscription.ts index 1b03dc0009..d6e7914935 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/agenda.subscription.ts +++ b/client/src/app/site/pages/meetings/pages/agenda/agenda.subscription.ts @@ -1,5 +1,5 @@ import { Id } from 'src/app/domain/definitions/key-types'; -import { FULL_FIELDSET, MEETING_ROUTING_FIELDS, mergeSubscriptionFollow } from 'src/app/domain/fieldsets/misc'; +import { FULL_FIELDSET, MEETING_ROUTING_FIELDS } from 'src/app/domain/fieldsets/misc'; import { MeetingUserFieldsets, UserFieldsets } from 'src/app/domain/fieldsets/user'; import { SubscriptionConfigGenerator } from 'src/app/domain/interfaces/subscription-config'; import { ViewMeeting } from 'src/app/site/pages/meetings/view-models/view-meeting'; @@ -23,21 +23,7 @@ export const agendaItemFollow: FollowList = [ idField: `speaker_ids`, fieldset: FULL_FIELDSET, follow: [ - mergeSubscriptionFollow( - { - idField: `meeting_user_id`, - follow: [ - mergeSubscriptionFollow( - { idField: `user_id`, ...UserFieldsets.FullNameSubscription }, - { - idField: `user_id`, - fieldset: [`meeting_user_ids`] - } - ) - ] - }, - { idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription } - ), + { idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription }, { idField: `point_of_order_category_id`, fieldset: FULL_FIELDSET @@ -72,23 +58,7 @@ export const getAgendaListSubscriptionConfig: SubscriptionConfigGenerator = (id: { idField: `submitter_ids`, fieldset: FULL_FIELDSET, - follow: [ - mergeSubscriptionFollow( - { - idField: `meeting_user_id`, - follow: [ - mergeSubscriptionFollow( - { idField: `user_id`, ...UserFieldsets.FullNameSubscription }, - { - idField: `user_id`, - fieldset: [`meeting_user_ids`] - } - ) - ] - }, - { idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription } - ) - ] + follow: [{ idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription }] } ] } diff --git a/client/src/app/site/pages/meetings/pages/assignments/assignments.subscription.ts b/client/src/app/site/pages/meetings/pages/assignments/assignments.subscription.ts index 867b587be1..ba69e5432f 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/assignments.subscription.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/assignments.subscription.ts @@ -1,6 +1,5 @@ import { Id } from 'src/app/domain/definitions/key-types'; -import { mergeSubscriptionFollow } from 'src/app/domain/fieldsets/misc'; -import { MeetingUserFieldsets, UserFieldsets } from 'src/app/domain/fieldsets/user'; +import { MeetingUserFieldsets } from 'src/app/domain/fieldsets/user'; import { SubscriptionConfigGenerator } from 'src/app/domain/interfaces/subscription-config'; import { ViewMeeting } from 'src/app/site/pages/meetings/view-models/view-meeting'; @@ -24,23 +23,7 @@ export const getAssignmentSubscriptionConfig: SubscriptionConfigGenerator = (id: }, { idField: `assignment_candidate_ids`, - follow: [ - mergeSubscriptionFollow( - { - idField: `meeting_user_id`, - follow: [ - mergeSubscriptionFollow( - { idField: `user_id`, ...UserFieldsets.FullNameSubscription }, - { - idField: `user_id`, - fieldset: [`meeting_user_ids`] - } - ) - ] - }, - { idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription } - ) - ] + follow: [{ idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription }] } ] }, diff --git a/client/src/app/site/pages/meetings/pages/autopilot/autopilot.subscription.ts b/client/src/app/site/pages/meetings/pages/autopilot/autopilot.subscription.ts index 61875bedb6..985b33fb6e 100644 --- a/client/src/app/site/pages/meetings/pages/autopilot/autopilot.subscription.ts +++ b/client/src/app/site/pages/meetings/pages/autopilot/autopilot.subscription.ts @@ -1,6 +1,6 @@ import { Id } from 'src/app/domain/definitions/key-types'; import { FULL_FIELDSET, MEETING_ROUTING_FIELDS, mergeSubscriptionFollow } from 'src/app/domain/fieldsets/misc'; -import { MeetingUserFieldsets, UserFieldsets } from 'src/app/domain/fieldsets/user'; +import { MeetingUserFieldsets } from 'src/app/domain/fieldsets/user'; import { SubscriptionConfig, SubscriptionConfigGenerator } from 'src/app/domain/interfaces/subscription-config'; import { ViewMeeting } from 'src/app/site/pages/meetings/view-models/view-meeting'; @@ -61,13 +61,6 @@ export const getAutopilotContentSubscriptionConfig = (id: Id): SubscriptionConfi { idField: `meeting_user_id`, follow: [ - mergeSubscriptionFollow( - { idField: `user_id`, ...UserFieldsets.FullNameSubscription }, - { - idField: `user_id`, - fieldset: [`meeting_user_ids`] - } - ), { idField: `structure_level_ids`, fieldset: [`name`, `color`] diff --git a/client/src/app/site/pages/meetings/pages/motions/motions.subscription.ts b/client/src/app/site/pages/meetings/pages/motions/motions.subscription.ts index 910c1d4c6d..da79017e59 100644 --- a/client/src/app/site/pages/meetings/pages/motions/motions.subscription.ts +++ b/client/src/app/site/pages/meetings/pages/motions/motions.subscription.ts @@ -1,6 +1,6 @@ import { Id } from 'src/app/domain/definitions/key-types'; -import { FULL_FIELDSET, mergeSubscriptionFollow } from 'src/app/domain/fieldsets/misc'; -import { MeetingUserFieldsets, UserFieldsets } from 'src/app/domain/fieldsets/user'; +import { FULL_FIELDSET } from 'src/app/domain/fieldsets/misc'; +import { MeetingUserFieldsets } from 'src/app/domain/fieldsets/user'; import { SubscriptionConfigGenerator } from 'src/app/domain/interfaces/subscription-config'; import { ViewMeeting } from 'src/app/site/pages/meetings/view-models/view-meeting'; @@ -72,65 +72,17 @@ export const getMotionListSubscriptionConfig: SubscriptionConfigGenerator = (id: { idField: `submitter_ids`, fieldset: FULL_FIELDSET, - follow: [ - mergeSubscriptionFollow( - { - idField: `meeting_user_id`, - follow: [ - mergeSubscriptionFollow( - { idField: `user_id`, ...UserFieldsets.FullNameSubscription }, - { - idField: `user_id`, - fieldset: [`meeting_user_ids`] - } - ) - ] - }, - { idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription } - ) - ] + follow: [{ idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription }] }, { idField: `editor_ids`, fieldset: FULL_FIELDSET, - follow: [ - mergeSubscriptionFollow( - { - idField: `meeting_user_id`, - follow: [ - mergeSubscriptionFollow( - { idField: `user_id`, ...UserFieldsets.FullNameSubscription }, - { - idField: `user_id`, - fieldset: [`meeting_user_ids`] - } - ) - ] - }, - { idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription } - ) - ] + follow: [{ idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription }] }, { idField: `working_group_speaker_ids`, fieldset: FULL_FIELDSET, - follow: [ - mergeSubscriptionFollow( - { - idField: `meeting_user_id`, - follow: [ - mergeSubscriptionFollow( - { idField: `user_id`, ...UserFieldsets.FullNameSubscription }, - { - idField: `user_id`, - fieldset: [`meeting_user_ids`] - } - ) - ] - }, - { idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription } - ) - ] + follow: [{ idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription }] } ] } diff --git a/client/src/app/site/pages/meetings/pages/participants/participants.subscription.ts b/client/src/app/site/pages/meetings/pages/participants/participants.subscription.ts index 510fbbe773..5187a92018 100644 --- a/client/src/app/site/pages/meetings/pages/participants/participants.subscription.ts +++ b/client/src/app/site/pages/meetings/pages/participants/participants.subscription.ts @@ -1,6 +1,6 @@ import { Id } from 'src/app/domain/definitions/key-types'; -import { FULL_FIELDSET, MEETING_ROUTING_FIELDS, mergeSubscriptionFollow } from 'src/app/domain/fieldsets/misc'; -import { MeetingUserFieldsets, UserFieldsets } from 'src/app/domain/fieldsets/user'; +import { FULL_FIELDSET, MEETING_ROUTING_FIELDS } from 'src/app/domain/fieldsets/misc'; +import { MeetingUserFieldsets } from 'src/app/domain/fieldsets/user'; import { SubscriptionConfigGenerator } from 'src/app/domain/interfaces/subscription-config'; import { ViewMeeting } from 'src/app/site/pages/meetings/view-models/view-meeting'; import { DEFAULT_FIELDSET } from 'src/app/site/services/model-request-builder'; @@ -32,13 +32,12 @@ export const getParticipantVoteInfoSubscriptionConfig: SubscriptionConfigGenerat `is_physical_person`, `is_active`, `meeting_ids`, - `meeting_user_ids`, `is_present_in_meeting_ids` ] }, { idField: `vote_delegated_to_id`, - follow: [{ idField: `user_id`, fieldset: [`is_present_in_meeting_ids`, `meeting_user_ids`] }], + follow: [{ idField: `user_id`, fieldset: [`is_present_in_meeting_ids`] }], fieldset: [`meeting_id`] } ] @@ -138,13 +137,7 @@ export const getSpeakersListSubscriptionConfig: SubscriptionConfigGenerator = (i idField: `speaker_ids`, fieldset: FULL_FIELDSET, follow: [ - mergeSubscriptionFollow( - { - idField: `meeting_user_id`, - follow: [{ idField: `user_id`, ...UserFieldsets.FullNameSubscription }] - }, - { idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription } - ), + { idField: `meeting_user_id`, ...MeetingUserFieldsets.FullNameSubscription }, { idField: `structure_level_list_of_speakers_id`, fieldset: [], diff --git a/client/src/app/worker/sw-autoupdate.ts b/client/src/app/worker/sw-autoupdate.ts index 64524b6a54..0bd5eb9521 100644 --- a/client/src/app/worker/sw-autoupdate.ts +++ b/client/src/app/worker/sw-autoupdate.ts @@ -19,6 +19,7 @@ const subscriptionQueues: { [key: string]: AutoupdateSubscription[] } = { required: [], requiredMeeting: [], sequentialnumbermapping: [], + detail: [], other: [] }; const openTimeouts = { @@ -77,7 +78,7 @@ function openConnection( function getRequestCategory( description: string, _request: unknown - ): 'required' | 'requiredMeeting' | 'other' | 'sequentialnumbermapping' { + ): 'required' | 'requiredMeeting' | 'other' | 'sequentialnumbermapping' | null { const required = [`theme_list:subscription`, `operator:subscription`, `organization:subscription`]; if (required.indexOf(description) !== -1) { return `required`; @@ -92,6 +93,12 @@ function openConnection( return `sequentialnumbermapping`; } + // Subscriptions ending with a number nomally are used for detail subscriptions + // and should not be bundled + if (!isNaN(+description.substring(description.length - 13, description.length - 14))) { + return null; + } + return `other`; } @@ -123,16 +130,20 @@ function openConnection( const category = getRequestCategory(description, request); const subscription = new AutoupdateSubscription(streamId, queryParams, requestHash, request, description, [ctx]); - subscriptionQueues[category].push(subscription); - - clearTimeout(openTimeouts[category]); - openTimeouts[category] = setTimeout(() => { - const queue = subscriptionQueues[category]; - subscriptionQueues[category] = []; - openTimeouts[category] = undefined; - - autoupdatePool.openNewStream(queue, queryParams); - }, 5); + if (category) { + subscriptionQueues[category].push(subscription); + + clearTimeout(openTimeouts[category]); + openTimeouts[category] = setTimeout(() => { + const queue = subscriptionQueues[category]; + subscriptionQueues[category] = []; + openTimeouts[category] = undefined; + + autoupdatePool.openNewStream(queue, queryParams); + }, 5); + } else { + autoupdatePool.openNewStream([subscription], queryParams); + } } function closeConnection(ctx: MessagePort, params: AutoupdateCloseStreamParams): void { From dacb94c6a8ac5d14a6b996ba40129d417564f8f9 Mon Sep 17 00:00:00 2001 From: Bastian Rihm Date: Tue, 30 Apr 2024 12:24:21 +0200 Subject: [PATCH 11/47] Fix election projection user names not displayed (#3623) --- .../components/poll-slide/components/poll-slide.component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/app/site/pages/meetings/modules/projector/modules/slides/components/poll-slide/components/poll-slide.component.ts b/client/src/app/site/pages/meetings/modules/projector/modules/slides/components/poll-slide/components/poll-slide.component.ts index c8f1cab69e..fec9fc3ee6 100644 --- a/client/src/app/site/pages/meetings/modules/projector/modules/slides/components/poll-slide/components/poll-slide.component.ts +++ b/client/src/app/site/pages/meetings/modules/projector/modules/slides/components/poll-slide/components/poll-slide.component.ts @@ -120,6 +120,8 @@ export class PollSlideComponent extends BaseSlideComponent { const getOptionTitle: () => OptionTitle = () => { if (data.text) { return { title: data.text }; + } else if (data.content_object && data.content_object.collection === `user`) { + return { title: (data.content_object).username }; } else if (data.content_object) { modifyAgendaItemNumber(data.content_object!); const repo = this.collectionMapperService.getRepository(data.content_object!.collection); From 63dd146f6a95297ff7abe8a3411772b856ab7978 Mon Sep 17 00:00:00 2001 From: Bastian Rihm Date: Tue, 30 Apr 2024 16:33:53 +0200 Subject: [PATCH 12/47] Fix pdf export line break delete if line numbering disabled (#3625) --- .../motion-html-to-pdf.service/motion-html-to-pdf.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/app/site/pages/meetings/pages/motions/services/export/motion-html-to-pdf.service/motion-html-to-pdf.service.ts b/client/src/app/site/pages/meetings/pages/motions/services/export/motion-html-to-pdf.service/motion-html-to-pdf.service.ts index 8ae680e00a..4443c9ceba 100644 --- a/client/src/app/site/pages/meetings/pages/motions/services/export/motion-html-to-pdf.service/motion-html-to-pdf.service.ts +++ b/client/src/app/site/pages/meetings/pages/motions/services/export/motion-html-to-pdf.service/motion-html-to-pdf.service.ts @@ -54,6 +54,7 @@ export class MotionHtmlToPdfService extends HtmlToPdfService { // Cleanup of dirty html would happen here if (this.lineNumberingMode === LineNumberingMode.None) { htmlText = htmlText.replace(/\s+
/g, ` `); + htmlText = htmlText.replace(/
/g, ``); } else { htmlText = htmlText.replace(/\s+
/g, `
`); } From a2931b8682578cf4a2ec1313ca316f23aa5ed98f Mon Sep 17 00:00:00 2001 From: Elblinator <69210919+Elblinator@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:45:55 +0200 Subject: [PATCH 13/47] Fix editorial final version in a motion (#3621) --- .../motion-highlight-form.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-highlight-form/motion-highlight-form.component.ts b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-highlight-form/motion-highlight-form.component.ts index c492a96d50..bf7e1f558e 100644 --- a/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-highlight-form/motion-highlight-form.component.ts +++ b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-highlight-form/motion-highlight-form.component.ts @@ -3,7 +3,7 @@ import { UntypedFormControl } from '@angular/forms'; import { ErrorStateMatcher } from '@angular/material/core'; import { MatLegacyMenuTrigger as MatMenuTrigger } from '@angular/material/legacy-menu'; import { TranslateService } from '@ngx-translate/core'; -import { combineLatest, Subscription } from 'rxjs'; +import { combineLatest, distinctUntilChanged, Subscription, takeUntil, timer } from 'rxjs'; import { ChangeRecoMode, LineNumberingMode } from 'src/app/domain/models/motions/motions.constants'; import { ViewMotion, ViewMotionChangeRecommendation } from 'src/app/site/pages/meetings/pages/motions'; import { ViewPortService } from 'src/app/site/services/view-port.service'; @@ -310,10 +310,10 @@ export class MotionHighlightFormComponent extends BaseMotionDetailChildComponent .get(`motions_default_line_numbering`) .subscribe(mode => this.setLineNumberingMode(mode)), combineLatest([ - this.changeRecoRepo.getViewModelListObservable(), - this.meetingSettingsService.get(`motions_recommendation_text_mode`) + this.changeRecoRepo.getViewModelListObservable().pipe(takeUntil(timer(1000))), + this.meetingSettingsService.get(`motions_recommendation_text_mode`).pipe(distinctUntilChanged()) ]).subscribe(([_, mode]) => { - if (mode) { + if (!this.isEditingFinalVersion && mode) { this.setChangeRecoMode(this.determineCrMode(mode as ChangeRecoMode)); } }) From e1affcf8f344901a9668bb62978ed8c7cc10157c Mon Sep 17 00:00:00 2001 From: Bastian Rihm Date: Tue, 30 Apr 2024 17:29:59 +0200 Subject: [PATCH 14/47] Add option to disable connection closing on inactivity (#3634) --- .../account-dialog.component.html | 18 +++++++++++ .../account-dialog.component.ts | 31 +++++++++++++++++-- .../services/autoupdate/autoupdate.service.ts | 14 ++++++--- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.html b/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.html index 427f42b862..7a64b3506c 100644 --- a/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.html +++ b/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.html @@ -36,6 +36,10 @@

*ngSwitchCase="menuItemsRef.CHANGE_PASSWORD" [ngTemplateOutlet]="changePasswordView" > +

@@ -95,3 +99,17 @@

{{ 'Change password' | translate }}

+ + +

{{ 'Settings' | translate }}

+
+ + {{ 'Disable connection closing on inactivity' | translate }} + +
+
+ +
+
diff --git a/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.ts b/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.ts index 0522a2d0dd..8df09e61e3 100644 --- a/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.ts +++ b/client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.ts @@ -1,8 +1,10 @@ import { Component, OnInit, ViewChild } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { MatDialogRef } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; import { TranslateService } from '@ngx-translate/core'; import { Permission } from 'src/app/domain/definitions/permission'; +import { StorageService } from 'src/app/gateways/storage.service'; import { PasswordForm, PasswordFormComponent } from 'src/app/site/modules/user-components'; import { ViewGroup } from 'src/app/site/pages/meetings/pages/participants'; import { MeetingControllerService } from 'src/app/site/pages/meetings/services/meeting-controller.service'; @@ -21,7 +23,8 @@ interface MenuItem { enum MenuItems { CHANGE_PASSWORD = `Change password`, SHOW_PROFILE = `My profile`, - SHOW_MEETINGS = `My meetings` + SHOW_MEETINGS = `My meetings`, + CLIENT_SETTINGS = `Settings` } @Component({ @@ -42,6 +45,9 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit { }, { name: MenuItems.CHANGE_PASSWORD + }, + { + name: MenuItems.CLIENT_SETTINGS } ]; @@ -89,6 +95,7 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit { public isUserPasswordValid = false; public userPersonalForm: any; public userPasswordForm!: PasswordForm; + public clientSettingsForm!: UntypedFormGroup; private _self: ViewUser | null = null; private _isUserInScope = false; @@ -102,7 +109,9 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit { private userService: UserService, private snackbar: MatSnackBar, private authService: AuthService, - private translate: TranslateService + private translate: TranslateService, + private fb: UntypedFormBuilder, + private store: StorageService ) { super(); } @@ -112,6 +121,17 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit { this.repo.getViewModelObservable(this.operator.operatorId!).subscribe(user => (this._self = user)), this.operator.operatorUpdated.subscribe(() => this.updateIsUserInScope()) ); + + this.clientSettingsForm = this.fb.group({ + disablePauseAuConnections: [false] + }); + + this.store.get(`clientSettings`).then((val: any) => { + if (val) { + this.clientSettingsForm.patchValue(val); + this.clientSettingsForm.markAsUntouched(); + } + }); } /** @@ -143,7 +163,7 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit { const meetingIds = this.self.ensuredMeetingIds; return meetingIds .map(id => this.meetingRepo.getViewModel(id) as ViewMeeting) - .sort((meetingA, meetingB) => meetingA.name.localeCompare(meetingB.name)); + .sort((meetingA, meetingB) => meetingA.name.localeCompare(meetingB?.name)); } public getGroupsForMeeting(meeting: ViewMeeting): ViewGroup[] { @@ -183,6 +203,11 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit { this.isEditing = false; } + public async saveClientSettings(): Promise { + this.store.set(`clientSettings`, this.clientSettingsForm.getRawValue()); + this.clientSettingsForm.markAsPristine(); + } + private async updateIsUserInScope(): Promise { if (this.operator.operatorId !== null) { this._isUserInScope = await this.userService.hasScopeManagePerms(this.operator.operatorId); diff --git a/client/src/app/site/services/autoupdate/autoupdate.service.ts b/client/src/app/site/services/autoupdate/autoupdate.service.ts index da753b6ac7..3c22cd109d 100644 --- a/client/src/app/site/services/autoupdate/autoupdate.service.ts +++ b/client/src/app/site/services/autoupdate/autoupdate.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { marker as _ } from '@colsen1991/ngx-translate-extract-marker'; import { firstValueFrom } from 'rxjs'; import { ModelRequest } from 'src/app/domain/interfaces/model-request'; +import { StorageService } from 'src/app/gateways/storage.service'; import { Collection, Id, Ids } from '../../../domain/definitions/key-types'; import { HttpStreamEndpointService } from '../../../gateways/http-stream'; @@ -68,7 +69,7 @@ export const OUT_OF_SYNC_BANNER: BannerDefinition = { icon: `sync_disabled` }; -const PAUSE_ON_INACTIVITY_TIMEOUT = 5 * 60 * 1000; // 5 Minutes +export const AU_PAUSE_ON_INACTIVITY_TIMEOUT = 5 * 60 * 1000; // 5 Minutes @Injectable({ providedIn: `root` @@ -85,7 +86,8 @@ export class AutoupdateService { private communication: AutoupdateCommunicationService, private bannerService: BannerService, private visibilityService: WindowVisibilityService, - private lifecycle: LifecycleService + private lifecycle: LifecycleService, + private store: StorageService ) { this.setAutoupdateConfig(null); this.httpEndpointService.registerEndpoint( @@ -102,8 +104,12 @@ export class AutoupdateService { }); firstValueFrom(this.lifecycle.appLoaded).then(() => - this.visibilityService.hiddenFor(PAUSE_ON_INACTIVITY_TIMEOUT).subscribe(() => { - this.pauseUntilVisible(); + this.visibilityService.hiddenFor(AU_PAUSE_ON_INACTIVITY_TIMEOUT).subscribe(() => { + this.store.get(`clientSettings`).then((settings: any) => { + if (!settings || !settings?.disablePauseAuConnections) { + this.pauseUntilVisible(); + } + }); }) ); From a60f556a31f10444cb2b0fe97c892b96e571cbb2 Mon Sep 17 00:00:00 2001 From: "openslides-automation[bot]" <125256978+openslides-automation[bot]@users.noreply.github.com> Date: Thu, 2 May 2024 11:10:34 +0200 Subject: [PATCH 15/47] Update meta repository (#3611) Co-authored-by: rrenkert <19685715+rrenkert@users.noreply.github.com> --- client/src/meta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/meta b/client/src/meta index ea10c6c670..7177fe38a2 160000 --- a/client/src/meta +++ b/client/src/meta @@ -1 +1 @@ -Subproject commit ea10c6c670885febc9b4dd0bd45c12a629d66314 +Subproject commit 7177fe38a2f111312c2d34eb8c8164cf679710e6 From fc1dd4f5a104c370d3f4801592ec385907da2766 Mon Sep 17 00:00:00 2001 From: reicda <165993199+reicda@users.noreply.github.com> Date: Fri, 3 May 2024 09:19:24 +0200 Subject: [PATCH 16/47] FIX Search Dialouge Layout (#3624) --- .../components/global-search/global-search.component.scss | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/src/app/site/modules/global-headbar/components/global-search/global-search.component.scss b/client/src/app/site/modules/global-headbar/components/global-search/global-search.component.scss index fe6dd65c48..0552a4ba8a 100644 --- a/client/src/app/site/modules/global-headbar/components/global-search/global-search.component.scss +++ b/client/src/app/site/modules/global-headbar/components/global-search/global-search.component.scss @@ -2,6 +2,11 @@ width: 800px; max-width: 100%; min-height: fit-content; + + &::before { + height: 0; + } + .search-top-form { display: flex; .search-input-container { @@ -126,7 +131,7 @@ padding: 0; display: flex; flex-wrap: wrap; - margin-top: 12px; + margin: 12px 0; padding: 0 50px; justify-content: space-between; .filter { From 943cfe9ade786ff8d5b9add530f204049799c467 Mon Sep 17 00:00:00 2001 From: reiterl Date: Fri, 3 May 2024 10:09:32 +0200 Subject: [PATCH 17/47] Add committee selector to motion meta data submitters (#3606) This selector allows to fill the submitters extension text field with committee names. --- ...forwarding-committees-presenter.service.ts | 25 ++++++++ .../src/app/gateways/presenter/presenter.ts | 1 + ...manage-motion-meeting-users.component.html | 13 +++++ ...n-manage-motion-meeting-users.component.ts | 58 +++++++++++++++++-- .../motion-meta-data.component.html | 2 + .../motion-meta-data.component.ts | 30 +++++++++- 6 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 client/src/app/gateways/presenter/get-forwarding-committees-presenter.service.ts diff --git a/client/src/app/gateways/presenter/get-forwarding-committees-presenter.service.ts b/client/src/app/gateways/presenter/get-forwarding-committees-presenter.service.ts new file mode 100644 index 0000000000..9abeaad619 --- /dev/null +++ b/client/src/app/gateways/presenter/get-forwarding-committees-presenter.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core'; +import { Id } from 'src/app/domain/definitions/key-types'; + +import { Presenter } from './presenter'; +import { PresenterService } from './presenter.service'; + +interface GetForwardCommitteesPresenterPayload { + meeting_id: Id; +} + +type GetForwardingCommitteesPresenterResult = string[]; + +@Injectable({ + providedIn: `root` +}) +export class GetForwardingCommitteesPresenterService { + public constructor(private presenter: PresenterService) {} + + public async call(payload: GetForwardCommitteesPresenterPayload): Promise { + return await this.presenter.call( + Presenter.GET_FORWARDING_COMMITTEES, + payload + ); + } +} diff --git a/client/src/app/gateways/presenter/presenter.ts b/client/src/app/gateways/presenter/presenter.ts index 11a56c5d2e..d7995cbb66 100644 --- a/client/src/app/gateways/presenter/presenter.ts +++ b/client/src/app/gateways/presenter/presenter.ts @@ -4,6 +4,7 @@ export enum Presenter { GET_ACTIVE_USER_AMOUNT = `get_active_users_amount`, GET_USER_RELATED_MODELS = `get_user_related_models`, GET_USER_SCOPE = `get_user_scope`, + GET_FORWARDING_COMMITTEES = `get_forwarding_committees`, GET_FORWARDING_MEETINGS = `get_forwarding_meetings`, SEARCH_USERS = `search_users`, SEARCH_DELETED_MODELS = `search_deleted_models`, diff --git a/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-manage-motion-meeting-users/motion-manage-motion-meeting-users.component.html b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-manage-motion-meeting-users/motion-manage-motion-meeting-users.component.html index d26ea32e21..e557fe96d6 100644 --- a/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-manage-motion-meeting-users/motion-manage-motion-meeting-users.component.html +++ b/client/src/app/site/pages/meetings/pages/motions/pages/motion-detail/components/motion-manage-motion-meeting-users/motion-manage-motion-meeting-users.component.html @@ -69,6 +69,19 @@

+
+ + {{ secondSelectorLabel }} + + +
+

+ -

+
+
+ + All + SSE + Longpolling + +
+ {{ 'last updated' | translate }}: {{ lastUpdated / 1000 | localizedDate : 'PPpp' }}

- {{ userIds().length }} {{ 'logged-in users' | translate }} ({{ stats.activeUserHandles }} + {{ userIds().length }} {{ 'logged-in users' | translate }} ({{ activeUserHandles }} {{ 'connections' | translate }})

{{ 'Groups' | translate }}:
  • - {{ stats.groups[groupId].name | translate }}: + {{ selectedStats.groups[groupId].name | translate }}: {{ userInGroupIds(groupId).length }} {{ 'logged-in users' | translate }} ({{ - stats.groups[groupId].userHandleCount + selectedStats.groups[groupId].userHandleCount }} {{ 'connections' | translate }})
  • diff --git a/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/components/count-users/count-users.component.scss b/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/components/count-users/count-users.component.scss index e69de29bb2..3e1b0c3e82 100644 --- a/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/components/count-users/count-users.component.scss +++ b/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/components/count-users/count-users.component.scss @@ -0,0 +1,3 @@ +.connection-selection { + margin: 20px 0; +} diff --git a/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/components/count-users/count-users.component.ts b/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/components/count-users/count-users.component.ts index bd5d53b566..0ea3a6af30 100644 --- a/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/components/count-users/count-users.component.ts +++ b/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/components/count-users/count-users.component.ts @@ -3,18 +3,33 @@ import { BaseUiComponent } from 'src/app/ui/base/base-ui-component'; import { CountUsersStatisticsService, CountUserStatistics } from '../../services/count-users-statistics.service'; +enum DISPLAY_MODES { + SSE = 0, + LONGPOLLING = 1, + BOTH = 2 +} + @Component({ selector: `os-count-users`, templateUrl: `./count-users.component.html`, styleUrls: [`./count-users.component.scss`] }) export class CountUsersComponent extends BaseUiComponent { - public stats: CountUserStatistics | null = null; + public stats: CountUserStatistics[] | null = null; + public displayMode: DISPLAY_MODES = DISPLAY_MODES.BOTH; public get lastUpdated(): number { return this.countUsersStatisticService.lastUpdated; } + public get activeUserHandles(): number { + return this.stats ? this.stats[this.displayMode].activeUserHandles : 0; + } + + public get selectedStats(): CountUserStatistics { + return this.stats ? this.stats[this.displayMode] : null; + } + public constructor(private countUsersStatisticService: CountUsersStatisticsService) { super(); } @@ -24,14 +39,14 @@ export class CountUsersComponent extends BaseUiComponent { } public userIds(): number[] { - return this.stats ? Object.keys(this.stats.activeUsers).map(id => +id) : []; + return this.stats ? Object.keys(this.stats[this.displayMode].activeUsers).map(id => +id) : []; } public groupIds(): number[] { - return this.stats ? Object.keys(this.stats.groups).map(id => +id) : []; + return this.stats ? Object.keys(this.stats[this.displayMode].groups).map(id => +id) : []; } public userInGroupIds(groupId: number): number[] { - return this.stats ? Object.keys(this.stats.groups[groupId].users).map(id => +id) : []; + return this.stats ? Object.keys(this.stats[this.displayMode].groups[groupId].users).map(id => +id) : []; } } diff --git a/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/meeting-info.module.ts b/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/meeting-info.module.ts index 43b2ae6df4..3181252d93 100644 --- a/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/meeting-info.module.ts +++ b/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/meeting-info.module.ts @@ -1,5 +1,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatCardModule } from '@angular/material/card'; import { InfoModule } from 'src/app/site/modules/info'; import { OpenSlidesTranslationModule } from 'src/app/site/modules/translations'; @@ -19,7 +21,9 @@ import { MeetingInfoRoutingModule } from './meeting-info-routing.module'; CommonModule, MeetingInfoRoutingModule, MatCardModule, + MatButtonToggleModule, HeadBarModule, + FormsModule, ListModule, DirectivesModule, OpenSlidesTranslationModule.forChild(), diff --git a/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/services/count-users-statistics.service.ts b/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/services/count-users-statistics.service.ts index 83282a967f..7d21d5e00a 100644 --- a/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/services/count-users-statistics.service.ts +++ b/client/src/app/site/pages/meetings/pages/home/pages/meeting-info/services/count-users-statistics.service.ts @@ -23,12 +23,6 @@ export interface CountUserStatistics { }; } -export const DEFAULT_COUNT_USERS_OBJECT: CountUserStatistics = { - activeUserHandles: 0, - activeUsers: {}, - groups: {} -}; - const CONNECTION_COUNT_PATH = `/system/${AUTOUPDATE_DEFAULT_ENDPOINT}/connection_count`; @Injectable({ @@ -39,7 +33,6 @@ export class CountUsersStatisticsService { return this._lastUpdated; } - private currentCount: CountUserStatistics; private _lastUpdated: number; public constructor(private http: HttpService, private userRepo: UserRepositoryService) {} @@ -47,45 +40,54 @@ export class CountUsersStatisticsService { /** * Starts counting users. * - * @returns a 2-tuple: A token to stop the counting with `stopCounting` and - * an observable where the statistics are published. + * @returns a 2-tuple of count user statistics */ - public async countUsers(): Promise { - const raw = await this.http.get<{ [key: string]: number }>(CONNECTION_COUNT_PATH); + public async countUsers(): Promise { + const raw = await this.http.get<{ [key: number]: { [id: string]: number } }>(CONNECTION_COUNT_PATH); this._lastUpdated = Date.now(); - const entries = Object.entries(raw).filter( - entry => entry[1] > 0 && this.userRepo.getViewModel(+entry[0])?.getMeetingUser() - ); - const users = Object.fromEntries(entries); - const result = { - activeUserHandles: entries - .map(entry => entry[1]) - .reduce((previousValue, currentValue) => (previousValue ?? 0) + currentValue), - activeUsers: users, - groups: {} - }; - entries.forEach(entry => { - const userId = !!entry[0] ? +entry[0] : 0; + const result: CountUserStatistics[] = []; + raw[2] = {}; + for (let i = 0; i < 2; i++) { + for (const id of Object.keys(raw[i])) { + raw[2][id] = raw[2][id] || 0; + raw[2][id] += raw[i][id]; + } + } + + for (let i = 0; i < 3; i++) { + const entries = Object.entries(raw[i]).filter( + entry => entry[1] > 0 && this.userRepo.getViewModel(+entry[0])?.getMeetingUser() + ); + const users = Object.fromEntries(entries); + result[i] = { + activeUserHandles: entries + .map(entry => entry[1]) + .reduce((previousValue, currentValue) => (previousValue ?? 0) + currentValue, 0), + activeUsers: users, + groups: {} + }; + entries.forEach(entry => { + const userId = !!entry[0] ? +entry[0] : 0; - const user = this.userRepo.getViewModel(userId); + const user = this.userRepo.getViewModel(userId); - // Add to group stats - const groups = user ? user.groups() : []; - groups.forEach(group => { - if (!result.groups[group.id]) { - result.groups[group.id] = { - name: group.name, - users: {}, - userHandleCount: 0, - meeting_id: group.meeting_id - }; - } - result.groups[group.id].userHandleCount += entry[1]; - result.groups[group.id].users[userId] = result.activeUsers[userId]; + // Add to group stats + const groups = user ? user.groups() : []; + groups.forEach(group => { + if (!result[i].groups[group.id]) { + result[i].groups[group.id] = { + name: group.name, + users: {}, + userHandleCount: 0, + meeting_id: group.meeting_id + }; + } + result[i].groups[group.id].userHandleCount += entry[1]; + result[i].groups[group.id].users[userId] = result[i].activeUsers[userId]; + }); }); - }); + } - this.currentCount = result; - return this.currentCount; + return result; } } From 28e646648b1b29c85ddcc3b5f08f453ef1357c55 Mon Sep 17 00:00:00 2001 From: Elblinator <69210919+Elblinator@users.noreply.github.com> Date: Wed, 15 May 2024 10:35:18 +0200 Subject: [PATCH 31/47] Fix accesibility linter warnings (#3647) --- .../speaking-times/speaking-times.component.html | 6 +++--- .../components/topic-detail/topic-detail.component.html | 2 +- .../agenda-item-info-dialog.component.html | 2 +- .../category-detail/category-detail.component.html | 2 +- .../components/category-list/category-list.component.html | 2 +- .../comment-section-list.component.html | 2 +- .../motion-block-create-dialog.component.html | 2 +- .../motion-block-edit-dialog.component.html | 8 +++++++- .../amendment-create-wizard.component.html | 1 + .../motion-content/motion-content.component.html | 2 +- .../tags/components/tag-list/tag-list.component.html | 2 +- .../structure-level-list.component.html | 2 +- .../components/overlay/overlay.component.html | 2 +- .../scrolling-table/scrolling-table.component.html | 1 + 14 files changed, 22 insertions(+), 14 deletions(-) diff --git a/client/src/app/site/pages/meetings/modules/list-of-speakers-content/components/speaking-times/speaking-times.component.html b/client/src/app/site/pages/meetings/modules/list-of-speakers-content/components/speaking-times/speaking-times.component.html index 553310cd74..76a53be133 100644 --- a/client/src/app/site/pages/meetings/modules/list-of-speakers-content/components/speaking-times/speaking-times.component.html +++ b/client/src/app/site/pages/meetings/modules/list-of-speakers-content/components/speaking-times/speaking-times.component.html @@ -63,7 +63,7 @@

    {{ currentEntry.name }}

    -
    + {{ 'Total time' | translate }} @@ -77,7 +77,7 @@

    {{ currentEntry.name }}

-
+
diff --git a/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/components/participant-list/participant-list.component.ts b/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/components/participant-list/participant-list.component.ts index 03cb70978a..be1472e0e7 100644 --- a/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/components/participant-list/participant-list.component.ts +++ b/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/components/participant-list/participant-list.component.ts @@ -200,11 +200,7 @@ export class ParticipantListComponent extends BaseMeetingListViewComponent) { + if (this.isHiddenSubscription) { + this.isHiddenSubscription.unsubscribe(); + } + if (typeof isHidden == `boolean`) { + this.isHiddenSubscription = null; + this._isHidden = isHidden; + } else { + isHidden.subscribe(isMobileView => { + this._isHidden = !isMobileView; + }); + } } @Input() @@ -81,6 +92,7 @@ export class ScrollingTableCellDirective implements OnInit, ScrollingTableCellDe private _property = ``; private _labelString = ``; private _isDefault = false; + private isHiddenSubscription: Subscription | null = null; public constructor( public readonly template: TemplateRef, @@ -96,6 +108,12 @@ export class ScrollingTableCellDirective implements OnInit, ScrollingTableCellDe } } + public ngOnDestroy(): void { + if (this.isHiddenSubscription) { + this.isHiddenSubscription.unsubscribe(); + } + } + private render(): void { const { width, minWidth, maxWidth } = this._config; if (width) {