Enhance projector list
- The projector list now scales to give a better overview - selecting the projector for the CLOS reference is more intuitive - editing and creating projectors now works over a dialog - editing projectors is now possible from the detail page - projector tiles look overall cleaner - Editing the projector offers a preview - no changes "on the fly" - Dialog has apply button to allow saving without closing - The slider has an input fild on the right side to allow the usage of specific values
This commit is contained in:
parent
f62b506dee
commit
233961b466
@ -1,5 +1,5 @@
|
|||||||
<div id="container" #container [osResized]="resizeSubject">
|
<div id="container" #container [osResized]="resizeSubject">
|
||||||
<div id="projector" [ngStyle]="projectorStyle">
|
<div id="projector" [ngStyle]="projectorStyle">
|
||||||
<os-projector [projector]="projector"></os-projector>
|
<os-projector *ngIf="projector" [projector]="projector"></os-projector>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,6 +9,7 @@ import { OfflineService } from 'app/core/core-services/offline.service';
|
|||||||
import { ProjectorDataService, SlideData } from 'app/core/core-services/projector-data.service';
|
import { ProjectorDataService, SlideData } 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';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
|
import { Projector } from 'app/shared/models/core/projector';
|
||||||
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';
|
||||||
|
|
||||||
@ -27,40 +28,13 @@ export class ProjectorComponent extends BaseComponent implements OnDestroy {
|
|||||||
*/
|
*/
|
||||||
private projectorId: number | null = null;
|
private projectorId: number | null = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* The projector. Accessors are below.
|
|
||||||
*/
|
|
||||||
private _projector: ViewProjector;
|
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
public set projector(projector: ViewProjector) {
|
public set projector(projector: ViewProjector) {
|
||||||
this._projector = projector;
|
this._projector = projector;
|
||||||
// check, if ID changed:
|
this.setProjector(projector.projector);
|
||||||
const newId = projector ? projector.id : null;
|
|
||||||
if (this.projectorId !== newId) {
|
|
||||||
this.projectorIdChanged(this.projectorId, newId);
|
|
||||||
this.projectorId = newId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update scaling, if projector is set.
|
private _projector: ViewProjector;
|
||||||
if (projector) {
|
|
||||||
const oldSize: Size = { ...this.currentProjectorSize };
|
|
||||||
this.currentProjectorSize.height = projector.height;
|
|
||||||
this.currentProjectorSize.width = projector.width;
|
|
||||||
if (
|
|
||||||
oldSize.height !== this.currentProjectorSize.height ||
|
|
||||||
oldSize.width !== this.currentProjectorSize.width
|
|
||||||
) {
|
|
||||||
this.updateScaling();
|
|
||||||
}
|
|
||||||
this.css.projector.color = projector.color;
|
|
||||||
this.css.projector.backgroundColor = projector.background_color;
|
|
||||||
this.css.projector.H1Color = this.projector.header_h1_color;
|
|
||||||
this.css.headerFooter.color = projector.header_font_color;
|
|
||||||
this.css.headerFooter.backgroundColor = projector.header_background_color;
|
|
||||||
this.updateCSS();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public get projector(): ViewProjector {
|
public get projector(): ViewProjector {
|
||||||
return this._projector;
|
return this._projector;
|
||||||
@ -240,6 +214,39 @@ export class ProjectorComponent extends BaseComponent implements OnDestroy {
|
|||||||
this.offlineSubscription = this.offlineService.isOffline().subscribe(isOffline => (this.isOffline = isOffline));
|
this.offlineSubscription = this.offlineService.isOffline().subscribe(isOffline => (this.isOffline = isOffline));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular routine to set a projector
|
||||||
|
*
|
||||||
|
* @param projector
|
||||||
|
*/
|
||||||
|
public setProjector(projector: Projector): void {
|
||||||
|
// check, if ID changed:
|
||||||
|
const newId = projector ? projector.id : null;
|
||||||
|
if (this.projectorId !== newId) {
|
||||||
|
this.projectorIdChanged(this.projectorId, newId);
|
||||||
|
this.projectorId = newId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update scaling, if projector is set.
|
||||||
|
if (projector) {
|
||||||
|
const oldSize: Size = { ...this.currentProjectorSize };
|
||||||
|
this.currentProjectorSize.height = projector.height;
|
||||||
|
this.currentProjectorSize.width = projector.width;
|
||||||
|
if (
|
||||||
|
oldSize.height !== this.currentProjectorSize.height ||
|
||||||
|
oldSize.width !== this.currentProjectorSize.width
|
||||||
|
) {
|
||||||
|
this.updateScaling();
|
||||||
|
}
|
||||||
|
this.css.projector.color = projector.color;
|
||||||
|
this.css.projector.backgroundColor = projector.background_color;
|
||||||
|
this.css.projector.H1Color = projector.header_h1_color;
|
||||||
|
this.css.headerFooter.color = projector.header_font_color;
|
||||||
|
this.css.headerFooter.backgroundColor = projector.header_background_color;
|
||||||
|
this.updateCSS();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scales the projector to the right format.
|
* Scales the projector to the right format.
|
||||||
*/
|
*/
|
||||||
|
@ -3,15 +3,15 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|||||||
|
|
||||||
import { E2EImportsModule } from 'e2e-imports.module';
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
import { MessageData, MessageDialogComponent } from './message-dialog.component';
|
import { MessageDialogComponent } from './message-dialog.component';
|
||||||
|
|
||||||
describe('MessageDialogComponent', () => {
|
describe('MessageDialogComponent', () => {
|
||||||
let component: MessageDialogComponent;
|
let component: MessageDialogComponent;
|
||||||
let fixture: ComponentFixture<MessageDialogComponent>;
|
let fixture: ComponentFixture<MessageDialogComponent>;
|
||||||
|
|
||||||
const dialogData: MessageData = {
|
// const dialogData: MessageData = {
|
||||||
text: ''
|
// text: ''
|
||||||
};
|
// };
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@ -21,7 +21,7 @@ describe('MessageDialogComponent', () => {
|
|||||||
{ provide: MatDialogRef, useValue: {} },
|
{ provide: MatDialogRef, useValue: {} },
|
||||||
{
|
{
|
||||||
provide: MAT_DIALOG_DATA,
|
provide: MAT_DIALOG_DATA,
|
||||||
useValue: dialogData
|
useValue: {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<os-head-bar [nav]="false" [goBack]="true">
|
<os-head-bar [nav]="false" [hasMainButton]="true" mainButtonIcon="edit" [goBack]="true" (mainEvent)="editProjector()">
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div class="title-slot">
|
<div class="title-slot">
|
||||||
<h2>{{ projector?.name | translate }}</h2>
|
<h2>{{ projector?.name | translate }}</h2>
|
||||||
|
@ -20,7 +20,7 @@ import { SizeObject } from 'app/shared/components/tile/tile.component';
|
|||||||
import { Countdown } from 'app/shared/models/core/countdown';
|
import { Countdown } from 'app/shared/models/core/countdown';
|
||||||
import { ProjectorElement } from 'app/shared/models/core/projector';
|
import { ProjectorElement } from 'app/shared/models/core/projector';
|
||||||
import { ProjectorMessage } from 'app/shared/models/core/projector-message';
|
import { ProjectorMessage } from 'app/shared/models/core/projector-message';
|
||||||
import { infoDialogSettings, mediumDialogSettings } from 'app/shared/utils/dialog-settings';
|
import { infoDialogSettings, largeDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
import { Projectable } from 'app/site/base/projectable';
|
import { Projectable } from 'app/site/base/projectable';
|
||||||
import { ViewCountdown } from 'app/site/projector/models/view-countdown';
|
import { ViewCountdown } from 'app/site/projector/models/view-countdown';
|
||||||
@ -30,6 +30,7 @@ import { CountdownData, CountdownDialogComponent } from '../countdown-dialog/cou
|
|||||||
import { CurrentListOfSpeakersSlideService } from '../../services/current-list-of-of-speakers-slide.service';
|
import { CurrentListOfSpeakersSlideService } from '../../services/current-list-of-of-speakers-slide.service';
|
||||||
import { CurrentSpeakerChyronSlideService } from '../../services/current-speaker-chyron-slide.service';
|
import { CurrentSpeakerChyronSlideService } from '../../services/current-speaker-chyron-slide.service';
|
||||||
import { MessageData, MessageDialogComponent } from '../message-dialog/message-dialog.component';
|
import { MessageData, MessageDialogComponent } from '../message-dialog/message-dialog.component';
|
||||||
|
import { ProjectorEditDialogComponent } from '../projector-edit-dialog/projector-edit-dialog.component';
|
||||||
import { ViewProjector } from '../../models/view-projector';
|
import { ViewProjector } from '../../models/view-projector';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,18 +107,34 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
|||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.route.params.subscribe(params => {
|
this.route.params.subscribe(params => {
|
||||||
const projectorId = parseInt(params.id, 10) || 1;
|
const projectorId = parseInt(params.id, 10) || 1;
|
||||||
|
|
||||||
|
this.subscriptions.push(
|
||||||
this.repo.getViewModelObservable(projectorId).subscribe(projector => {
|
this.repo.getViewModelObservable(projectorId).subscribe(projector => {
|
||||||
if (projector) {
|
if (projector) {
|
||||||
const title = projector.name;
|
const title = projector.name;
|
||||||
super.setTitle(title);
|
super.setTitle(title);
|
||||||
this.projector = projector;
|
this.projector = projector;
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.subscriptions.push(timer(0, 500).subscribe(() => this.cd.detectChanges()));
|
this.subscriptions.push(timer(0, 500).subscribe(() => this.cd.detectChanges()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public editProjector(): void {
|
||||||
|
const dialogRef = this.dialog.open(ProjectorEditDialogComponent, {
|
||||||
|
data: this.projector,
|
||||||
|
...largeDialogSettings
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(event => {
|
||||||
|
if (event) {
|
||||||
|
this.cd.detectChanges();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the scroll
|
* Change the scroll
|
||||||
*
|
*
|
||||||
@ -252,7 +269,7 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
|||||||
|
|
||||||
const dialogRef = this.dialog.open(MessageDialogComponent, {
|
const dialogRef = this.dialog.open(MessageDialogComponent, {
|
||||||
data: messageData,
|
data: messageData,
|
||||||
...mediumDialogSettings
|
...largeDialogSettings
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
@ -0,0 +1,177 @@
|
|||||||
|
<h1 mat-dialog-title>
|
||||||
|
<span translate>Edit Projector</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="settings-grid">
|
||||||
|
<form [formGroup]="updateForm" (ngSubmit)="onSubmitProjector()">
|
||||||
|
<div mat-dialog-content *ngIf="projector">
|
||||||
|
<!-- Name field -->
|
||||||
|
<mat-form-field>
|
||||||
|
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
|
||||||
|
<mat-hint *ngIf="!updateForm.controls.name.valid">
|
||||||
|
<span translate>Required</span>
|
||||||
|
</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<h3 translate>Resolution and size</h3>
|
||||||
|
<!-- Aspect ratio field -->
|
||||||
|
<mat-radio-group formControlName="aspectRatio" name="aspectRatio">
|
||||||
|
<mat-radio-button
|
||||||
|
*ngFor="let ratio of aspectRatiosKeys"
|
||||||
|
[value]="ratio"
|
||||||
|
(change)="aspectRatioChanged($event)"
|
||||||
|
>
|
||||||
|
{{ ratio }}
|
||||||
|
</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<div class="spacer-top-20 grid-form">
|
||||||
|
<mat-slider
|
||||||
|
class="grid-start"
|
||||||
|
formControlName="width"
|
||||||
|
[thumbLabel]="true"
|
||||||
|
[min]="getMinWidth()"
|
||||||
|
[max]="maxResolution"
|
||||||
|
[step]="resolutionChangeStep"
|
||||||
|
[value]="updateForm.value.width"
|
||||||
|
></mat-slider>
|
||||||
|
<div class="grid-end">
|
||||||
|
<mat-form-field>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
type="number"
|
||||||
|
formControlName="width"
|
||||||
|
[min]="getMinWidth()"
|
||||||
|
[max]="maxResolution"
|
||||||
|
[step]="resolutionChangeStep"
|
||||||
|
[value]="updateForm.value.width"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- checkboxes -->
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<mat-checkbox formControlName="show_header_footer">
|
||||||
|
<span translate>Show header and footer</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-checkbox formControlName="show_title">
|
||||||
|
<span translate>Show title</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-checkbox formControlName="show_logo">
|
||||||
|
<span translate>Show logo</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-checkbox formControlName="clock">
|
||||||
|
<span translate>Show clock</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- projection defaults -->
|
||||||
|
<h3 translate>Projection defaults</h3>
|
||||||
|
<mat-select
|
||||||
|
formControlName="projectiondefaults_id"
|
||||||
|
placeholder="{{ 'Projection defaults' | translate }}"
|
||||||
|
[multiple]="true"
|
||||||
|
>
|
||||||
|
<mat-option *ngFor="let pd of projectionDefaults" [value]="pd.id">
|
||||||
|
{{ pd.getTitle() | translate }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
|
||||||
|
<!-- colors -->
|
||||||
|
<div class="spacer-top-10">
|
||||||
|
<!-- Template to streamline all the color forms -->
|
||||||
|
<ng-template #colorFormField let-title="title" let-form="form">
|
||||||
|
<div class="grid-form">
|
||||||
|
<div class="grid-start">
|
||||||
|
<mat-form-field class="color-picker-form">
|
||||||
|
<span>{{ title | translate }}</span>
|
||||||
|
<input matInput [formControlName]="form" type="color" />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="grid-end">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
mat-icon-button
|
||||||
|
matTooltip="{{ 'Reset' | translate }}"
|
||||||
|
(click)="resetField(form)"
|
||||||
|
>
|
||||||
|
<mat-icon>replay</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template
|
||||||
|
[ngTemplateOutlet]="colorFormField"
|
||||||
|
[ngTemplateOutletContext]="{ title: 'Foreground color', form: 'color' }"
|
||||||
|
>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template
|
||||||
|
[ngTemplateOutlet]="colorFormField"
|
||||||
|
[ngTemplateOutletContext]="{ title: 'Background color', form: 'background_color' }"
|
||||||
|
>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template
|
||||||
|
[ngTemplateOutlet]="colorFormField"
|
||||||
|
[ngTemplateOutletContext]="{ title: 'Header background color', form: 'header_background_color' }"
|
||||||
|
>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template
|
||||||
|
[ngTemplateOutlet]="colorFormField"
|
||||||
|
[ngTemplateOutletContext]="{ title: 'Header font color', form: 'header_font_color' }"
|
||||||
|
>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template
|
||||||
|
[ngTemplateOutlet]="colorFormField"
|
||||||
|
[ngTemplateOutletContext]="{ title: 'Headline color', form: 'header_h1_color' }"
|
||||||
|
>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template
|
||||||
|
[ngTemplateOutlet]="colorFormField"
|
||||||
|
[ngTemplateOutletContext]="{ title: 'Chyron background color', form: 'chyron_background_color' }"
|
||||||
|
>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template
|
||||||
|
[ngTemplateOutlet]="colorFormField"
|
||||||
|
[ngTemplateOutletContext]="{ title: 'Chyron font color', form: 'chyron_font_color' }"
|
||||||
|
>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Actions -->
|
||||||
|
<div mat-dialog-actions>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
<button type="submit" mat-button color="primary">
|
||||||
|
<span translate>Save</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" mat-button [mat-dialog-close]="null">
|
||||||
|
<span translate>Cancel</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" mat-button (click)="applyChanges()">
|
||||||
|
<span translate>Apply</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div>
|
||||||
|
<h3 translate>Preview</h3>
|
||||||
|
<div>
|
||||||
|
<os-projector #preview *ngIf="previewProjector" [projector]="previewProjector"></os-projector>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,56 @@
|
|||||||
|
@import '~assets/styles/variables.scss';
|
||||||
|
|
||||||
|
form {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 10px;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
grid-template-columns: 40% 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-form {
|
||||||
|
width: 100%;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 50px;
|
||||||
|
|
||||||
|
.grid-start {
|
||||||
|
grid-column-start: 1;
|
||||||
|
grid-column-end: 1;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-end {
|
||||||
|
grid-column-start: 2;
|
||||||
|
grid-column-end: 2;
|
||||||
|
margin: auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-picker-form {
|
||||||
|
.mat-form-field-wrapper {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field-underline {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-markup {
|
||||||
|
/* Do not let the a tag ruin the projector */
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-dialog-actions {
|
||||||
|
display: block;
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
||||||
|
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
|
import { ProjectorEditDialogComponent } from './projector-edit-dialog.component';
|
||||||
|
|
||||||
|
describe('ProjectorEditDialogComponent', () => {
|
||||||
|
let component: ProjectorEditDialogComponent;
|
||||||
|
let fixture: ComponentFixture<ProjectorEditDialogComponent>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A view model has to be injected here, hence it's currently not possbile (anymore)
|
||||||
|
* to mock the creation of view models
|
||||||
|
*/
|
||||||
|
const dialogData = null;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ProjectorEditDialogComponent],
|
||||||
|
imports: [E2EImportsModule],
|
||||||
|
providers: [
|
||||||
|
{ provide: MatDialogRef, useValue: {} },
|
||||||
|
{
|
||||||
|
provide: MAT_DIALOG_DATA,
|
||||||
|
useValue: dialogData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProjectorEditDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,258 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
Inject,
|
||||||
|
OnInit,
|
||||||
|
ViewChild,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef, MatRadioChange, MatSnackBar } from '@angular/material';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { auditTime } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { ProjectionDefaultRepositoryService } from 'app/core/repositories/projector/projection-default-repository.service';
|
||||||
|
import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service';
|
||||||
|
import { ProjectorComponent } from 'app/shared/components/projector/projector.component';
|
||||||
|
import { Projector } from 'app/shared/models/core/projector';
|
||||||
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
|
import { ClockSlideService } from '../../services/clock-slide.service';
|
||||||
|
import { ViewProjectionDefault } from '../../models/view-projection-default';
|
||||||
|
import { ViewProjector } from '../../models/view-projector';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All supported aspect rations for projectors.
|
||||||
|
*/
|
||||||
|
const aspectRatios: { [ratio: string]: number } = {
|
||||||
|
'4:3': 4 / 3,
|
||||||
|
'16:9': 16 / 9,
|
||||||
|
'16:10': 16 / 10,
|
||||||
|
'30:9': 30 / 9
|
||||||
|
};
|
||||||
|
|
||||||
|
const aspectRatio_30_9_MinWidth = 1150;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog to edit the given projector
|
||||||
|
* Shows a preview
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'os-projector-edit-dialog',
|
||||||
|
templateUrl: './projector-edit-dialog.component.html',
|
||||||
|
styleUrls: ['./projector-edit-dialog.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ProjectorEditDialogComponent extends BaseViewComponent implements OnInit {
|
||||||
|
/**
|
||||||
|
* import the projector as view child, to determine when to update
|
||||||
|
* the preview.
|
||||||
|
*/
|
||||||
|
@ViewChild('preview', { static: false })
|
||||||
|
public preview: ProjectorComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The update form. Will be refreahed for each projector. Just one update
|
||||||
|
* form can be shown per time.
|
||||||
|
*/
|
||||||
|
public updateForm: FormGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All aspect ratio keys/strings for the UI.
|
||||||
|
*/
|
||||||
|
public aspectRatiosKeys: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All ProjectionDefaults to select from.
|
||||||
|
*/
|
||||||
|
public projectionDefaults: ViewProjectionDefault[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* show a preview of the changes
|
||||||
|
*/
|
||||||
|
public previewProjector: Projector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* define the maximum resolution
|
||||||
|
*/
|
||||||
|
public maxResolution = 2000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the step of resolution changes
|
||||||
|
*/
|
||||||
|
public resolutionChangeStep = 10;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
title: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
|
formBuilder: FormBuilder,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public projector: ViewProjector,
|
||||||
|
private dialogRef: MatDialogRef<ProjectorEditDialogComponent>,
|
||||||
|
private repo: ProjectorRepositoryService,
|
||||||
|
private projectionDefaultRepo: ProjectionDefaultRepositoryService,
|
||||||
|
private clockSlideService: ClockSlideService,
|
||||||
|
private cd: ChangeDetectorRef
|
||||||
|
) {
|
||||||
|
super(title, translate, matSnackBar);
|
||||||
|
this.aspectRatiosKeys = Object.keys(aspectRatios);
|
||||||
|
|
||||||
|
if (projector) {
|
||||||
|
this.previewProjector = new Projector(projector.getModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateForm = formBuilder.group({
|
||||||
|
name: ['', Validators.required],
|
||||||
|
aspectRatio: ['', Validators.required],
|
||||||
|
width: [0, Validators.required],
|
||||||
|
projectiondefaults_id: [[]],
|
||||||
|
clock: [true],
|
||||||
|
color: ['', Validators.required],
|
||||||
|
background_color: ['', Validators.required],
|
||||||
|
header_background_color: ['', Validators.required],
|
||||||
|
header_font_color: ['', Validators.required],
|
||||||
|
header_h1_color: ['', Validators.required],
|
||||||
|
chyron_background_color: ['', Validators.required],
|
||||||
|
chyron_font_color: ['', Validators.required],
|
||||||
|
show_header_footer: [],
|
||||||
|
show_title: [],
|
||||||
|
show_logo: []
|
||||||
|
});
|
||||||
|
|
||||||
|
// react to form changes
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.updateForm.valueChanges.pipe(auditTime(100)).subscribe(() => {
|
||||||
|
this.onChangeForm();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watches all projection defaults
|
||||||
|
*/
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.projectionDefaults = this.projectionDefaultRepo.getViewModelList();
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.projectionDefaultRepo.getViewModelListObservable().subscribe(pds => (this.projectionDefaults = pds))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.projector) {
|
||||||
|
this.updateForm.patchValue(this.projector.projector);
|
||||||
|
this.updateForm.patchValue({
|
||||||
|
name: this.translate.instant(this.projector.name),
|
||||||
|
aspectRatio: this.getAspectRatioKey(),
|
||||||
|
clock: this.clockSlideService.isProjectedOn(this.projector)
|
||||||
|
});
|
||||||
|
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.repo.getViewModelObservable(this.projector.id).subscribe(update => {
|
||||||
|
// patches the projector with updated values
|
||||||
|
const projectorPatch = {};
|
||||||
|
Object.keys(this.updateForm.controls).forEach(ctrl => {
|
||||||
|
if (update[ctrl]) {
|
||||||
|
projectorPatch[ctrl] = update[ctrl];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.updateForm.patchValue(projectorPatch);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply changes and close the dialog
|
||||||
|
*/
|
||||||
|
public async onSubmitProjector(): Promise<void> {
|
||||||
|
await this.applyChanges();
|
||||||
|
this.dialogRef.close(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the current changes on the projector
|
||||||
|
*/
|
||||||
|
public async applyChanges(): Promise<void> {
|
||||||
|
const updateProjector: Partial<Projector> = this.updateForm.value;
|
||||||
|
updateProjector.height = this.calcHeight(this.updateForm.value.width, this.updateForm.value.aspectRatio);
|
||||||
|
try {
|
||||||
|
await this.clockSlideService.setProjectedOn(this.projector, this.updateForm.value.clock);
|
||||||
|
await this.repo.update(updateProjector, this.projector);
|
||||||
|
} catch (e) {
|
||||||
|
this.raiseError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to form changes to update the preview
|
||||||
|
* @param previewUpdate
|
||||||
|
*/
|
||||||
|
public onChangeForm(): void {
|
||||||
|
if (this.previewProjector && this.projector) {
|
||||||
|
Object.assign(this.previewProjector, this.updateForm.value);
|
||||||
|
this.previewProjector.height = this.calcHeight(
|
||||||
|
this.updateForm.value.width,
|
||||||
|
this.updateForm.value.aspectRatio
|
||||||
|
);
|
||||||
|
this.preview.setProjector(this.previewProjector);
|
||||||
|
this.cd.markForCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to calc height
|
||||||
|
* @param width
|
||||||
|
* @param aspectRatio
|
||||||
|
*/
|
||||||
|
private calcHeight(width: number, aspectRatio: string): number {
|
||||||
|
return Math.round(width / aspectRatios[aspectRatio]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the given form field to the given default.
|
||||||
|
*/
|
||||||
|
public resetField(field: string): void {
|
||||||
|
const patchValue = {};
|
||||||
|
patchValue[field] = this.projector[field];
|
||||||
|
this.updateForm.patchValue(patchValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public aspectRatioChanged(event: MatRadioChange): void {
|
||||||
|
let width: number;
|
||||||
|
if (event.value === '30:9' && this.updateForm.value.width < aspectRatio_30_9_MinWidth) {
|
||||||
|
width = aspectRatio_30_9_MinWidth;
|
||||||
|
} else {
|
||||||
|
width = this.updateForm.value.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the aspect ratio of the given projector.
|
||||||
|
* If no matching ratio is found, the first ratio is returned.
|
||||||
|
*
|
||||||
|
* @param projector The projector to check
|
||||||
|
* @returns the found ratio key.
|
||||||
|
*/
|
||||||
|
public getAspectRatioKey(): string {
|
||||||
|
const ratio = this.projector.width / this.projector.height;
|
||||||
|
const RATIO_ENVIRONMENT = 0.05;
|
||||||
|
const foundRatioKey = Object.keys(aspectRatios).find(key => {
|
||||||
|
const value = aspectRatios[key];
|
||||||
|
return value >= ratio - RATIO_ENVIRONMENT && value <= ratio + RATIO_ENVIRONMENT;
|
||||||
|
});
|
||||||
|
if (!foundRatioKey) {
|
||||||
|
return Object.keys(aspectRatios)[0];
|
||||||
|
} else {
|
||||||
|
return foundRatioKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMinWidth(): number {
|
||||||
|
if (this.updateForm.value.aspectRatio === '30:9') {
|
||||||
|
return aspectRatio_30_9_MinWidth;
|
||||||
|
} else {
|
||||||
|
return 800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,20 @@
|
|||||||
<os-meta-text-block showActionRow="false" *ngIf="projector" [disableExpandControl]="true">
|
<os-meta-text-block class="projector-tile" showActionRow="false" *ngIf="projector" [disableExpandControl]="true">
|
||||||
<ng-container class="meta-text-block-title">
|
<ng-container class="meta-text-block-title">
|
||||||
{{ projector.getTitle() | translate }}
|
{{ projector.getTitle() | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container class="meta-text-block-action-row" *ngIf="canManage">
|
<ng-container class="meta-text-block-action-row" *osPerms="'core.can_manage_projector'">
|
||||||
<button mat-icon-button *ngIf="!isEditing" (click)="onEditButton()">
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
(click)="onSetAsClosRef()"
|
||||||
|
matTooltip="{{ 'Sets this projector as the reference for the current list of speakers' | translate }}"
|
||||||
|
>
|
||||||
|
<mat-icon *ngIf="this.projector.isReferenceProjector">star</mat-icon>
|
||||||
|
<mat-icon *ngIf="!this.projector.isReferenceProjector">star_border</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button (click)="editProjector()" matTooltip="{{ 'Edit this projector' | translate }}">
|
||||||
<mat-icon>edit</mat-icon>
|
<mat-icon>edit</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button *ngIf="isEditing" (click)="onCancelButton()">
|
<button mat-icon-button color="warn" (click)="onDeleteButton()" matTooltip="{{ 'Deletes this projector' | translate }}">
|
||||||
<mat-icon>close</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-icon-button *ngIf="isEditing" (click)="onSaveButton()">
|
|
||||||
<mat-icon>save</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-icon-button color="warn" (click)="onDeleteButton()">
|
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -22,221 +24,5 @@
|
|||||||
<os-projector [projector]="projector"></os-projector>
|
<os-projector [projector]="projector"></os-projector>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<ng-container *ngIf="isEditing">
|
|
||||||
<form [formGroup]="updateForm" (keydown)="keyDownFunction($event, projector)">
|
|
||||||
<!-- Name field -->
|
|
||||||
<mat-form-field>
|
|
||||||
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
|
|
||||||
<mat-hint *ngIf="!updateForm.controls.name.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<h3 translate>Resolution and size</h3>
|
|
||||||
<!-- Aspect ratio field -->
|
|
||||||
<mat-radio-group formControlName="aspectRatio" [name]="projector.id">
|
|
||||||
<mat-radio-button
|
|
||||||
*ngFor="let ratio of aspectRatiosKeys"
|
|
||||||
[value]="ratio"
|
|
||||||
(change)="aspectRatioChanged($event)"
|
|
||||||
>
|
|
||||||
{{ ratio }}
|
|
||||||
</mat-radio-button>
|
|
||||||
</mat-radio-group>
|
|
||||||
<div class="spacer-top-20">
|
|
||||||
<mat-slider
|
|
||||||
[thumbLabel]="true"
|
|
||||||
[min]="getMinWidth()"
|
|
||||||
max="2000"
|
|
||||||
step="10"
|
|
||||||
value="{{ updateForm.value.width }}"
|
|
||||||
(change)="widthSliderValueChanged($event)"
|
|
||||||
></mat-slider>
|
|
||||||
{{ updateForm.value.width }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- projection defaults -->
|
|
||||||
<h3 translate>Projection defaults</h3>
|
|
||||||
<mat-select
|
|
||||||
formControlName="projectiondefaults_id"
|
|
||||||
placeholder="{{ 'Projection defaults' | translate }}"
|
|
||||||
[multiple]="true"
|
|
||||||
>
|
|
||||||
<mat-option *ngFor="let pd of projectionDefaults" [value]="pd.id">
|
|
||||||
{{ pd.getTitle() | translate }}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
|
|
||||||
<!-- colors -->
|
|
||||||
<div class="color-field-wrapper">
|
|
||||||
<div class="form">
|
|
||||||
<mat-form-field>
|
|
||||||
<span translate>Foreground color</span>
|
|
||||||
<input matInput formControlName="color" type="color" />
|
|
||||||
<mat-hint *ngIf="!updateForm.controls.color.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<div class="reset-button">
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
matTooltip="{{ 'Reset' | translate }}"
|
|
||||||
(click)="resetField('color', '#000000')"
|
|
||||||
>
|
|
||||||
<mat-icon>replay</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="color-field-wrapper">
|
|
||||||
<div class="form">
|
|
||||||
<mat-form-field>
|
|
||||||
<span translate>Background color</span>
|
|
||||||
<input matInput formControlName="background_color" type="color" />
|
|
||||||
<mat-hint *ngIf="!updateForm.controls.background_color.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<div class="reset-button">
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
matTooltip="{{ 'Reset' | translate }}"
|
|
||||||
(click)="resetField('background_color', '#ffffff')"
|
|
||||||
>
|
|
||||||
<mat-icon>replay</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="color-field-wrapper">
|
|
||||||
<div class="form">
|
|
||||||
<mat-form-field>
|
|
||||||
<span translate>Header background color</span>
|
|
||||||
<input matInput formControlName="header_background_color" type="color" />
|
|
||||||
<mat-hint *ngIf="!updateForm.controls.header_background_color.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<div class="reset-button">
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
matTooltip="{{ 'Reset' | translate }}"
|
|
||||||
(click)="resetField('header_background_color', '#317796')"
|
|
||||||
>
|
|
||||||
<mat-icon>replay</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="color-field-wrapper">
|
|
||||||
<div class="form">
|
|
||||||
<mat-form-field>
|
|
||||||
<span translate>Header font color</span>
|
|
||||||
<input matInput formControlName="header_font_color" type="color" />
|
|
||||||
<mat-hint *ngIf="!updateForm.controls.header_font_color.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<div class="reset-button">
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
matTooltip="{{ 'Reset' | translate }}"
|
|
||||||
(click)="resetField('header_font_color', '#f5f5f5')"
|
|
||||||
>
|
|
||||||
<mat-icon>replay</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="color-field-wrapper">
|
|
||||||
<div class="form">
|
|
||||||
<mat-form-field>
|
|
||||||
<span translate>Headline color</span>
|
|
||||||
<input matInput formControlName="header_h1_color" type="color" />
|
|
||||||
<mat-hint *ngIf="!updateForm.controls.header_h1_color.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<div class="reset-button">
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
matTooltip="{{ 'Reset' | translate }}"
|
|
||||||
(click)="resetField('header_h1_color', '#317796')"
|
|
||||||
>
|
|
||||||
<mat-icon>replay</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="color-field-wrapper">
|
|
||||||
<div class="form">
|
|
||||||
<mat-form-field>
|
|
||||||
<span translate>Chyron background color</span>
|
|
||||||
<input matInput formControlName="chyron_background_color" type="color" />
|
|
||||||
<mat-hint *ngIf="!updateForm.controls.chyron_background_color.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<div class="reset-button">
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
matTooltip="{{ 'Reset' | translate }}"
|
|
||||||
(click)="resetField('chyron_background_color', '#317796')"
|
|
||||||
>
|
|
||||||
<mat-icon>replay</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="color-field-wrapper">
|
|
||||||
<div class="form">
|
|
||||||
<mat-form-field>
|
|
||||||
<span translate>Chyron font color</span>
|
|
||||||
<input matInput formControlName="chyron_font_color" type="color" />
|
|
||||||
<mat-hint *ngIf="!updateForm.controls.chyron_font_color.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<div class="reset-button">
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
matTooltip="{{ 'Reset' | translate }}"
|
|
||||||
(click)="resetField('chyron_font_color', '#ffffff')"
|
|
||||||
>
|
|
||||||
<mat-icon>replay</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- checkboxes -->
|
|
||||||
<div>
|
|
||||||
<mat-checkbox formControlName="show_header_footer">
|
|
||||||
<span translate>Show header and footer</span>
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<mat-checkbox formControlName="show_title">
|
|
||||||
<span translate>Show title</span>
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<mat-checkbox formControlName="show_logo">
|
|
||||||
<span translate>Show logo</span>
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<mat-checkbox formControlName="clock">
|
|
||||||
<span translate>Show clock</span>
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</ng-container>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</os-meta-text-block>
|
</os-meta-text-block>
|
||||||
|
@ -1,31 +1,21 @@
|
|||||||
|
.projector-tile {
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.meta-text-block {
|
||||||
|
margin: 0 !important;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-markup > div {
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
.projector {
|
.projector {
|
||||||
width: 320px;
|
|
||||||
color: black;
|
color: black;
|
||||||
border: 1px solid lightgrey;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-markup {
|
|
||||||
/* Do not let the a tag ruin the projector */
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-field-wrapper {
|
|
||||||
width: 100%;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: auto 30px;
|
|
||||||
|
|
||||||
.form {
|
|
||||||
grid-column-start: 1;
|
|
||||||
grid-column-end: 1;
|
|
||||||
}
|
|
||||||
.reset-button {
|
|
||||||
grid-column-start: 2;
|
|
||||||
grid-column-end: 2;
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,71 +1,33 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { MatDialog } from '@angular/material';
|
||||||
import { MatRadioChange } from '@angular/material';
|
|
||||||
import { MatSliderChange } from '@angular/material/slider';
|
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
|
||||||
import { ProjectionDefaultRepositoryService } from 'app/core/repositories/projector/projection-default-repository.service';
|
|
||||||
import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service';
|
import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { Projector } from 'app/shared/models/core/projector';
|
import { largeDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
import { ClockSlideService } from '../../services/clock-slide.service';
|
import { ProjectorEditDialogComponent } from '../projector-edit-dialog/projector-edit-dialog.component';
|
||||||
import { ViewProjectionDefault } from '../../models/view-projection-default';
|
|
||||||
import { ViewProjector } from '../../models/view-projector';
|
import { ViewProjector } from '../../models/view-projector';
|
||||||
|
|
||||||
/**
|
|
||||||
* All supported aspect rations for projectors.
|
|
||||||
*/
|
|
||||||
const aspectRatios: { [ratio: string]: number } = {
|
|
||||||
'4:3': 4 / 3,
|
|
||||||
'16:9': 16 / 9,
|
|
||||||
'16:10': 16 / 10,
|
|
||||||
'30:9': 30 / 9
|
|
||||||
};
|
|
||||||
|
|
||||||
const aspectRatio_30_9_MinWidth = 1150;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List for all projectors.
|
* List for all projectors.
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'os-projector-list-entry',
|
selector: 'os-projector-list-entry',
|
||||||
templateUrl: './projector-list-entry.component.html',
|
templateUrl: './projector-list-entry.component.html',
|
||||||
styleUrls: ['./projector-list-entry.component.scss']
|
styleUrls: ['./projector-list-entry.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class ProjectorListEntryComponent extends BaseViewComponent implements OnInit {
|
export class ProjectorListEntryComponent extends BaseViewComponent implements OnInit {
|
||||||
/**
|
|
||||||
* The update form. Will be refreahed for each projector. Just one update
|
|
||||||
* form can be shown per time.
|
|
||||||
*/
|
|
||||||
public updateForm: FormGroup;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves, if this projector currently is edited.
|
|
||||||
*/
|
|
||||||
public isEditing = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All ProjectionDefaults to select from.
|
|
||||||
*/
|
|
||||||
public projectionDefaults: ViewProjectionDefault[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All aspect ratio keys/strings for the UI.
|
|
||||||
*/
|
|
||||||
public aspectRatiosKeys: string[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The projector shown by this entry.
|
* The projector shown by this entry.
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
public set projector(value: ViewProjector) {
|
public set projector(value: ViewProjector) {
|
||||||
this._projector = value;
|
this._projector = value;
|
||||||
this.updateForm.patchValue({ width: value.width });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get projector(): ViewProjector {
|
public get projector(): ViewProjector {
|
||||||
@ -74,15 +36,6 @@ export class ProjectorListEntryComponent extends BaseViewComponent implements On
|
|||||||
|
|
||||||
private _projector: ViewProjector;
|
private _projector: ViewProjector;
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to check manage permissions
|
|
||||||
*
|
|
||||||
* @returns true if the user can manage projectors
|
|
||||||
*/
|
|
||||||
public get canManage(): boolean {
|
|
||||||
return this.operator.hasPerms('core.can_manage_projector');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor. Initializes the update form.
|
* Constructor. Initializes the update form.
|
||||||
*
|
*
|
||||||
@ -100,142 +53,29 @@ export class ProjectorListEntryComponent extends BaseViewComponent implements On
|
|||||||
protected translate: TranslateService, // protected required for ng-translate-extract
|
protected translate: TranslateService, // protected required for ng-translate-extract
|
||||||
matSnackBar: MatSnackBar,
|
matSnackBar: MatSnackBar,
|
||||||
private repo: ProjectorRepositoryService,
|
private repo: ProjectorRepositoryService,
|
||||||
private formBuilder: FormBuilder,
|
|
||||||
private promptService: PromptService,
|
private promptService: PromptService,
|
||||||
private clockSlideService: ClockSlideService,
|
private dialogService: MatDialog
|
||||||
private operator: OperatorService,
|
|
||||||
private projectionDefaultRepo: ProjectionDefaultRepositoryService
|
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar);
|
super(titleService, translate, matSnackBar);
|
||||||
|
|
||||||
this.aspectRatiosKeys = Object.keys(aspectRatios);
|
|
||||||
|
|
||||||
this.updateForm = this.formBuilder.group({
|
|
||||||
name: ['', Validators.required],
|
|
||||||
aspectRatio: ['', Validators.required],
|
|
||||||
width: [0, Validators.required],
|
|
||||||
projectiondefaults_id: [[]],
|
|
||||||
clock: [true],
|
|
||||||
color: ['', Validators.required],
|
|
||||||
background_color: ['', Validators.required],
|
|
||||||
header_background_color: ['', Validators.required],
|
|
||||||
header_font_color: ['', Validators.required],
|
|
||||||
header_h1_color: ['', Validators.required],
|
|
||||||
chyron_background_color: ['', Validators.required],
|
|
||||||
chyron_font_color: ['', Validators.required],
|
|
||||||
show_header_footer: [],
|
|
||||||
show_title: [],
|
|
||||||
show_logo: []
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public ngOnInit(): void {}
|
||||||
* Watches all projectiondefaults.
|
|
||||||
*/
|
|
||||||
public ngOnInit(): void {
|
|
||||||
this.projectionDefaults = this.projectionDefaultRepo.getViewModelList();
|
|
||||||
this.subscriptions.push(
|
|
||||||
this.projectionDefaultRepo.getViewModelListObservable().subscribe(pds => (this.projectionDefaults = pds))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event on Key Down in update form.
|
|
||||||
*
|
|
||||||
* @param event the keyboard event
|
|
||||||
* @param the current view in scope
|
|
||||||
*/
|
|
||||||
public keyDownFunction(event: KeyboardEvent): void {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
this.onSaveButton();
|
|
||||||
}
|
|
||||||
if (event.key === 'Escape') {
|
|
||||||
this.onCancelButton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the aspect ratio of the given projector.
|
|
||||||
* If no matching ratio is found, the first ratio is returned.
|
|
||||||
*
|
|
||||||
* @param projector The projector to check
|
|
||||||
* @returns the found ratio key.
|
|
||||||
*/
|
|
||||||
public getAspectRatioKey(): string {
|
|
||||||
const ratio = this.projector.width / this.projector.height;
|
|
||||||
const RATIO_ENVIRONMENT = 0.05;
|
|
||||||
const foundRatioKey = Object.keys(aspectRatios).find(key => {
|
|
||||||
const value = aspectRatios[key];
|
|
||||||
return value >= ratio - RATIO_ENVIRONMENT && value <= ratio + RATIO_ENVIRONMENT;
|
|
||||||
});
|
|
||||||
if (!foundRatioKey) {
|
|
||||||
return Object.keys(aspectRatios)[0];
|
|
||||||
} else {
|
|
||||||
return foundRatioKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts editing for the given projector.
|
* Starts editing for the given projector.
|
||||||
*/
|
*/
|
||||||
public onEditButton(): void {
|
public editProjector(): void {
|
||||||
if (this.isEditing) {
|
this.dialogService.open(ProjectorEditDialogComponent, {
|
||||||
return;
|
data: this.projector,
|
||||||
}
|
...largeDialogSettings
|
||||||
this.isEditing = true;
|
|
||||||
this.updateForm.reset();
|
|
||||||
|
|
||||||
this.updateForm.patchValue(this.projector.projector);
|
|
||||||
this.updateForm.patchValue({
|
|
||||||
name: this.translate.instant(this.projector.name),
|
|
||||||
aspectRatio: this.getAspectRatioKey(),
|
|
||||||
clock: this.clockSlideService.isProjectedOn(this.projector)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels the current editing.
|
* Handler to set the selected projector as CLOS reference
|
||||||
*/
|
*/
|
||||||
public onCancelButton(): void {
|
public onSetAsClosRef(): void {
|
||||||
this.isEditing = false;
|
this.repo.setDefaultProjector(this.projector.id);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the projector
|
|
||||||
*
|
|
||||||
* @param projector The projector to save.
|
|
||||||
*/
|
|
||||||
public async onSaveButton(): Promise<void> {
|
|
||||||
const updateProjector: Partial<Projector> = this.updateForm.value;
|
|
||||||
updateProjector.height = Math.round(
|
|
||||||
this.updateForm.value.width / aspectRatios[this.updateForm.value.aspectRatio]
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.clockSlideService.setProjectedOn(this.projector, this.updateForm.value.clock);
|
|
||||||
await this.repo.update(updateProjector, this.projector);
|
|
||||||
this.isEditing = false;
|
|
||||||
} catch (e) {
|
|
||||||
this.raiseError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public aspectRatioChanged(event: MatRadioChange): void {
|
|
||||||
let width: number;
|
|
||||||
if (event.value === '30:9' && this.updateForm.value.width < aspectRatio_30_9_MinWidth) {
|
|
||||||
width = aspectRatio_30_9_MinWidth;
|
|
||||||
} else {
|
|
||||||
width = this.updateForm.value.width;
|
|
||||||
}
|
|
||||||
this.updateProjectorDimensions(width, event.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getMinWidth(): number {
|
|
||||||
if (this.updateForm.value.aspectRatio === '30:9') {
|
|
||||||
return aspectRatio_30_9_MinWidth;
|
|
||||||
} else {
|
|
||||||
return 800;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -247,30 +87,4 @@ export class ProjectorListEntryComponent extends BaseViewComponent implements On
|
|||||||
this.repo.delete(this.projector).catch(this.raiseError);
|
this.repo.delete(this.projector).catch(this.raiseError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Eventhandler for slider changes. Directly saves the new aspect ratio.
|
|
||||||
*
|
|
||||||
* @param event The slider value
|
|
||||||
*/
|
|
||||||
public widthSliderValueChanged(event: MatSliderChange): void {
|
|
||||||
this.updateProjectorDimensions(event.value, this.updateForm.value.aspectRatio);
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateProjectorDimensions(width: number, aspectRatioKey: string): void {
|
|
||||||
const updateProjector: Partial<Projector> = {
|
|
||||||
width: width
|
|
||||||
};
|
|
||||||
updateProjector.height = Math.round(width / aspectRatios[aspectRatioKey]);
|
|
||||||
this.repo.update(updateProjector, this.projector).catch(this.raiseError);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the given form field to the given default.
|
|
||||||
*/
|
|
||||||
public resetField(field: string, value: string): void {
|
|
||||||
const patchValue = {};
|
|
||||||
patchValue[field] = value;
|
|
||||||
this.updateForm.patchValue(patchValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,39 @@
|
|||||||
<os-head-bar [nav]="true" [hasMainButton]="canManage" (mainEvent)="onPlusButton()">
|
<os-head-bar [nav]="true" [hasMainButton]="canManage" (mainEvent)="createNewProjector(projectorDialog)">
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div class="title-slot">
|
<div class="title-slot">
|
||||||
<h2 translate>Projectors</h2>
|
<h2 translate>Projectors</h2>
|
||||||
</div>
|
</div>
|
||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
|
|
||||||
<mat-card *ngIf="!showCreateForm && projectors && projectors.length > 1">
|
<!-- Create projector dialog -->
|
||||||
<span translate> Reference projector for current list of speakers: </span>
|
<ng-template #projectorDialog>
|
||||||
<mat-form-field>
|
<h1 mat-dialog-title>
|
||||||
<mat-select
|
<span translate>New Projector</span>
|
||||||
[disabled]="!!editId"
|
</h1>
|
||||||
[value]="projectors.length ? projectors[0].reference_projector_id : null"
|
|
||||||
(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="showCreateForm">
|
<form [formGroup]="createForm">
|
||||||
<mat-card-title translate>New Projector</mat-card-title>
|
<div mat-dialog-content>
|
||||||
<mat-card-content>
|
|
||||||
<form [formGroup]="createForm" (keydown)="keyDownFunction($event)">
|
|
||||||
<p>
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
|
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
|
||||||
<mat-hint *ngIf="!createForm.controls.name.valid">
|
<mat-hint *ngIf="!createForm.controls.name.valid">
|
||||||
<span translate>Required</span>
|
<span translate>Required</span>
|
||||||
</mat-hint>
|
</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</p>
|
</div>
|
||||||
</form>
|
|
||||||
</mat-card-content>
|
<div mat-dialog-actions>
|
||||||
<mat-card-actions>
|
<button type="submit" mat-button [disabled]="!createForm.valid" color="primary" [mat-dialog-close]="true">
|
||||||
<button mat-button (click)="create()">
|
|
||||||
<span translate>Create</span>
|
<span translate>Create</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-button (click)="showCreateForm = false">
|
<button type="button" mat-button [mat-dialog-close]="null">
|
||||||
<span translate>Cancel</span>
|
<span translate>Cancel</span>
|
||||||
</button>
|
</button>
|
||||||
</mat-card-actions>
|
</div>
|
||||||
</mat-card>
|
</form>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<div id="card-wrapper">
|
<div id="card-wrapper">
|
||||||
<div class="projector-card" *ngFor="let projector of projectors; trackBy: trackByIndex">
|
<div class="projector-card" *ngFor="let projector of projectors | async; trackBy: trackByIndex">
|
||||||
<os-projector-list-entry [projector]="projector"></os-projector-list-entry>
|
<os-projector-list-entry [projector]="projector"></os-projector-list-entry>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
#card-wrapper {
|
#card-wrapper {
|
||||||
margin-top: 10px;
|
margin: 10px;
|
||||||
margin-left: 10px;
|
display: grid;
|
||||||
|
grid-gap: 10px;
|
||||||
|
// if there is only 1 and desktop size, use 0.5 fr
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||||
|
|
||||||
.projector-card {
|
.projector-card {
|
||||||
width: 350px;
|
width: 100%;
|
||||||
margin: 10px;
|
max-width: 100vmin;
|
||||||
float: left;
|
max-height: 100vh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,19 +5,21 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
|
TemplateRef,
|
||||||
ViewEncapsulation
|
ViewEncapsulation
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MatSelectChange } from '@angular/material/select';
|
import { MatDialog } from '@angular/material';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { timer } from 'rxjs';
|
import { BehaviorSubject, timer } from 'rxjs';
|
||||||
|
|
||||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||||
import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service';
|
import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service';
|
||||||
import { Projector } from 'app/shared/models/core/projector';
|
import { Projector } from 'app/shared/models/core/projector';
|
||||||
|
import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
import { ViewProjector } from '../../models/view-projector';
|
import { ViewProjector } from '../../models/view-projector';
|
||||||
|
|
||||||
@ -32,11 +34,6 @@ import { ViewProjector } from '../../models/view-projector';
|
|||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class ProjectorListComponent extends BaseViewComponent implements OnInit, AfterViewInit, OnDestroy {
|
export class ProjectorListComponent extends BaseViewComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
/**
|
|
||||||
* This member is set, if the user is creating a new projector.
|
|
||||||
*/
|
|
||||||
public showCreateForm = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The create form.
|
* The create form.
|
||||||
*/
|
*/
|
||||||
@ -50,7 +47,7 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit,
|
|||||||
/**
|
/**
|
||||||
* All projectors.
|
* All projectors.
|
||||||
*/
|
*/
|
||||||
public projectors: ViewProjector[];
|
public projectors: BehaviorSubject<ViewProjector[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to check manage permissions
|
* Helper to check manage permissions
|
||||||
@ -80,6 +77,7 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit,
|
|||||||
private repo: ProjectorRepositoryService,
|
private repo: ProjectorRepositoryService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private operator: OperatorService,
|
private operator: OperatorService,
|
||||||
|
private dialogService: MatDialog,
|
||||||
private cd: ChangeDetectorRef
|
private cd: ChangeDetectorRef
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar);
|
super(titleService, translate, matSnackBar);
|
||||||
@ -103,8 +101,26 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit,
|
|||||||
*/
|
*/
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
super.setTitle('Projectors');
|
super.setTitle('Projectors');
|
||||||
this.projectors = this.repo.getViewModelList();
|
this.projectors = this.repo.getViewModelListBehaviorSubject();
|
||||||
this.repo.getViewModelListObservable().subscribe(projectors => (this.projectors = projectors));
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param dialog
|
||||||
|
*/
|
||||||
|
public createNewProjector(dialog: TemplateRef<string>): void {
|
||||||
|
this.createForm.reset();
|
||||||
|
const dialogRef = this.dialogService.open(dialog, { ...infoDialogSettings, disableClose: true });
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
if (result) {
|
||||||
|
const projectorToCreate: Partial<Projector> = {
|
||||||
|
name: this.createForm.value.name
|
||||||
|
};
|
||||||
|
|
||||||
|
this.repo.create(projectorToCreate).then(() => {
|
||||||
|
this.cd.detectChanges();
|
||||||
|
}, this.raiseError);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,52 +137,4 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit,
|
|||||||
super.ngOnDestroy();
|
super.ngOnDestroy();
|
||||||
this.cd.detach();
|
this.cd.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the create form.
|
|
||||||
*/
|
|
||||||
public onPlusButton(): void {
|
|
||||||
if (!this.showCreateForm) {
|
|
||||||
this.showCreateForm = true;
|
|
||||||
this.createForm.setValue({ name: '' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the comment section from the create form.
|
|
||||||
*/
|
|
||||||
public create(): void {
|
|
||||||
if (this.createForm.valid && this.showCreateForm) {
|
|
||||||
const projector: Partial<Projector> = {
|
|
||||||
name: this.createForm.value.name,
|
|
||||||
reference_projector_id: this.projectors[0].reference_projector_id
|
|
||||||
};
|
|
||||||
this.repo.create(projector).then(() => {
|
|
||||||
this.showCreateForm = false;
|
|
||||||
this.cd.detectChanges();
|
|
||||||
}, this.raiseError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event on Key Down in update or create form.
|
|
||||||
*
|
|
||||||
* @param event the keyboard event
|
|
||||||
*/
|
|
||||||
public keyDownFunction(event: KeyboardEvent): void {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
this.create();
|
|
||||||
}
|
|
||||||
if (event.key === 'Escape') {
|
|
||||||
this.showCreateForm = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event handler when the reference projector is changed
|
|
||||||
* @param change the change event that contains the new id
|
|
||||||
*/
|
|
||||||
public onSelectReferenceProjector(change: MatSelectChange): void {
|
|
||||||
this.repo.setDefaultProjector(change.value).catch(this.raiseError);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,10 @@ export class ViewProjector extends BaseViewModel<Projector> {
|
|||||||
public get non_stable_elements(): ProjectorElements {
|
public get non_stable_elements(): ProjectorElements {
|
||||||
return this.projector.elements.filter(element => !element.stable);
|
return this.projector.elements.filter(element => !element.stable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get isReferenceProjector(): boolean {
|
||||||
|
return this.id === this.reference_projector_id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
interface IProjectorRelations {
|
interface IProjectorRelations {
|
||||||
referenceProjector: ViewProjector;
|
referenceProjector: ViewProjector;
|
||||||
|
@ -7,6 +7,7 @@ import { MessageControlsComponent } from './components/message-controls/message-
|
|||||||
import { MessageDialogComponent } from './components/message-dialog/message-dialog.component';
|
import { MessageDialogComponent } from './components/message-dialog/message-dialog.component';
|
||||||
import { PresentationControlComponent } from './components/presentation-control/presentation-control.component';
|
import { PresentationControlComponent } from './components/presentation-control/presentation-control.component';
|
||||||
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
||||||
|
import { ProjectorEditDialogComponent } from './components/projector-edit-dialog/projector-edit-dialog.component';
|
||||||
import { ProjectorListEntryComponent } from './components/projector-list-entry/projector-list-entry.component';
|
import { ProjectorListEntryComponent } from './components/projector-list-entry/projector-list-entry.component';
|
||||||
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
||||||
import { ProjectorRoutingModule } from './projector-routing.module';
|
import { ProjectorRoutingModule } from './projector-routing.module';
|
||||||
@ -22,13 +23,15 @@ import { SharedModule } from '../../shared/shared.module';
|
|||||||
CountdownDialogComponent,
|
CountdownDialogComponent,
|
||||||
MessageControlsComponent,
|
MessageControlsComponent,
|
||||||
MessageDialogComponent,
|
MessageDialogComponent,
|
||||||
PresentationControlComponent
|
PresentationControlComponent,
|
||||||
|
ProjectorEditDialogComponent
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
CountdownDialogComponent,
|
CountdownDialogComponent,
|
||||||
MessageDialogComponent,
|
MessageDialogComponent,
|
||||||
PresentationControlComponent,
|
PresentationControlComponent,
|
||||||
ProjectorListEntryComponent
|
ProjectorListEntryComponent,
|
||||||
|
ProjectorEditDialogComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ProjectorModule {}
|
export class ProjectorModule {}
|
||||||
|
Loading…
Reference in New Issue
Block a user