diff --git a/client/src/app/core/services/projector.service.ts b/client/src/app/core/services/projector.service.ts index 682c03b1b..6926cc35a 100644 --- a/client/src/app/core/services/projector.service.ts +++ b/client/src/app/core/services/projector.service.ts @@ -39,41 +39,11 @@ export class ProjectorService extends OpenSlidesComponent { } /** - * Checks, if a given object is projected. + * Retusn the identifiable projector element from the given types of slides/elements/descriptors * - * @param obj The object in question - * @returns true, if the object is projected on one projector. + * @param obj Something related to IdentifiableProjectorElement + * @returns the identifiable projector element from obj. */ - public isProjected(obj: Projectable | ProjectorElementBuildDeskriptor): boolean { - if (isProjectable(obj)) { - return this.DS.getAll('core/projector').some(projector => { - return projector.isElementShown(obj.getSlide().getBasicProjectorElement()); - }); - } else { - return this.DS.getAll('core/projector').some(projector => { - return projector.isElementShown(obj.getBasicProjectorElement()); - }); - } - } - - /** - * Get all projectors where the object is prejected on. - * - * @param obj The object in question - * @return All projectors, where this Object is projected on - */ - public getProjectorsWhichAreProjecting(obj: Projectable | ProjectorElementBuildDeskriptor): Projector[] { - if (isProjectable(obj)) { - return this.DS.getAll('core/projector').filter(projector => { - return projector.isElementShown(obj.getSlide().getBasicProjectorElement()); - }); - } else { - return this.DS.getAll('core/projector').filter(projector => { - return projector.isElementShown(obj.getBasicProjectorElement()); - }); - } - } - private getProjectorElement( obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement ): IdentifiableProjectorElement { @@ -86,6 +56,34 @@ export class ProjectorService extends OpenSlidesComponent { } } + /** + * Checks, if a given object is projected. + * + * @param obj The object in question + * @returns true, if the object is projected on one projector. + */ + public isProjected(obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement): boolean { + const element = this.getProjectorElement(obj); + return this.DS.getAll('core/projector').some(projector => { + return projector.isElementShown(element); + }); + } + + /** + * Get all projectors where the object is prejected on. + * + * @param obj The object in question + * @return All projectors, where this Object is projected on + */ + public getProjectorsWhichAreProjecting( + obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement + ): Projector[] { + const element = this.getProjectorElement(obj); + return this.DS.getAll('core/projector').filter(projector => { + return projector.isElementShown(element); + }); + } + /** * Checks, if the object is projected on the given projector. * @@ -101,12 +99,8 @@ export class ProjectorService extends OpenSlidesComponent { } /** - * Projects the given ProjectorElement on the given projectors. - * - * TODO: this does not care about the element being stable. Some more logic must be added later. - * - * On the given projectors: Delete all non-stable elements and add the given element. - * On all other projectors: If the element (compared with name and id) is there, it will be deleted. + * Projects the given ProjectorElement on the given projectors. Removes the element + * from all non-given projectors * * @param projectors All projectors where to add the element. * @param element The element in question. @@ -121,6 +115,13 @@ export class ProjectorService extends OpenSlidesComponent { }); } + /** + * Projcets the given object on the projector. If the object is non-stable, all other non-stable + * elements will be removed and added to the history. + * + * @param projector The projector to add the object to. + * @param obj The object to project + */ public async projectOn( projector: Projector, obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement @@ -147,26 +148,40 @@ export class ProjectorService extends OpenSlidesComponent { } } + /** + * Removes the given object from the projector. Non stable elements will be added to the history. + * + * @param projector The projector + * @param obj the object to unproject + */ public async removeFrom( projector: Projector, obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement ): Promise { const element = this.getProjectorElement(obj); - if (element.stable) { - // Just remove this stable element - projector.removeElements(element); - await this.projectRequest(projector, projector.elements); - } else { - // For non-stable elements remove all current non-stable elements and add them to the history - const removedElements = projector.removeElements(element); - if (removedElements.length > 0) { - console.log(projector.elements, removedElements); + const removedElements = projector.removeElements(element); + if (removedElements.length > 0) { + if (element.stable) { + await this.projectRequest(projector, projector.elements); + } else { + // For non-stable elements: Add removed elements to the history. await this.projectRequest(projector, projector.elements, null, removedElements); } } } + /** + * Executes the request to change projector elements. + * + * Note: Just one of `appendToHistory` and `deleteLastHistoryElement` can be given. + * + * @param projector The affected projector + * @param elements (optional) Elements to set. + * @param preview (optional) preview to set + * @param appendToHistory (optional) Elements to be appended to the history + * @param deleteLastHistroyElement (optional) If given, the last history element will be removed. + */ private async projectRequest( projector: Projector, elements?: ProjectorElements, @@ -206,21 +221,41 @@ export class ProjectorService extends OpenSlidesComponent { }); } + /** + * Returns a model associated with the identifiable projector element. Throws an error, + * if the element is not mappable. + * + * @param element The projector element + * @returns the model from the projector element + */ public getModelFromProjectorElement(element: IdentifiableProjectorElement): T { if (!this.slideManager.canSlideBeMappedToModel(element.name)) { - throw new Error('THis projectorelement cannot be mapped to a model'); + throw new Error('This projector element cannot be mapped to a model'); } const identifiers = element.getIdentifiers(); - if (!identifiers.includes('name') || !identifiers.includes('name')) { + if (!identifiers.includes('name') || !identifiers.includes('id')) { throw new Error('To map this element to a model, a name and id is needed.'); } return this.DS.get(element.name, element.id); } + /** + * Projects the next slide in the queue. Moves all currently projected + * non-stable slides to the history. + * + * @param projector The projector + */ public async projectNextSlide(projector: Projector): Promise { await this.projectPreviewSlide(projector, 0); } + /** + * Projects one slide (given by the index of the preview) on the given projector. Moves + * all current projected non-stable elements to the history. + * + * @param projector The projector + * @param previewIndex The index in the `elements_preview` array. + */ public async projectPreviewSlide(projector: Projector, previewIndex: number): Promise { if (projector.elements_preview.length === 0 || previewIndex >= projector.elements_preview.length) { return; @@ -231,6 +266,11 @@ export class ProjectorService extends OpenSlidesComponent { await this.projectRequest(projector, projector.elements, projector.elements_preview, removedElements); } + /** + * Projects the last slide of the history. This slide will be removed from the history. + * + * @param projector The projector + */ public async projectPreviousSlide(projector: Projector): Promise { if (projector.elements_history.length === 0) { return; @@ -253,10 +293,21 @@ export class ProjectorService extends OpenSlidesComponent { await this.projectRequest(projector, projector.elements, projector.elements_preview, null, true); } + /** + * Saves the preview of the projector + * + * @param projector The projector to save the preview. + */ public async savePreview(projector: Projector): Promise { await this.projectRequest(projector, null, projector.elements_preview); } + /** + * Appends the given element to the preview. + * + * @param projector The projector + * @param element The element to add to the preview. + */ public async addElementToPreview(projector: Projector, element: ProjectorElement): Promise { projector.elements_preview.push(element); await this.projectRequest(projector, null, projector.elements_preview); diff --git a/client/src/app/core/services/servertime.service.ts b/client/src/app/core/services/servertime.service.ts index 44a0f54c4..dc2068900 100644 --- a/client/src/app/core/services/servertime.service.ts +++ b/client/src/app/core/services/servertime.service.ts @@ -5,6 +5,13 @@ import { HttpService } from './http.service'; import { environment } from 'environments/environment.prod'; import { BehaviorSubject, Observable } from 'rxjs'; +/** + * This service provides the timeoffset to the server and a user of this service + * can query the servertime. + * + * This service needs to be started with `startScheduler` which will update + * the servertime frequently. + */ @Injectable({ providedIn: 'root' }) @@ -13,7 +20,7 @@ export class ServertimeService extends OpenSlidesComponent { private static NORMAL_TIMEOUT = 60 * 5; /** - * In milliseconds + * The server offset in milliseconds */ private serverOffsetSubject = new BehaviorSubject(0); @@ -21,27 +28,40 @@ export class ServertimeService extends OpenSlidesComponent { super(); } + /** + * Starts the scheduler to sync with the server. + */ public startScheduler(): void { this.scheduleNextRefresh(0); } + /** + * Get an observable for the server offset. + */ public getServerOffsetObservable(): Observable { return this.serverOffsetSubject.asObservable(); } + /** + * Schedules the next sync with the server. + * + * @param seconds The timeout in seconds to the refresh. + */ private scheduleNextRefresh(seconds: number): void { setTimeout(async () => { let timeout = ServertimeService.NORMAL_TIMEOUT; try { await this.refreshServertime(); } catch (e) { - console.log(e); timeout = ServertimeService.FAILURE_TIMEOUT; } this.scheduleNextRefresh(timeout); }, 1000 * seconds); } + /** + * Queries the servertime and calculates the offset. + */ private async refreshServertime(): Promise { // servertime is the time in seconds. const servertime = await this.http.get(environment.urlPrefix + '/core/servertime/'); @@ -52,6 +72,9 @@ export class ServertimeService extends OpenSlidesComponent { this.serverOffsetSubject.next(Math.floor(Date.now() - servertime * 1000)); } + /** + * Calculate the time of the server. + */ public getServertime(): number { return Date.now() - this.serverOffsetSubject.getValue(); } diff --git a/client/src/app/shared/components/projector-button/projector-button.component.ts b/client/src/app/shared/components/projector-button/projector-button.component.ts index be596fed8..ac6c678de 100644 --- a/client/src/app/shared/components/projector-button/projector-button.component.ts +++ b/client/src/app/shared/components/projector-button/projector-button.component.ts @@ -1,10 +1,19 @@ import { Component, OnInit, Input } from '@angular/core'; -import { Projectable, ProjectorElementBuildDeskriptor } from 'app/site/base/projectable'; +import { + Projectable, + ProjectorElementBuildDeskriptor, + isProjectable, + isProjectorElementBuildDeskriptor +} from 'app/site/base/projectable'; import { ProjectionDialogService } from 'app/core/services/projection-dialog.service'; import { ProjectorService } from '../../../core/services/projector.service'; /** + * The projector button to project something on the projector. + * + * Use the input [object] to specify the object to project. It can either be + * a Projectable or a ProjectorElementBuildDeskriptor */ @Component({ selector: 'os-projector-button', @@ -12,11 +21,29 @@ import { ProjectorService } from '../../../core/services/projector.service'; styleUrls: ['./projector-button.component.scss'] }) export class ProjectorButtonComponent implements OnInit { + /** + * The object to project. + */ + private _object: Projectable | ProjectorElementBuildDeskriptor; + + public get object(): Projectable | ProjectorElementBuildDeskriptor { + return this._object; + } + @Input() - public object: Projectable | ProjectorElementBuildDeskriptor; + public set object(obj: Projectable | ProjectorElementBuildDeskriptor) { + if (isProjectable(obj) || isProjectorElementBuildDeskriptor(obj)) { + this._object = obj; + } else { + console.error( + 'Your model for the projectorbutton is not projectable and not' + 'a projectorElementBuildDescriptor!', + obj + ); + } + } /** - * The consotructor + * The constructor */ public constructor( private projectionDialogService: ProjectionDialogService, @@ -28,6 +55,11 @@ export class ProjectorButtonComponent implements OnInit { */ public ngOnInit(): void {} + /** + * Click on the projector button + * + * @param event the click event + */ public onClick(event: Event): void { event.stopPropagation(); this.projectionDialogService.openProjectDialogFor(this.object); @@ -39,10 +71,9 @@ export class ProjectorButtonComponent implements OnInit { * @returns true, if the object is projected on one projector. */ public isProjected(): boolean { - if (this.object) { - return this.projectorService.isProjected(this.object); - } else { + if (!this.object) { return false; } + return this.projectorService.isProjected(this.object); } } diff --git a/client/src/app/shared/models/core/projector.ts b/client/src/app/shared/models/core/projector.ts index 13a5f4acb..689ea8592 100644 --- a/client/src/app/shared/models/core/projector.ts +++ b/client/src/app/shared/models/core/projector.ts @@ -84,8 +84,12 @@ export class Projector extends BaseModel { * @returns all removed unstable elements */ public removeAllNonStableElements(): ProjectorElements { - const unstableElements = this.elements.filter(element => !element.stable); - this.elements = this.elements.filter(element => element.stable); + let unstableElements: ProjectorElements; + let stableElements: ProjectorElements; + + [unstableElements, stableElements] = this.partitionArray(this.elements, element => !element.stable); + + this.elements = stableElements; return unstableElements; } @@ -99,8 +103,11 @@ export class Projector extends BaseModel { } /** - * Must match everything. If a projectorelement does not have all keys - * to identify, it will be removed, if all existing keys match + * Removes and returns all projector elements, witch can be identified with the + * given element. + * + * @param element The element to remove + * @returns all removed projector elements */ public removeElements(element: IdentifiableProjectorElement): ProjectorElements { let removedElements: ProjectorElements; @@ -114,6 +121,14 @@ export class Projector extends BaseModel { return removedElements; } + /** + * 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. + * + * @param array The array to split + * @param callback To evaluate every entry + * @returns the splitted array + */ private partitionArray(array: T[], callback: (element: T) => boolean): [T[], T[]] { return array.reduce( (result, element) => { diff --git a/client/src/app/site/common/components/countdown-list/countdown-list.component.ts b/client/src/app/site/common/components/countdown-list/countdown-list.component.ts index ed4ee140e..bca61da4b 100644 --- a/client/src/app/site/common/components/countdown-list/countdown-list.component.ts +++ b/client/src/app/site/common/components/countdown-list/countdown-list.component.ts @@ -69,7 +69,7 @@ export class CountdownListComponent extends BaseViewComponent implements OnInit } /** - * Add a new Section. + * Add a new countdown. */ public onPlusButton(): void { if (!this.countdownToCreate) { diff --git a/client/src/app/site/common/components/projectormessage-list/projectormessage-list.component.ts b/client/src/app/site/common/components/projectormessage-list/projectormessage-list.component.ts index 6effe7ada..db9bb9b92 100644 --- a/client/src/app/site/common/components/projectormessage-list/projectormessage-list.component.ts +++ b/client/src/app/site/common/components/projectormessage-list/projectormessage-list.component.ts @@ -7,7 +7,7 @@ import { BaseViewComponent } from '../../../base/base-view'; import { MatSnackBar } from '@angular/material'; /** - * List view for the statute paragraphs. + * List view for the projector messages. */ @Component({ selector: 'os-projectormessage-list', @@ -22,7 +22,7 @@ export class ProjectorMessageListComponent extends BaseViewComponent implements /** * Init function. * - * Sets the title and gets/observes countdowns from DataStore + * Sets the title and gets/observes messages from DataStore */ public ngOnInit(): void { super.setTitle('Messages'); diff --git a/client/src/app/site/projector/services/current-list-of-of-speakers-slide.service.ts b/client/src/app/site/projector/services/current-list-of-of-speakers-slide.service.ts index a86229739..bcb9ebdd1 100644 --- a/client/src/app/site/projector/services/current-list-of-of-speakers-slide.service.ts +++ b/client/src/app/site/projector/services/current-list-of-of-speakers-slide.service.ts @@ -15,7 +15,7 @@ export class CurrentListOfSpeakersSlideService { return { name: overlay ? 'agenda/current-list-of-speakers-overlay' : 'agenda/current-list-of-speakers', stable: overlay, - getIdentifiers: () => ['name', 'stable'] + getIdentifiers: () => ['name'] }; } diff --git a/client/src/app/site/projector/services/projector-data.service.ts b/client/src/app/site/projector/services/projector-data.service.ts index 3268cc8bf..71a4cb535 100644 --- a/client/src/app/site/projector/services/projector-data.service.ts +++ b/client/src/app/site/projector/services/projector-data.service.ts @@ -46,7 +46,7 @@ export class ProjectorDataService { Object.keys(update).forEach(_id => { const id = parseInt(_id, 10); if ((<{ error: string }>update[id]).error !== undefined) { - console.log('TODO: Why does the server sends errors on autpupdates?'); + console.log('TODO: Why does the server sends errors on autoupdates?'); } else { if (this.currentProjectorData[id]) { this.currentProjectorData[id].next(update[id] as ProjectorData); diff --git a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.ts b/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.ts index df9b2bc0f..31b21f2d2 100644 --- a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.ts +++ b/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.ts @@ -1,9 +1,7 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { BaseSlideComponent } from 'app/slides/base-slide-component'; -import { HttpService } from 'app/core/services/http.service'; import { AgendaCurrentListOfSpeakersSlideData } from '../base/agenda-current-list-of-speakers-slide-data'; -import { SlideData } from 'app/site/projector/services/projector-data.service'; @Component({ selector: 'os-agenda-current-list-of-speakers-slide', @@ -12,23 +10,8 @@ import { SlideData } from 'app/site/projector/services/projector-data.service'; }) export class AgendaCurrentListOfSpeakersSlideComponent extends BaseSlideComponent implements OnInit { - private _data: SlideData; - - public isOverlay: boolean; - - public get data(): SlideData { - return this._data; - } - - @Input() - public set data(value: SlideData) { - this.isOverlay = !value || value.element.stable; - this._data = value; - } - - public constructor(private http: HttpService) { + public constructor() { super(); - console.log(this.http); } public ngOnInit(): void { diff --git a/client/src/app/slides/core/clock/core-clock-slide.component.ts b/client/src/app/slides/core/clock/core-clock-slide.component.ts index 88663d7d3..760be78f5 100644 --- a/client/src/app/slides/core/clock/core-clock-slide.component.ts +++ b/client/src/app/slides/core/clock/core-clock-slide.component.ts @@ -32,7 +32,7 @@ export class CoreClockSlideComponent extends BaseSlideComponent<{}> implements O const hours = '0' + time.getHours(); const minutes = '0' + time.getMinutes(); - // Will display time in 10:30:23 format + // Will display time in hh:mm format this.time = hours.slice(-2) + ':' + minutes.slice(-2); } diff --git a/client/src/app/slides/motions/motion/motions-motion-slide.component.html b/client/src/app/slides/motions/motion/motions-motion-slide.component.html index 73f494852..1b6ed9317 100644 --- a/client/src/app/slides/motions/motion/motions-motion-slide.component.html +++ b/client/src/app/slides/motions/motion/motions-motion-slide.component.html @@ -24,7 +24,8 @@
-
+
diff --git a/openslides/users/projector.py b/openslides/users/projector.py index a383d1bfb..ca0d9ee3b 100644 --- a/openslides/users/projector.py +++ b/openslides/users/projector.py @@ -23,15 +23,14 @@ def user_slide(all_data: AllData, element: Dict[str, Any]) -> Dict[str, Any]: user_id = element.get("id") if user_id is None: - return {"error": "id is required for user slide"} + raise ProjectorElementException("id is required for user slide") try: user = all_data["users/user"][user_id] except KeyError: raise ProjectorElementException(f"user with id {user_id} does not exist") - return_value = {"user": get_user_name(all_data, user["id"])} - return return_value + return {"user": get_user_name(all_data, user["id"])} def get_user_name(all_data: AllData, user_id: int) -> str: