Merge pull request #4441 from FinnStutzenstein/closReference

One global clos reference. More projector buttons for the clos view
This commit is contained in:
Emanuel Schütze 2019-03-01 13:37:28 +01:00 committed by GitHub
commit c8c55ce597
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 161 additions and 91 deletions

View File

@ -43,7 +43,7 @@ export class ProjectorRepositoryService extends BaseRepository<ViewProjector, Pr
private http: HttpService,
private translate: TranslateService
) {
super(DS, mapperService, viewModelStoreService, Projector);
super(DS, mapperService, viewModelStoreService, Projector, [Projector]);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -3,14 +3,14 @@
[ngClass]="isProjected() ? 'projector-active' : 'projector-inactive'">
<mat-icon>videocam</mat-icon>
</button>
<button type="button" *ngIf="menuItem" mat-menu-item (click)="onClick()"
[ngClass]="isProjected() ? 'projector-active' : 'projector-inactive'">
<mat-icon>videocam</mat-icon>
<span translate>Project</span>
</button>
<button type="button" *ngIf="text && !menuItem" mat-button (click)="onClick($event)"
[ngClass]="isProjected() ? 'projector-active' : 'projector-inactive'">
<mat-icon>videocam</mat-icon>
{{ text | translate }}
</button>
<button type="button" *ngIf="menuItem" mat-menu-item (click)="onClick()"
[ngClass]="isProjected() ? 'projector-active' : 'projector-inactive'">
<mat-icon>videocam</mat-icon>
{{ (text || 'Project') | translate }}
</button>
</ng-container>

View File

@ -11,20 +11,6 @@
</div>
</os-head-bar>
<!-- Select projector -->
<mat-card *ngIf="currentListOfSpeakers">
<h3 translate>
Manage the list of speakers for...
</h3>
<mat-form-field *ngIf="projectors && projectors.length > 0">
<mat-select [value]="projectors[0]" (selectionChange)="onSelectProjector($event)">
<mat-option *ngFor="let projector of projectors" [value]="projector">
{{ projector.name | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card>
<h1 class="title on-transition-fade" *ngIf="viewItem">{{ viewItem.contentObject.getTitle() }}</h1>
<mat-card class="speaker-card" *ngIf="viewItem">
@ -131,10 +117,25 @@
</mat-card>
<mat-menu #speakerMenu="matMenu">
<os-projector-button
*ngIf="viewItem && projectors && projectors.length > 1"
[object]="getClosSlide()"
[menuItem]="true"
text="Current list of speakers (as slide)"
></os-projector-button>
<os-projector-button
*ngIf="viewItem"
[object]="viewItem.listOfSpeakersSlide"
[menuItem]="true"
text="List of speakers"
></os-projector-button>
<os-projector-button
*ngIf="viewItem"
[object]="viewItem.contentObject"
[menuItem]="true"
[text]="getContentObjectProjectorButtonText()"
></os-projector-button>
<button mat-menu-item *ngIf="closedList" (click)="openSpeakerList()">

View File

@ -1,7 +1,7 @@
import { ActivatedRoute } from '@angular/router';
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { MatSnackBar, MatSelectChange } from '@angular/material';
import { MatSnackBar } from '@angular/material';
import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
@ -21,6 +21,9 @@ import { UserRepositoryService } from 'app/core/repositories/users/user-reposito
import { DurationService } from 'app/core/ui-services/duration.service';
import { CurrentAgendaItemService } from 'app/site/projector/services/current-agenda-item.service';
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
import { CurrentListOfSpeakersSlideService } from 'app/site/projector/services/current-list-of-of-speakers-slide.service';
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
/**
* The list of speakers for agenda items.
@ -92,6 +95,13 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
return this.activeSpeaker ? false : true;
}
/**
* Used to detect changes in the projector reference.
*/
private closReferenceProjectorId: number | null;
private closItemSubscription: Subscription | null;
/**
* Constructor for speaker list component. Generates the forms and subscribes
* to the {@link currentListOfSpeakers}
@ -121,7 +131,9 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
private promptService: PromptService,
private currentAgendaItemService: CurrentAgendaItemService,
private durationService: DurationService,
private userRepository: UserRepositoryService
private userRepository: UserRepositoryService,
private collectionStringMapper: CollectionStringMapperService,
private currentListOfSpeakersSlideService: CurrentListOfSpeakersSlideService
) {
super(title, translate, snackBar);
this.isCurrentListOfSpeakers();
@ -129,9 +141,10 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
if (this.currentListOfSpeakers) {
this.projectors = projectorRepo.getViewModelList();
this.showClosOfProjector(this.projectors[0]);
this.updateClosProjector();
projectorRepo.getViewModelListObservable().subscribe(newProjectors => {
this.projectors = newProjectors;
this.updateClosProjector();
});
} else {
this.getItemByUrl();
@ -172,29 +185,26 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
}
}
/**
* Executed by selecting projectors
*
* @param event Holds the selected projector
*/
public onSelectProjector(event: MatSelectChange): void {
this.showClosOfProjector(event.value);
}
/**
* Shows the current list of speakers (CLOS) of a given projector.
* Triggers after mat-select-change
*
* @param event Mat select change event, holds the projector in value
*/
private showClosOfProjector(projector: ViewProjector): void {
private updateClosProjector(): void {
if (!this.projectors.length) {
return;
}
const referenceProjector = this.projectors[0].referenceProjector;
if (!referenceProjector || referenceProjector.id === this.closReferenceProjectorId) {
return;
}
this.closReferenceProjectorId = referenceProjector.id;
if (this.projectorSubscription) {
this.projectorSubscription.unsubscribe();
this.viewItem = null;
}
this.projectorSubscription = this.currentAgendaItemService
.getAgendaItemObservable(projector)
.getAgendaItemObservable(referenceProjector)
.subscribe(item => {
if (item) {
this.setSpeakerList(item.id);
@ -202,6 +212,13 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
});
}
/**
* @returns the CLOS slide build descriptor
*/
public getClosSlide(): ProjectorElementBuildDeskriptor {
return this.currentListOfSpeakersSlideService.getSlide(false);
}
/**
* Extract the ID from the url
* Determine whether the speaker list belongs to a motion or a topic
@ -217,7 +234,11 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
* @param item the item to use as List of Speakers
*/
private setSpeakerList(id: number): void {
this.itemRepo.getViewModelObservable(id).subscribe(newAgendaItem => {
if (this.closItemSubscription) {
this.closItemSubscription.unsubscribe();
}
this.closItemSubscription = this.itemRepo.getViewModelObservable(id).subscribe(newAgendaItem => {
if (newAgendaItem) {
this.viewItem = newAgendaItem;
const allSpeakers = this.repo.createSpeakerList(newAgendaItem.item);
@ -228,6 +249,17 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
});
}
/**
* @returns the verbose name of the model of the content object from viewItem.
* If a motion is the current content object, "Motion" will be the returned value.
*/
public getContentObjectProjectorButtonText(): string {
const verboseName = this.collectionStringMapper
.getRepository(this.viewItem.item.content_object.collection)
.getVerboseName();
return verboseName;
}
/**
* Create a speaker out of an id
*

View File

@ -14,6 +14,19 @@
</div>
</os-head-bar>
<mat-card *ngIf="!projectorToCreate && projectors">
<span translate>
Reference projector for current list of speakers:
</span>
<mat-form-field>
<mat-select [disabled]="!!editId" [value]="projectors[0].reference_projector_id" (selectionChange)="onSelectReferenceProjector($event)">
<mat-option *ngFor="let projector of projectors" [value]="projector.id">
{{ projector.getTitle() | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card>
<mat-card *ngIf="projectorToCreate">
<mat-card-title translate>New Projector</mat-card-title>
<mat-card-content>
@ -74,19 +87,6 @@
</mat-hint>
</mat-form-field>
<!-- Reference projector for the current list of speakers -->
<h3 translate>Current list of speakers reference</h3>
<mat-form-field>
<mat-select formControlName="reference_projector_id" placeholder="{{ 'Reference projector' | translate }}">
<mat-option [value]="projector.id">
<span translate>self</span>
</mat-option>
<mat-option *ngFor="let refProjector of getReferenceProjectorsFor(projector)" [value]="refProjector.id">
<span>{{ refProjector.getTitle() | translate }}</span>
</mat-option>
</mat-select>
</mat-form-field>
<h3 translate>Resolution and size</h3>
<!-- Aspect ratio field -->
<mat-radio-group formControlName="aspectRatio" [name]="projector.id">

View File

@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Title } from '@angular/platform-browser';
import { MatSnackBar } from '@angular/material';
import { MatSnackBar, MatSelectChange } from '@angular/material';
import { TranslateService } from '@ngx-translate/core';
@ -105,7 +105,6 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
aspectRatio: ['', Validators.required],
width: [0, Validators.required],
clock: [true],
reference_projector_id: [],
background_color: ['', Validators.required],
header_background_color: ['', Validators.required],
header_font_color: ['', Validators.required],
@ -140,11 +139,12 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
public create(): void {
if (this.createForm.valid && this.projectorToCreate) {
this.projectorToCreate.patchValues(this.createForm.value as Projector);
// TODO: the server shouldn't want to have this data..
// TODO: the server shouldn't want to have element data..
this.projectorToCreate.patchValues({
elements: [{ name: 'core/clock', stable: true }],
elements_preview: [],
elements_history: []
elements_history: [],
reference_projector_id: this.projectors[0].reference_projector_id
});
this.repo.create(this.projectorToCreate).then(() => (this.projectorToCreate = null), this.raiseError);
}
@ -206,25 +206,11 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
this.editId = projector.id;
this.updateForm.reset();
const reference_projector_id = projector.reference_projector_id
? projector.reference_projector_id
: projector.id;
/*this.updateForm.patchValue({
name: projector.name,
aspectRatio: this.getAspectRatioKey(projector),
width: projector.width,
clock: this.clockSlideService.isProjectedOn(projector),
reference_projector_id: reference_projector_id,
background_color: projector.background_color,
show_header_footer: projector.show_header_footer,
show_title: projector.show_title,
show_logo: projector.show_logo
});*/
this.updateForm.patchValue(projector.projector);
this.updateForm.patchValue({
name: this.translate.instant(projector.name),
aspectRatio: this.getAspectRatioKey(projector),
clock: this.clockSlideService.isProjectedOn(projector),
reference_projector_id: reference_projector_id
clock: this.clockSlideService.isProjectedOn(projector)
});
}
@ -249,16 +235,6 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
return;
}
const updateProjector: Partial<Projector> = this.updateForm.value;
/*const updateProjector: Partial<Projector> = {
name: this.updateForm.value.name,
width: this.updateForm.value.width,
height: Math.round(this.updateForm.value.width / aspectRatios[this.updateForm.value.aspectRatio]),
reference_projector_id: this.updateForm.value.reference_projector_id,
background_color: this.updateForm.value.background_color,
show_header_footer: this.updateForm.value.show_header_footer,
show_title: this.updateForm.value.show_title,
show_logo: this.updateForm.value.show_logo
};*/
updateProjector.height = Math.round(
this.updateForm.value.width / aspectRatios[this.updateForm.value.aspectRatio]
);
@ -284,13 +260,13 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
}
}
/**
* Get all available reference projectors for the given projector. These
* projectors are all existing projectors exluding the given projector
*
* @returns all available reference projectors
*/
public getReferenceProjectorsFor(projector: ViewProjector): ViewProjector[] {
return this.repo.getViewModelList().filter(p => p.id !== projector.id);
public onSelectReferenceProjector(change: MatSelectChange): void {
const update: Partial<Projector> = {
reference_projector_id: change.value
};
const promises = this.projectors.map(projector => {
return this.repo.update(update, projector);
});
Promise.all(promises).then(null, this.raiseError);
}
}

View File

@ -5,11 +5,20 @@ export class ViewProjector extends BaseViewModel {
public static COLLECTIONSTRING = Projector.COLLECTIONSTRING;
private _projector: Projector;
private _referenceProjector: ViewProjector;
public get projector(): Projector {
return this._projector;
}
public get referenceProjector(): ViewProjector {
if (!this.reference_projector_id) {
return this;
} else {
return this._referenceProjector;
}
}
public get id(): number {
return this.projector.id;
}
@ -87,14 +96,19 @@ export class ViewProjector extends BaseViewModel {
*/
public getVerboseName;
public constructor(projector?: Projector) {
public constructor(projector: Projector, referenceProjector?: ViewProjector) {
super(Projector.COLLECTIONSTRING);
this._projector = projector;
this._referenceProjector = referenceProjector;
}
public getTitle = () => {
return this.name;
};
public updateDependencies(update: BaseViewModel): void {}
public updateDependencies(update: BaseViewModel): void {
if (update instanceof ViewProjector && this.reference_projector_id === update.id) {
this._referenceProjector = update;
}
}
}

View File

@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
import { ProjectorService } from 'app/core/core-services/projector.service';
import { ViewProjector } from '../models/view-projector';
import { IdentifiableProjectorElement } from 'app/shared/models/core/projector';
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
/**
* Handles the curent list of speakers slide. Manages the projection and provides
@ -29,6 +30,18 @@ export class CurrentListOfSpeakersSlideService {
};
}
/**
* @returns the slide build descriptor for the overlay or slide
*/
public getSlide(overlay: boolean): ProjectorElementBuildDeskriptor {
return {
getBasicProjectorElement: options => this.getCurrentListOfSpeakersProjectorElement(overlay),
slideOptions: [],
projectionDefaultName: 'agenda_current_list_of_speakers',
getDialogTitle: () => 'Current list of speakers'
};
}
/**
* Queries, if the slide/overlay is projected on the given projector.
*

View File

@ -0,0 +1,34 @@
# Generated by Finn Stutzenstein on 2019-03-01 10:02
from django.db import migrations
def set_reference_projector(apps, schema_editor):
"""
Sets all references to one projector. Tries to get the id from the former
config value. If there is no config or the id is invalid, the first projector
will be taken
"""
ConfigStore = apps.get_model("core", "ConfigStore")
Projector = apps.get_model("core", "Projector")
try:
config = ConfigStore.objects.get(
key="projector_currentListOfSpeakers_reference"
)
reference_id = config.value
config.delete() # cleanup. this config is not needed anymore
reference_projector = Projector.objects.get(pk=reference_id)
except (ConfigStore.DoesNotExist, Projector.DoesNotExist):
reference_projector = Projector.objects.first()
for projector in Projector.objects.all():
projector.reference_projector = reference_projector
projector.save(skip_autoupdate=True)
class Migration(migrations.Migration):
dependencies = [("core", "0019_countdown_title_2")]
operations = [migrations.RunPython(set_reference_projector)]