diff --git a/client/src/app/site/projector/services/projector-data.service.spec.ts b/client/src/app/core/core-services/projector-data.service.spec.ts similarity index 87% rename from client/src/app/site/projector/services/projector-data.service.spec.ts rename to client/src/app/core/core-services/projector-data.service.spec.ts index 80cfc4aeb..2af4f63ba 100644 --- a/client/src/app/site/projector/services/projector-data.service.spec.ts +++ b/client/src/app/core/core-services/projector-data.service.spec.ts @@ -1,5 +1,5 @@ import { TestBed, inject } from '@angular/core/testing'; -import { E2EImportsModule } from '../../../../e2e-imports.module'; +import { E2EImportsModule } from '../../../e2e-imports.module'; import { ProjectorDataService } from './projector-data.service'; describe('ProjectorDataService', () => { diff --git a/client/src/app/site/projector/services/projector-data.service.ts b/client/src/app/core/core-services/projector-data.service.ts similarity index 79% rename from client/src/app/site/projector/services/projector-data.service.ts rename to client/src/app/core/core-services/projector-data.service.ts index 4d3eafb95..21ebc7a2f 100644 --- a/client/src/app/site/projector/services/projector-data.service.ts +++ b/client/src/app/core/core-services/projector-data.service.ts @@ -1,9 +1,11 @@ import { Injectable } from '@angular/core'; -import { WebsocketService } from 'app/core/core-services/websocket.service'; -import { Observable, BehaviorSubject } from 'rxjs'; -import { ProjectorElement } from 'app/shared/models/core/projector'; -export interface SlideData { +import { Observable, BehaviorSubject } from 'rxjs'; + +import { WebsocketService } from 'app/core/core-services/websocket.service'; +import { ProjectorElement, Projector } from 'app/shared/models/core/projector'; + +export interface SlideData { data: T; element: ProjectorElement; error?: string; @@ -40,21 +42,16 @@ export class ProjectorDataService { * @param websocketService */ public constructor(private websocketService: WebsocketService) { - // TODO: On reconnect, we do need to re-inform the server about all needed projectors. This also - // updates our projector data, which is great! this.websocketService.getOberservable('projector').subscribe((update: AllProjectorData) => { Object.keys(update).forEach(_id => { const id = parseInt(_id, 10); - if ((<{ error: string }>update[id]).error !== undefined) { - console.log(update, update[_id]); - console.log('TODO: Why does the server sends errors on autoupdates?'); - } else { - if (this.currentProjectorData[id]) { - this.currentProjectorData[id].next(update[id] as ProjectorData); - } + if (this.currentProjectorData[id]) { + this.currentProjectorData[id].next(update[id] as ProjectorData); } }); }); + + this.websocketService.reconnectEvent.subscribe(() => this.updateProjectorDataSubscription()); } /** @@ -105,4 +102,15 @@ export class ProjectorDataService { .filter(id => this.openProjectorInstances[id] > 0); this.websocketService.send('listenToProjectors', { projector_ids: allActiveProjectorIds }); } + + /** + * @returns the available projectior data for the given projector. Note that the data + * might not be there, if there is no subscribtion for this projector. But the + * data, if exist, is always the current data. + */ + public getAvailableProjectorData(projector: Projector): ProjectorData | null { + if (this.currentProjectorData[projector.id]) { + return this.currentProjectorData[projector.id].getValue(); + } + } } diff --git a/client/src/app/core/core-services/projector.service.ts b/client/src/app/core/core-services/projector.service.ts index b57f795fe..0a4ee7931 100644 --- a/client/src/app/core/core-services/projector.service.ts +++ b/client/src/app/core/core-services/projector.service.ts @@ -1,5 +1,7 @@ import { Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; + import { Projectable, ProjectorElementBuildDeskriptor, @@ -11,15 +13,16 @@ import { Projector, ProjectorElement, ProjectorElements, - IdentifiableProjectorElement + IdentifiableProjectorElement, + elementIdentifies } from 'app/shared/models/core/projector'; import { HttpService } from './http.service'; import { SlideManager } from 'app/slides/services/slide-manager.service'; import { BaseModel } from 'app/shared/models/base/base-model'; import { ViewModelStoreService } from './view-model-store.service'; import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model'; -import { TranslateService } from '@ngx-translate/core'; import { ConfigService } from '../ui-services/config.service'; +import { ProjectorDataService } from './projector-data.service'; /** * This service cares about Projectables being projected and manage all projection-related @@ -43,7 +46,8 @@ export class ProjectorService { private slideManager: SlideManager, private viewModelStore: ViewModelStoreService, private translate: TranslateService, - private configService: ConfigService + private configService: ConfigService, + private projectorDataService: ProjectorDataService ) {} /** @@ -201,7 +205,7 @@ export class ProjectorService { ): Promise { const requestData: any = {}; if (elements) { - requestData.elements = elements; + requestData.elements = this.cleanupElements(projector, elements); } if (preview) { requestData.preview = preview; @@ -218,6 +222,31 @@ export class ProjectorService { await this.http.post(`/rest/core/projector/${projector.id}/project/`, requestData); } + /** + * Cleans up stable elements with errors from the projector + * + * @param projector The projector + * @param elements The elements to clean up + * @reutns the cleaned up elements. + */ + private cleanupElements(projector: Projector, elements: ProjectorElements): ProjectorElements { + const projectorData = this.projectorDataService.getAvailableProjectorData(projector); + + if (projectorData) { + projectorData.forEach(entry => { + if (entry.data.error && entry.element.stable) { + // Remove this element + const idElementToRemove = this.slideManager.getIdentifialbeProjectorElement(entry.element); + elements = elements.filter(element => { + return !elementIdentifies(idElementToRemove, element); + }); + } + }); + } + + return elements; + } + /** * Given a projectiondefault, we want to retrieve the projector, that is assigned * to this default. diff --git a/client/src/app/shared/components/projector/projector.component.ts b/client/src/app/shared/components/projector/projector.component.ts index ac6e05079..c0f1fe341 100644 --- a/client/src/app/shared/components/projector/projector.component.ts +++ b/client/src/app/shared/components/projector/projector.component.ts @@ -8,7 +8,7 @@ import { BaseComponent } from 'app/base.component'; import { ConfigService } from 'app/core/ui-services/config.service'; import { ViewProjector } from 'app/site/projector/models/view-projector'; import { Size } from 'app/site/projector/size'; -import { SlideData, ProjectorDataService } from 'app/site/projector/services/projector-data.service'; +import { SlideData, ProjectorDataService } from 'app/core/core-services/projector-data.service'; import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service'; /** diff --git a/client/src/app/shared/components/slide-container/slide-container.component.ts b/client/src/app/shared/components/slide-container/slide-container.component.ts index 276070582..94b7057f0 100644 --- a/client/src/app/shared/components/slide-container/slide-container.component.ts +++ b/client/src/app/shared/components/slide-container/slide-container.component.ts @@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core'; import { BaseComponent } from 'app/base.component'; import { SlideManager } from 'app/slides/services/slide-manager.service'; import { BaseSlideComponent } from 'app/slides/base-slide-component'; -import { SlideData } from 'app/site/projector/services/projector-data.service'; +import { SlideData } from 'app/core/core-services/projector-data.service'; import { ProjectorElement } from 'app/shared/models/core/projector'; import { ViewProjector } from 'app/site/projector/models/view-projector'; diff --git a/client/src/app/shared/models/core/projector.ts b/client/src/app/shared/models/core/projector.ts index 87f523cad..aa970f1fc 100644 --- a/client/src/app/shared/models/core/projector.ts +++ b/client/src/app/shared/models/core/projector.ts @@ -29,6 +29,19 @@ export interface IdentifiableProjectorElement extends ProjectorElement { getIdentifiers(): (keyof IdentifiableProjectorElement)[]; } +/** + * Compares an identifiable element to an element. Every identifier of `a` must match, if + * the attribute is given in the element `b`. + * + * @param a The identifiable element + * @param b The non-identifiable element + */ +export function elementIdentifies(a: IdentifiableProjectorElement, b: ProjectorElement): boolean { + return a.getIdentifiers().every(identifier => { + return !b[identifier] || b[identifier] === a[identifier]; + }); +} + /** * Multiple elements. */ @@ -128,14 +141,24 @@ export class Projector extends BaseModel { let removedElements: ProjectorElements; let nonRemovedElements: ProjectorElements; [removedElements, nonRemovedElements] = this.partitionArray(this.elements, elementOnProjector => { - return element.getIdentifiers().every(identifier => { - return !elementOnProjector[identifier] || elementOnProjector[identifier] === element[identifier]; - }); + return elementIdentifies(element, elementOnProjector); }); this.elements = nonRemovedElements; return removedElements; } + /** + * Replaces all elements with the given elements, if these elements can identify to the + * given one. + * + * @param element The element to replace + */ + public replaceElements(element: IdentifiableProjectorElement): void { + this.elements = this.elements.map(elementOnProjector => + elementIdentifies(element, elementOnProjector) ? element : elementOnProjector + ); + } + /** * Splits up the array into two arrays. All elements with a true return value from the callback * will be in the fist array, all others in the second one. diff --git a/client/src/app/site/projector/projector.module.ts b/client/src/app/site/projector/projector.module.ts index 6f4eb3833..571490314 100644 --- a/client/src/app/site/projector/projector.module.ts +++ b/client/src/app/site/projector/projector.module.ts @@ -5,15 +5,11 @@ import { ProjectorRoutingModule } from './projector-routing.module'; import { SharedModule } from '../../shared/shared.module'; import { ProjectorListComponent } from './components/projector-list/projector-list.component'; import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component'; -import { ClockSlideService } from './services/clock-slide.service'; -import { ProjectorDataService } from './services/projector-data.service'; -import { CurrentListOfSpeakersSlideService } from './services/current-list-of-of-speakers-slide.service'; import { CountdownListComponent } from './components/countdown-list/countdown-list.component'; import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component'; import { CountdownControlsComponent } from './components/countdown-controls/countdown-controls.component'; @NgModule({ - providers: [ClockSlideService, ProjectorDataService, CurrentListOfSpeakersSlideService], imports: [CommonModule, ProjectorRoutingModule, SharedModule], declarations: [ ProjectorListComponent, diff --git a/client/src/app/slides/base-slide-component.ts b/client/src/app/slides/base-slide-component.ts index 15bc149af..29d1e2eec 100644 --- a/client/src/app/slides/base-slide-component.ts +++ b/client/src/app/slides/base-slide-component.ts @@ -1,6 +1,7 @@ import { Input } from '@angular/core'; -import { SlideData } from 'app/site/projector/services/projector-data.service'; + import { ViewProjector } from 'app/site/projector/models/view-projector'; +import { SlideData } from 'app/core/core-services/projector-data.service'; /** * Every slide has to extends this base class. It forces the slides diff --git a/client/src/app/slides/motions/motion/motion-slide.component.ts b/client/src/app/slides/motions/motion/motion-slide.component.ts index de3821783..06bb8c4dc 100644 --- a/client/src/app/slides/motions/motion/motion-slide.component.ts +++ b/client/src/app/slides/motions/motion/motion-slide.component.ts @@ -8,7 +8,7 @@ import { DiffLinesInParagraph, DiffService, LineRange } from '../../../core/ui-s import { LinenumberingService } from '../../../core/ui-services/linenumbering.service'; import { ViewUnifiedChange } from '../../../shared/models/motions/view-unified-change'; import { MotionSlideObjChangeReco } from './motion-slide-obj-change-reco'; -import { SlideData } from '../../../site/projector/services/projector-data.service'; +import { SlideData } from '../../../core/core-services/projector-data.service'; import { MotionSlideObjAmendmentParagraph } from './motion-slide-obj-amendment-paragraph'; @Component({