Merge pull request #4444 from FinnStutzenstein/stableElementsCleanup

clean up stable projectorelements, if they have an error
This commit is contained in:
Emanuel Schütze 2019-03-01 14:03:19 +01:00 committed by GitHub
commit 40de87c5b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 86 additions and 29 deletions

View File

@ -1,5 +1,5 @@
import { TestBed, inject } from '@angular/core/testing'; 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'; import { ProjectorDataService } from './projector-data.service';
describe('ProjectorDataService', () => { describe('ProjectorDataService', () => {

View File

@ -1,9 +1,11 @@
import { Injectable } from '@angular/core'; 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<T = object> { 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<T = { error?: string }> {
data: T; data: T;
element: ProjectorElement; element: ProjectorElement;
error?: string; error?: string;
@ -40,21 +42,16 @@ export class ProjectorDataService {
* @param websocketService * @param websocketService
*/ */
public constructor(private websocketService: 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) => { this.websocketService.getOberservable('projector').subscribe((update: AllProjectorData) => {
Object.keys(update).forEach(_id => { Object.keys(update).forEach(_id => {
const id = parseInt(_id, 10); 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]) { if (this.currentProjectorData[id]) {
this.currentProjectorData[id].next(update[id] as ProjectorData); 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); .filter(id => this.openProjectorInstances[id] > 0);
this.websocketService.send('listenToProjectors', { projector_ids: allActiveProjectorIds }); 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();
}
}
} }

View File

@ -1,5 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { import {
Projectable, Projectable,
ProjectorElementBuildDeskriptor, ProjectorElementBuildDeskriptor,
@ -11,15 +13,16 @@ import {
Projector, Projector,
ProjectorElement, ProjectorElement,
ProjectorElements, ProjectorElements,
IdentifiableProjectorElement IdentifiableProjectorElement,
elementIdentifies
} from 'app/shared/models/core/projector'; } from 'app/shared/models/core/projector';
import { HttpService } from './http.service'; import { HttpService } from './http.service';
import { SlideManager } from 'app/slides/services/slide-manager.service'; import { SlideManager } from 'app/slides/services/slide-manager.service';
import { BaseModel } from 'app/shared/models/base/base-model'; import { BaseModel } from 'app/shared/models/base/base-model';
import { ViewModelStoreService } from './view-model-store.service'; import { ViewModelStoreService } from './view-model-store.service';
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model'; import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
import { TranslateService } from '@ngx-translate/core';
import { ConfigService } from '../ui-services/config.service'; 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 * This service cares about Projectables being projected and manage all projection-related
@ -43,7 +46,8 @@ export class ProjectorService {
private slideManager: SlideManager, private slideManager: SlideManager,
private viewModelStore: ViewModelStoreService, private viewModelStore: ViewModelStoreService,
private translate: TranslateService, private translate: TranslateService,
private configService: ConfigService private configService: ConfigService,
private projectorDataService: ProjectorDataService
) {} ) {}
/** /**
@ -201,7 +205,7 @@ export class ProjectorService {
): Promise<void> { ): Promise<void> {
const requestData: any = {}; const requestData: any = {};
if (elements) { if (elements) {
requestData.elements = elements; requestData.elements = this.cleanupElements(projector, elements);
} }
if (preview) { if (preview) {
requestData.preview = preview; requestData.preview = preview;
@ -218,6 +222,31 @@ export class ProjectorService {
await this.http.post(`/rest/core/projector/${projector.id}/project/`, requestData); 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 * Given a projectiondefault, we want to retrieve the projector, that is assigned
* to this default. * to this default.

View File

@ -8,7 +8,7 @@ import { BaseComponent } from 'app/base.component';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { ViewProjector } from 'app/site/projector/models/view-projector'; import { ViewProjector } from 'app/site/projector/models/view-projector';
import { Size } from 'app/site/projector/size'; 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'; import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service';
/** /**

View File

@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core';
import { BaseComponent } from 'app/base.component'; import { BaseComponent } from 'app/base.component';
import { SlideManager } from 'app/slides/services/slide-manager.service'; import { SlideManager } from 'app/slides/services/slide-manager.service';
import { BaseSlideComponent } from 'app/slides/base-slide-component'; 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 { ProjectorElement } from 'app/shared/models/core/projector';
import { ViewProjector } from 'app/site/projector/models/view-projector'; import { ViewProjector } from 'app/site/projector/models/view-projector';

View File

@ -29,6 +29,19 @@ export interface IdentifiableProjectorElement extends ProjectorElement {
getIdentifiers(): (keyof IdentifiableProjectorElement)[]; 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. * Multiple elements.
*/ */
@ -128,14 +141,24 @@ export class Projector extends BaseModel<Projector> {
let removedElements: ProjectorElements; let removedElements: ProjectorElements;
let nonRemovedElements: ProjectorElements; let nonRemovedElements: ProjectorElements;
[removedElements, nonRemovedElements] = this.partitionArray(this.elements, elementOnProjector => { [removedElements, nonRemovedElements] = this.partitionArray(this.elements, elementOnProjector => {
return element.getIdentifiers().every(identifier => { return elementIdentifies(element, elementOnProjector);
return !elementOnProjector[identifier] || elementOnProjector[identifier] === element[identifier];
});
}); });
this.elements = nonRemovedElements; this.elements = nonRemovedElements;
return removedElements; 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 * 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. * will be in the fist array, all others in the second one.

View File

@ -5,15 +5,11 @@ import { ProjectorRoutingModule } from './projector-routing.module';
import { SharedModule } from '../../shared/shared.module'; import { SharedModule } from '../../shared/shared.module';
import { ProjectorListComponent } from './components/projector-list/projector-list.component'; import { ProjectorListComponent } from './components/projector-list/projector-list.component';
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.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 { CountdownListComponent } from './components/countdown-list/countdown-list.component';
import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component'; import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component';
import { CountdownControlsComponent } from './components/countdown-controls/countdown-controls.component'; import { CountdownControlsComponent } from './components/countdown-controls/countdown-controls.component';
@NgModule({ @NgModule({
providers: [ClockSlideService, ProjectorDataService, CurrentListOfSpeakersSlideService],
imports: [CommonModule, ProjectorRoutingModule, SharedModule], imports: [CommonModule, ProjectorRoutingModule, SharedModule],
declarations: [ declarations: [
ProjectorListComponent, ProjectorListComponent,

View File

@ -1,6 +1,7 @@
import { Input } from '@angular/core'; 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 { 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 * Every slide has to extends this base class. It forces the slides

View File

@ -8,7 +8,7 @@ import { DiffLinesInParagraph, DiffService, LineRange } from '../../../core/ui-s
import { LinenumberingService } from '../../../core/ui-services/linenumbering.service'; import { LinenumberingService } from '../../../core/ui-services/linenumbering.service';
import { ViewUnifiedChange } from '../../../shared/models/motions/view-unified-change'; import { ViewUnifiedChange } from '../../../shared/models/motions/view-unified-change';
import { MotionSlideObjChangeReco } from './motion-slide-obj-change-reco'; 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'; import { MotionSlideObjAmendmentParagraph } from './motion-slide-obj-amendment-paragraph';
@Component({ @Component({