Work on the presentation controls

This commit is contained in:
FinnStutzenstein 2019-04-29 13:39:02 +02:00 committed by Emanuel Schütze
parent efbce9b645
commit 469084a1b6
11 changed files with 164 additions and 82 deletions

View File

@ -5,9 +5,9 @@ import { Observable, BehaviorSubject } from 'rxjs';
import { WebsocketService } from 'app/core/core-services/websocket.service';
import { ProjectorElement, Projector } from 'app/shared/models/core/projector';
export interface SlideData<T = { error?: string }> {
export interface SlideData<T = { error?: string }, P extends ProjectorElement = ProjectorElement> {
data: T;
element: ProjectorElement;
element: P;
error?: string;
}

View File

@ -0,0 +1,13 @@
import { ProjectorElement } from 'app/shared/models/core/projector';
export interface MediafileProjectorElement extends ProjectorElement {
// Images and Pdf
rotation?: 0 | 90 | 180 | 270;
// Images
fullscreen?: boolean;
// Pdf
page?: number;
zoom?: number; // 0 is normal, then +-1, +-2, ...
}

View File

@ -7,12 +7,17 @@ import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
import { BaseViewModelWithListOfSpeakers } from 'app/site/base/base-view-model-with-list-of-speakers';
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
export const IMAGE_MIMETYPES = ['image/png', 'image/jpeg', 'image/gif'];
export const FONT_MIMETYPES = ['font/ttf', 'font/woff', 'application/font-woff', 'application/font-sfnt'];
export const PDF_MIMETYPES = ['application/pdf'];
export interface MediafileTitleInformation {
title: string;
}
export class ViewMediafile extends BaseViewModelWithListOfSpeakers<Mediafile>
implements MediafileTitleInformation, Searchable {
public static COLLECTIONSTRING = Mediafile.COLLECTIONSTRING;
private _uploader: ViewUser;
@ -110,7 +115,7 @@ export class ViewMediafile extends BaseViewModelWithListOfSpeakers<Mediafile>
* @returns true or false
*/
public isImage(): boolean {
return ['image/png', 'image/jpeg', 'image/gif'].includes(this.type);
return IMAGE_MIMETYPES.includes(this.type);
}
/**
@ -119,7 +124,7 @@ export class ViewMediafile extends BaseViewModelWithListOfSpeakers<Mediafile>
* @returns true or false
*/
public isFont(): boolean {
return ['font/ttf', 'font/woff', 'application/font-woff', 'application/font-sfnt'].includes(this.type);
return FONT_MIMETYPES.includes(this.type);
}
/**
@ -128,7 +133,7 @@ export class ViewMediafile extends BaseViewModelWithListOfSpeakers<Mediafile>
* @returns true or false
*/
public isPdf(): boolean {
return ['application/pdf'].includes(this.type);
return PDF_MIMETYPES.includes(this.type);
}
/**

View File

@ -1,34 +1,42 @@
<h5 *ngIf="elements.length" translate>Presentation control</h5>
<div *ngFor="let element of elements">
{{ getMediafile(element).getTitle() }}
<span *ngIf="getMediafile(element).isImage()">
<button type="button" mat-icon-button (click)="imageFullscreen(element)">
<mat-icon>fullscreen</mat-icon>
</button>
<button type="button" mat-icon-button (click)="imageRotate(element)">
<mat-icon>refresh</mat-icon>
</button>
</span>
<span *ngIf="getMediafile(element).isPdf()">
<button type="button" mat-icon-button (click)="pdfBackward(element)" [disabled]="getPage(element) <= 1">
<mat-icon>arrow_back</mat-icon>
</button>
<button type="button" mat-icon-button (click)="pdfForward(element)" [disabled]="getPage(element) >= getMediafile(element).pages">
<mat-icon>arrow_forward</mat-icon>
</button>
<!-- TODO: Use form for page number; use pdfSetPage then. -->
Page {{ getPage(element) }}/{{ getMediafile(element).pages }}
<button type="button" mat-icon-button (click)="pdfRotate(element)">
<mat-icon>refresh</mat-icon>
</button>
<button type="button" mat-icon-button (click)="pdfZoom(element, 'in')">
<mat-icon>zoom_in</mat-icon>
</button>
<button type="button" mat-icon-button (click)="pdfZoom(element, 'reset')">
<mat-icon>replay</mat-icon>
</button>
<button type="button" mat-icon-button (click)="pdfZoom(element, 'out')">
<mat-icon>zoom_out</mat-icon>
</button>
</span>
</div>
<mat-expansion-panel *ngIf="elements.length">
<mat-expansion-panel-header>
<span translate>Media controls</span>
</mat-expansion-panel-header>
<h5 translate>Presentation control</h5>
<div *ngFor="let element of elements">
{{ getMediafile(element).getTitle() }}
<span *ngIf="getMediafile(element).isImage()">
<button type="button" *ngIf="!element.fullscreen" mat-icon-button (click)="fullscreen(element)">
<mat-icon>fullscreen</mat-icon>
</button>
<button type="button" *ngIf="!!element.fullscreen" mat-icon-button (click)="fullscreen(element)">
<mat-icon>fullscreen_exit</mat-icon>
</button>
<button type="button" mat-icon-button (click)="rotate(element)">
<mat-icon>refresh</mat-icon>
</button>
</span>
<span *ngIf="getMediafile(element).isPdf()">
<button type="button" mat-icon-button (click)="pdfBackward(element)" [disabled]="getPage(element) <= 1">
<mat-icon>arrow_back</mat-icon>
</button>
<button type="button" mat-icon-button (click)="pdfForward(element)" [disabled]="getPage(element) >= getMediafile(element).pages">
<mat-icon>arrow_forward</mat-icon>
</button>
<!-- TODO: Use form for page number; use pdfSetPage then. -->
Page {{ getPage(element) }}/{{ getMediafile(element).pages }}
<button type="button" mat-icon-button (click)="rotate(element)">
<mat-icon>refresh</mat-icon>
</button>
<button type="button" mat-icon-button (click)="zoom(element, 'in')">
<mat-icon>zoom_in</mat-icon>
</button>
<button type="button" mat-icon-button (click)="zoom(element, 'reset')">
<mat-icon>replay</mat-icon>
</button>
<button type="button" mat-icon-button (click)="zoom(element, 'out')">
<mat-icon>zoom_out</mat-icon>
</button>
</span>
</div>
</mat-expansion-panel>

View File

@ -7,11 +7,11 @@ import { TranslateService } from '@ngx-translate/core';
import { BaseViewComponent } from 'app/site/base/base-view';
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
import { ProjectorElements, ProjectorElement } from 'app/shared/models/core/projector';
import { ProjectorService } from 'app/core/core-services/projector.service';
import { SlideManager } from 'app/slides/services/slide-manager.service';
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
import { ViewProjector } from '../../models/view-projector';
import { MediafileProjectorElement } from 'app/site/mediafiles/models/mediafile-projector-element';
/**
* The presentation controls.
@ -38,7 +38,7 @@ export class PresentationControlComponent extends BaseViewComponent {
}
// All mediafile elements.
public elements: ProjectorElements = [];
public elements: MediafileProjectorElement[] = [];
/**
* Constructor
@ -66,7 +66,7 @@ export class PresentationControlComponent extends BaseViewComponent {
*/
private updateElements(): void {
this.elements = this.projector.elements.filter(element => {
if (element.name !== Mediafile.COLLECTIONSTRING && !element.id) {
if (element.name !== Mediafile.COLLECTIONSTRING || !element.id) {
return false;
}
const mediafile = this.mediafileRepo.getViewModel(element.id);
@ -74,14 +74,14 @@ export class PresentationControlComponent extends BaseViewComponent {
});
}
public getMediafile(element: ProjectorElement): ViewMediafile {
public getMediafile(element: MediafileProjectorElement): ViewMediafile {
return this.mediafileRepo.getViewModel(element.id);
}
/**
* @returns the currently used page number (1 in case of unnumbered elements)
*/
public getPage(element: ProjectorElement): number {
public getPage(element: MediafileProjectorElement): number {
return element.page || 1;
}
@ -90,7 +90,7 @@ export class PresentationControlComponent extends BaseViewComponent {
*
* @param element
*/
public pdfForward(element: ProjectorElement): void {
public pdfForward(element: MediafileProjectorElement): void {
if (this.getPage(element) < this.getMediafile(element).pages) {
this.pdfSetPage(element, this.getPage(element) + 1);
}
@ -101,7 +101,7 @@ export class PresentationControlComponent extends BaseViewComponent {
*
* @param element
*/
public pdfBackward(element: ProjectorElement): void {
public pdfBackward(element: MediafileProjectorElement): void {
if (this.getPage(element) > 1) {
this.pdfSetPage(element, this.getPage(element) - 1);
}
@ -114,19 +114,47 @@ export class PresentationControlComponent extends BaseViewComponent {
* @param element
* @param page
*/
public pdfSetPage(element: ProjectorElement, page: number): void {
const idElement = this.slideManager.getIdentifialbeProjectorElement(element);
public pdfSetPage(element: MediafileProjectorElement, page: number): void {
if (this.getMediafile(element).pages >= page) {
idElement.page = page;
this.projectorService.updateElement(this.projector.projector, idElement).then(null, this.raiseError);
element.page = page;
this.updateElement(element);
}
}
public pdfZoom(element: ProjectorElement, direction: 'in' | 'out' | 'reset'): void {}
public zoom(element: MediafileProjectorElement, direction: 'in' | 'out' | 'reset'): void {
if (direction === 'reset') {
element.zoom = 0;
} else if (direction === 'in') {
element.zoom = (element.zoom || 0) + 1;
} else if (direction === 'out') {
element.zoom = (element.zoom || 0) - 1;
}
this.updateElement(element);
}
public pdfRotate(element: ProjectorElement): void {}
public fullscreen(element: MediafileProjectorElement): void {
element.fullscreen = !element.fullscreen;
this.updateElement(element);
}
public imageFullscreen(element: ProjectorElement): void {}
public rotate(element: MediafileProjectorElement): void {
let rotation: 0 | 90 | 180 | 270 = element.rotation || 0;
if (rotation === 0) {
rotation = 90;
} else if (rotation === 90) {
rotation = 180;
} else if (rotation === 180) {
rotation = 270;
} else {
// 270
rotation = 0;
}
element.rotation = rotation;
this.updateElement(element);
}
public imageRotate(element: ProjectorElement): void {}
private updateElement(element: MediafileProjectorElement): void {
const idElement = this.slideManager.getIdentifialbeProjectorElement(element);
this.projectorService.updateElement(this.projector.projector, idElement).then(null, this.raiseError);
}
}

View File

@ -264,13 +264,16 @@
</mat-expansion-panel>
<!-- File display controls -->
<mat-expansion-panel>
<!--<mat-expansion-panel>
<mat-expansion-panel-header>
<span translate>Media controls</span>
</mat-expansion-panel-header>
<os-presentation-control [projector]="projector">
</os-presentation-control>
</mat-expansion-panel>
</mat-expansion-panel>-->
<os-presentation-control [projector]="projector">
</os-presentation-control>
</mat-accordion>
</div>
</div>

View File

@ -2,17 +2,18 @@ import { Input } from '@angular/core';
import { ViewProjector } from 'app/site/projector/models/view-projector';
import { SlideData } from 'app/core/core-services/projector-data.service';
import { ProjectorElement } from 'app/shared/models/core/projector';
/**
* Every slide has to extends this base class. It forces the slides
* to have an input for the slidedata.
*/
export abstract class BaseSlideComponent<T extends object> {
export abstract class BaseSlideComponent<T extends object, P extends ProjectorElement = ProjectorElement> {
/**
* Each slide must take slide data.
*/
@Input()
public data: SlideData<T>;
public data: SlideData<T, P>;
/**
* The projector where this slide is projected on.

View File

@ -1,17 +1,19 @@
<div *ngIf="data">
<div *ngIf="data.data.type == 'image/png'">
<img [src]="url" alt=""/>
<div *ngIf="isImage">
<img [src]="url" alt="" [ngClass]="'rotate' + (data.element.rotation || 0)"/>
{{ data.element.fullscreen || false }}
</div>
<div *ngIf="data.data.type == 'image/jpeg'">
<img [src]="url" alt=""/>
</div>
<div *ngIf="data.data.type == 'application/pdf'">
<pdf-viewer
[show-all]="false"
[original-size]="false"
[autoresize]="true"
[page]="page"
[src]="url"
style="display: block;"></pdf-viewer>
<div *ngIf="isPdf">
{{ data.element.rotation || 0 }}
<pdf-viewer
[show-all]="false"
[original-size]="false"
[fit-to-page]="true"
[autoresize]="true"
[page]="data.element.page || 1"
[zoom]="zoom"
[rotation]="data.element.rotation || 0"
[src]="url"
style="display: block;"></pdf-viewer>
</div>
</div>

View File

@ -0,0 +1,12 @@
.rotate0 {
transform: rotate(0deg);
}
.rotate90 {
transform: rotate(90deg);
}
.rotate180 {
transform: rotate(180deg);
}
.rotate270 {
transform: rotate(270deg);
}

View File

@ -2,22 +2,32 @@ import { Component } from '@angular/core';
import { BaseSlideComponent } from 'app/slides/base-slide-component';
import { MediafileSlideData } from './mediafile-slide-data';
import { IMAGE_MIMETYPES, PDF_MIMETYPES } from 'app/site/mediafiles/models/view-mediafile';
import { MediafileProjectorElement } from 'app/site/mediafiles/models/mediafile-projector-element';
@Component({
selector: 'os-mediafile-slide',
templateUrl: './mediafile-slide.component.html',
styleUrls: ['./mediafile-slide.component.scss']
})
export class MediafileSlideComponent extends BaseSlideComponent<MediafileSlideData> {
public constructor() {
super();
}
public get page(): string {
return this.data.element.page;
}
export class MediafileSlideComponent extends BaseSlideComponent<MediafileSlideData, MediafileProjectorElement> {
public get url(): string {
return `${this.data.data.media_url_prefix}/${this.data.data.path}`;
}
public get zoom(): number {
return Math.pow(1.1, this.data.element.zoom || 0);
}
public get isImage(): boolean {
return IMAGE_MIMETYPES.includes(this.data.data.type);
}
public get isPdf(): boolean {
return PDF_MIMETYPES.includes(this.data.data.type);
}
public constructor() {
super();
}
}

View File

@ -54,8 +54,8 @@ export class SlideManager {
return this.loadedSlideConfigurations[slideName];
}
public getIdentifialbeProjectorElement(element: ProjectorElement): IdentifiableProjectorElement {
const identifiableElement: IdentifiableProjectorElement = element as IdentifiableProjectorElement;
public getIdentifialbeProjectorElement<P extends ProjectorElement>(element: P): IdentifiableProjectorElement & P {
const identifiableElement: IdentifiableProjectorElement & P = element as IdentifiableProjectorElement & P;
const identifiers = this.getManifest(element.name).elementIdentifiers.map(x => x); // map to copy.
identifiableElement.getIdentifiers = () => identifiers;
return identifiableElement;