Merge pull request #4547 from tsiegleauq/projector-messages-rework
Add better projector messages
This commit is contained in:
commit
8dfeaa5b09
@ -50,7 +50,7 @@ export class ProjectorButtonComponent implements OnInit {
|
|||||||
* Pre-define projection target
|
* Pre-define projection target
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
public projector: Projector;
|
public projector: Projector | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The constructor
|
* The constructor
|
||||||
@ -100,6 +100,9 @@ export class ProjectorButtonComponent implements OnInit {
|
|||||||
if (!this.object) {
|
if (!this.object) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.projectorService.isProjected(this.object);
|
|
||||||
|
return this.projector
|
||||||
|
? this.projectorService.isProjectedOn(this.object, this.projector)
|
||||||
|
: this.projectorService.isProjected(this.object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
grid-template-columns: min-content auto auto;
|
grid-template-columns: min-content auto auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// could be in a shared scss
|
||||||
.projector-button {
|
.projector-button {
|
||||||
grid-area: project;
|
grid-area: project;
|
||||||
margin: auto 10px auto 0;
|
margin: auto 10px auto 0;
|
||||||
@ -21,6 +22,7 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// could be in a shared scss
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
grid-area: buttons;
|
grid-area: buttons;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
<mat-card *ngIf="message">
|
||||||
|
<div class="grid-wrapper">
|
||||||
|
<div class="projector-button">
|
||||||
|
<os-projector-button [object]="message" [projector]="projector"></os-projector-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="small-mat-icon-button"
|
||||||
|
mat-icon-button
|
||||||
|
(click)="onBringDialog()"
|
||||||
|
matTooltip="{{ 'Open projection dialog' | translate }}"
|
||||||
|
>
|
||||||
|
<mat-icon>
|
||||||
|
open_in_new
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="small-mat-icon-button" mat-icon-button (click)="onEdit()">
|
||||||
|
<mat-icon>
|
||||||
|
edit
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="small-mat-icon-button" mat-icon-button (click)="onDelete()">
|
||||||
|
<mat-icon>
|
||||||
|
close
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message" [innerHTML]="message.getPreview(100)"></div>
|
||||||
|
</div>
|
||||||
|
</mat-card>
|
@ -0,0 +1,45 @@
|
|||||||
|
.grid-wrapper {
|
||||||
|
display: grid;
|
||||||
|
width: 100%;
|
||||||
|
grid-template-areas:
|
||||||
|
'project message buttons'
|
||||||
|
'project message buttons';
|
||||||
|
grid-gap: 10px;
|
||||||
|
grid-template-columns: min-content auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// could be in a shared scss
|
||||||
|
.projector-button {
|
||||||
|
grid-area: project;
|
||||||
|
margin: auto 10px auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// could be in a shared scss
|
||||||
|
.action-buttons {
|
||||||
|
grid-area: buttons;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
// smaller and closer mat icon buttons.
|
||||||
|
// use on button-tags
|
||||||
|
// TODO: Somehow does not apply when in style.scss
|
||||||
|
.small-mat-icon-button {
|
||||||
|
$size: 20px;
|
||||||
|
margin: 0 5px;
|
||||||
|
position: relative;
|
||||||
|
width: $size;
|
||||||
|
height: $size;
|
||||||
|
|
||||||
|
::ng-deep .mat-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
line-height: normal;
|
||||||
|
width: $size;
|
||||||
|
height: $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
font-size: $size;
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +1,21 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MessageControlsComponent } from './message-controls.component';
|
||||||
import { E2EImportsModule } from 'e2e-imports.module';
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
import { ProjectorMessageListComponent } from './projector-message-list.component';
|
|
||||||
|
|
||||||
describe('ProjectorMessageListComponent', () => {
|
describe('MessageControlsComponent', () => {
|
||||||
let component: ProjectorMessageListComponent;
|
let component: MessageControlsComponent;
|
||||||
let fixture: ComponentFixture<ProjectorMessageListComponent>;
|
let fixture: ComponentFixture<MessageControlsComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [E2EImportsModule],
|
imports: [E2EImportsModule],
|
||||||
declarations: [ProjectorMessageListComponent]
|
declarations: [MessageControlsComponent]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ProjectorMessageListComponent);
|
fixture = TestBed.createComponent(MessageControlsComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
@ -0,0 +1,90 @@
|
|||||||
|
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
|
||||||
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
|
import { Projector } from 'app/shared/models/core/projector';
|
||||||
|
import { ProjectorMessageRepositoryService } from 'app/core/repositories/projector/projector-message-repository.service';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
import { ViewProjectorMessage } from '../../models/view-projector-message';
|
||||||
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
import { ProjectionDialogService } from 'app/core/ui-services/projection-dialog.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small controls component for messages.
|
||||||
|
* Used in the projector detail view, can could be embedded anywhere else
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'os-message-controls',
|
||||||
|
templateUrl: './message-controls.component.html',
|
||||||
|
styleUrls: ['./message-controls.component.scss']
|
||||||
|
})
|
||||||
|
export class MessageControlsComponent extends BaseViewComponent implements OnInit {
|
||||||
|
/**
|
||||||
|
* Input slot for the projector message model
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
public message: ViewProjectorMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output event for the edit button
|
||||||
|
*/
|
||||||
|
@Output()
|
||||||
|
public editEvent = new EventEmitter<ViewProjectorMessage>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre defined projection target (if any)
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
public projector: Projector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param titleService set the title, required by parent
|
||||||
|
* @param matSnackBar show errors
|
||||||
|
* @param translate translate properties
|
||||||
|
* @param repo the projector message repo
|
||||||
|
* @param promptService delete prompt
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
titleService: Title,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
private repo: ProjectorMessageRepositoryService,
|
||||||
|
private promptService: PromptService,
|
||||||
|
private projectionDialogService: ProjectionDialogService
|
||||||
|
) {
|
||||||
|
super(titleService, translate, matSnackBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init
|
||||||
|
*/
|
||||||
|
public ngOnInit(): void {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires an edit event
|
||||||
|
*/
|
||||||
|
public onEdit(): void {
|
||||||
|
this.editEvent.next(this.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Brings the projection dialog
|
||||||
|
*/
|
||||||
|
public onBringDialog(): void {
|
||||||
|
this.projectionDialogService.openProjectDialogFor(this.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On delete button
|
||||||
|
*/
|
||||||
|
public async onDelete(): Promise<void> {
|
||||||
|
const content =
|
||||||
|
this.translate.instant('Delete message') + ` ${this.translate.instant(this.message.getTitle())}?`;
|
||||||
|
if (await this.promptService.open('Are you sure?', content)) {
|
||||||
|
this.repo.delete(this.message).then(() => {}, this.raiseError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
<h1 mat-dialog-title>
|
||||||
|
<span translate>Message</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<form [formGroup]="messageForm">
|
||||||
|
<div mat-dialog-content>
|
||||||
|
<!-- Message -->
|
||||||
|
<editor formControlName="text" [init]="tinyMceSettings"></editor>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions>
|
||||||
|
<!-- Submit countdown button -->
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
mat-button
|
||||||
|
color="primary"
|
||||||
|
[mat-dialog-close]="messageForm.value"
|
||||||
|
[disabled]="!messageForm.valid"
|
||||||
|
>
|
||||||
|
<span translate>Save</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Cancel Countdown button -->
|
||||||
|
<button type="button" mat-button [mat-dialog-close]="null">
|
||||||
|
<span translate>Cancel</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -0,0 +1,38 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MessageDialogComponent, MessageData } from './message-dialog.component';
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||||
|
|
||||||
|
describe('MessageDialogComponent', () => {
|
||||||
|
let component: MessageDialogComponent;
|
||||||
|
let fixture: ComponentFixture<MessageDialogComponent>;
|
||||||
|
|
||||||
|
const dialogData: MessageData = {
|
||||||
|
text: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [MessageDialogComponent],
|
||||||
|
imports: [E2EImportsModule],
|
||||||
|
providers: [
|
||||||
|
{ provide: MatDialogRef, useValue: {} },
|
||||||
|
{
|
||||||
|
provide: MAT_DIALOG_DATA,
|
||||||
|
useValue: dialogData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(MessageDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,58 @@
|
|||||||
|
import { Component, OnInit, Inject } from '@angular/core';
|
||||||
|
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||||
|
import { MAT_DIALOG_DATA, MatSnackBar } from '@angular/material';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine what to send
|
||||||
|
*/
|
||||||
|
export interface MessageData {
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog component to edit projector messages
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'os-message-dialog',
|
||||||
|
templateUrl: './message-dialog.component.html',
|
||||||
|
styleUrls: ['./message-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class MessageDialogComponent extends BaseViewComponent implements OnInit {
|
||||||
|
/**
|
||||||
|
* The form data
|
||||||
|
*/
|
||||||
|
public messageForm: FormGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constrcutor
|
||||||
|
*
|
||||||
|
* @param title required by parent
|
||||||
|
* @param matSnackBar to show errors
|
||||||
|
* @param translate to translate properties
|
||||||
|
* @param formBuilder to create the message form
|
||||||
|
* @param data the injected data, i.e the current text of a message to edit
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
title: Title,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
|
translate: TranslateService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: MessageData
|
||||||
|
) {
|
||||||
|
super(title, translate, matSnackBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init create the form
|
||||||
|
*/
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.messageForm = this.formBuilder.group({
|
||||||
|
text: [this.data.text, Validators.required]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -185,11 +185,7 @@
|
|||||||
<span translate>Countdowns</span>
|
<span translate>Countdowns</span>
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
<mat-list>
|
<mat-list>
|
||||||
<mat-list-item
|
<mat-list-item *ngFor="let countdown of countdowns" class="larger-mat-list-item">
|
||||||
*ngFor="let countdown of countdowns"
|
|
||||||
class="larger-mat-list-item"
|
|
||||||
[ngClass]="{ projected: isProjected(countdown) }"
|
|
||||||
>
|
|
||||||
<os-countdown-controls
|
<os-countdown-controls
|
||||||
class="dynamic-list-entry"
|
class="dynamic-list-entry"
|
||||||
[countdown]="countdown"
|
[countdown]="countdown"
|
||||||
@ -207,21 +203,25 @@
|
|||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
|
||||||
<!-- messages -->
|
<!-- messages -->
|
||||||
<mat-expansion-panel *ngIf="messages.length">
|
<mat-expansion-panel>
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<span translate>Messages</span>
|
<span translate>Messages</span>
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
<mat-list>
|
<mat-list>
|
||||||
<mat-list-item *ngFor="let message of messages" [ngClass]="{ projected: isProjected(message) }">
|
<mat-list-item *ngFor="let message of messages" class="larger-mat-list-item">
|
||||||
<button type="button" mat-icon-button (click)="project(message)">
|
<os-message-controls
|
||||||
<mat-icon>videocam</mat-icon>
|
class="dynamic-list-entry"
|
||||||
</button>
|
[message]="message"
|
||||||
<span>{{ message.getPreview(40) }}</span>
|
[projector]="projector.projector"
|
||||||
|
(editEvent)="openMessagesDialog($event)"
|
||||||
|
>
|
||||||
|
</os-message-controls>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
</mat-list>
|
</mat-list>
|
||||||
<mat-action-row>
|
<mat-action-row>
|
||||||
<button type="button" mat-icon-button routerLink="/projectors/messages">
|
<button type="button" mat-button (click)="openMessagesDialog()">
|
||||||
<mat-icon>edit</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
|
<span translate>Add message</span>
|
||||||
</button>
|
</button>
|
||||||
</mat-action-row>
|
</mat-action-row>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
@ -123,7 +123,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.larger-mat-list-item {
|
.larger-mat-list-item {
|
||||||
height: 90px;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.larger-mat-list-item + .larger-mat-list-item {
|
.larger-mat-list-item + .larger-mat-list-item {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { E2EImportsModule } from '../../../../../e2e-imports.module';
|
|
||||||
import { ProjectorDetailComponent } from './projector-detail.component';
|
import { ProjectorDetailComponent } from './projector-detail.component';
|
||||||
import { ProjectorModule } from '../../projector.module';
|
import { ProjectorModule } from '../../projector.module';
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
describe('ProjectorDetailComponent', () => {
|
describe('ProjectorDetailComponent', () => {
|
||||||
let component: ProjectorDetailComponent;
|
let component: ProjectorDetailComponent;
|
||||||
|
@ -18,13 +18,15 @@ import { CurrentSpeakerChyronSlideService } from '../../services/current-speaker
|
|||||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||||
import { ProjectorService } from 'app/core/core-services/projector.service';
|
import { ProjectorService } from 'app/core/core-services/projector.service';
|
||||||
import { moveItemInArray, CdkDragDrop } from '@angular/cdk/drag-drop';
|
import { moveItemInArray, CdkDragDrop } from '@angular/cdk/drag-drop';
|
||||||
|
import { Projectable } from 'app/site/base/projectable';
|
||||||
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 { SlideManager } from 'app/slides/services/slide-manager.service';
|
import { SlideManager } from 'app/slides/services/slide-manager.service';
|
||||||
import { CountdownRepositoryService } from 'app/core/repositories/projector/countdown-repository.service';
|
import { CountdownRepositoryService } from 'app/core/repositories/projector/countdown-repository.service';
|
||||||
import { ProjectorMessageRepositoryService } from 'app/core/repositories/projector/projector-message-repository.service';
|
import { ProjectorMessageRepositoryService } from 'app/core/repositories/projector/projector-message-repository.service';
|
||||||
import { ViewProjectorMessage } from 'app/site/projector/models/view-projector-message';
|
import { ViewProjectorMessage } from 'app/site/projector/models/view-projector-message';
|
||||||
import { ViewCountdown } from 'app/site/projector/models/view-countdown';
|
import { ViewCountdown } from 'app/site/projector/models/view-countdown';
|
||||||
import { Projectable } from 'app/site/base/projectable';
|
import { MessageDialogComponent, MessageData } from '../message-dialog/message-dialog.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The projector detail view.
|
* The projector detail view.
|
||||||
@ -177,7 +179,7 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the countdown dialog
|
* Opens the countdown dialog*
|
||||||
*
|
*
|
||||||
* @param viewCountdown optional existing countdown to edit
|
* @param viewCountdown optional existing countdown to edit
|
||||||
*/
|
*/
|
||||||
@ -212,6 +214,37 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* opens the "edit/create" dialog for messages
|
||||||
|
*
|
||||||
|
* @param viewMessage an optional ViewProjectorMessage to edit. If empty, a new one was created
|
||||||
|
*/
|
||||||
|
public openMessagesDialog(viewMessage?: ViewProjectorMessage): void {
|
||||||
|
let messageData: MessageData = {
|
||||||
|
text: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
if (viewMessage) {
|
||||||
|
messageData = {
|
||||||
|
text: viewMessage.message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogRef = this.dialog.open(MessageDialogComponent, {
|
||||||
|
data: messageData,
|
||||||
|
maxHeight: '90vh',
|
||||||
|
width: '800px',
|
||||||
|
maxWidth: '90vw',
|
||||||
|
disableClose: true
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
if (result) {
|
||||||
|
this.submitMessage(result, viewMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to send a countdown
|
* Function to send a countdown
|
||||||
*
|
*
|
||||||
@ -234,4 +267,22 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
|||||||
this.countdownRepo.create(sendData).then(() => {}, this.raiseError);
|
this.countdownRepo.create(sendData).then(() => {}, this.raiseError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit altered messages to the message repository
|
||||||
|
*
|
||||||
|
* @param data: The message to post
|
||||||
|
* @param viewMessage optional, set viewMessage to update an existing message
|
||||||
|
*/
|
||||||
|
public submitMessage(data: MessageData, viewMessage?: ViewProjectorMessage): void {
|
||||||
|
const sendData = new ProjectorMessage({
|
||||||
|
message: data.text
|
||||||
|
});
|
||||||
|
|
||||||
|
if (viewMessage) {
|
||||||
|
this.messageRepo.update(sendData, viewMessage).then(() => {}, this.raiseError);
|
||||||
|
} else {
|
||||||
|
this.messageRepo.create(sendData).then(() => {}, this.raiseError);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,6 @@
|
|||||||
<div class="title-slot">
|
<div class="title-slot">
|
||||||
<h2 translate>Projectors</h2>
|
<h2 translate>Projectors</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Menu -->
|
|
||||||
<div class="menu-slot">
|
|
||||||
<div *osPerms="'core.can_manage_projector'">
|
|
||||||
<button type="button" mat-icon-button [matMenuTriggerFor]="ellipsisMenu">
|
|
||||||
<mat-icon>more_vert</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
|
|
||||||
<mat-card *ngIf="!projectorToCreate && projectors && projectors.length > 1">
|
<mat-card *ngIf="!projectorToCreate && projectors && projectors.length > 1">
|
||||||
@ -173,10 +164,3 @@
|
|||||||
</os-meta-text-block>
|
</os-meta-text-block>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-menu #ellipsisMenu="matMenu">
|
|
||||||
<button type="button" mat-menu-item routerLink="/projectors/messages">
|
|
||||||
<mat-icon>note</mat-icon>
|
|
||||||
<span translate>Projector messages</span>
|
|
||||||
</button>
|
|
||||||
</mat-menu>
|
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
<os-head-bar [nav]="false" [goBack]="true" [mainButton]="true" (mainEvent)="onPlusButton()">
|
|
||||||
<!-- Title -->
|
|
||||||
<div class="title-slot">
|
|
||||||
<h2 translate>Messages</h2>
|
|
||||||
</div>
|
|
||||||
</os-head-bar>
|
|
||||||
|
|
||||||
<div class="head-spacer"></div>
|
|
||||||
<mat-card *ngIf="messageToCreate">
|
|
||||||
<mat-card-title translate>New message</mat-card-title>
|
|
||||||
<mat-card-content>
|
|
||||||
<form [formGroup]="createForm"
|
|
||||||
(keydown)="onKeyDownCreate($event)">
|
|
||||||
<p>
|
|
||||||
<editor formControlName="message" [init]="tinyMceSettings"></editor>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</mat-card-content>
|
|
||||||
<mat-card-actions>
|
|
||||||
<button mat-button (click)="create()">
|
|
||||||
<span translate>Save</span>
|
|
||||||
</button>
|
|
||||||
<button mat-button (click)="onCancelCreate()">
|
|
||||||
<span translate>Cancel</span>
|
|
||||||
</button>
|
|
||||||
</mat-card-actions>
|
|
||||||
</mat-card>
|
|
||||||
|
|
||||||
<mat-accordion class="os-card">
|
|
||||||
<mat-expansion-panel *ngFor="let message of messages" (opened)="openId = message.id"
|
|
||||||
(closed)="panelClosed(message)" [expanded]="openId === message.id" multiple="false">
|
|
||||||
|
|
||||||
<!-- Projector button and countdown description-->
|
|
||||||
<mat-expansion-panel-header>
|
|
||||||
<mat-panel-title>
|
|
||||||
<div class="header-container">
|
|
||||||
<div class="header-projector-button">
|
|
||||||
<os-projector-button [object]="message"></os-projector-button>
|
|
||||||
</div>
|
|
||||||
<div class="header-name">
|
|
||||||
{{ message.getPreview() }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</mat-panel-title>
|
|
||||||
</mat-expansion-panel-header>
|
|
||||||
<form [formGroup]="updateForm"
|
|
||||||
*ngIf="editId === message.id"
|
|
||||||
(keydown)="onKeyDownUpdate($event)">
|
|
||||||
<h5 translate>Edit message</h5>
|
|
||||||
<p>
|
|
||||||
<editor formControlName="message" [init]="tinyMceSettings"></editor>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
<ng-container *ngIf="editId !== message.id">
|
|
||||||
<div class="message" *ngIf="message.message" [innerHTML]="getSafeMessage(message)"></div>
|
|
||||||
<div *ngIf="!message.message" class="no-content" translate>No messages</div>
|
|
||||||
</ng-container>
|
|
||||||
<mat-action-row>
|
|
||||||
<button *ngIf="editId !== message.id" mat-button class="on-transition-fade" (click)="onEditButton(message)"
|
|
||||||
mat-icon-button>
|
|
||||||
<mat-icon>edit</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button *ngIf="editId === message.id" mat-button class="on-transition-fade" (click)="onCancelUpdate()"
|
|
||||||
mat-icon-button>
|
|
||||||
<mat-icon>close</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button *ngIf="editId === message.id" mat-button class="on-transition-fade" (click)="onSaveButton(message)"
|
|
||||||
mat-icon-button>
|
|
||||||
<mat-icon>save</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-button class='on-transition-fade' (click)=onDeleteButton(message) mat-icon-button>
|
|
||||||
<mat-icon>delete</mat-icon>
|
|
||||||
</button>
|
|
||||||
</mat-action-row>
|
|
||||||
</mat-expansion-panel>
|
|
||||||
</mat-accordion>
|
|
||||||
|
|
||||||
<mat-card *ngIf="messages.length === 0">
|
|
||||||
<mat-card-content>
|
|
||||||
<div class="no-content" translate>No messages</div>
|
|
||||||
</mat-card-content>
|
|
||||||
</mat-card>
|
|
@ -1,42 +0,0 @@
|
|||||||
.head-spacer {
|
|
||||||
width: 100%;
|
|
||||||
height: 60px;
|
|
||||||
line-height: 60px;
|
|
||||||
text-align: right;
|
|
||||||
background: white; /* TODO: remove this and replace with theme */
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
mat-card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-container {
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: auto;
|
|
||||||
grid-template-columns: 40px 1fr;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
grid-row-start: 1;
|
|
||||||
grid-row-end: span 1;
|
|
||||||
grid-column-end: span 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-projector-button {
|
|
||||||
grid-column-start: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-name {
|
|
||||||
grid-column-start: 2;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
::ng-deep p {
|
|
||||||
margin: 0 0 10px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,192 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { Title, SafeHtml, DomSanitizer } from '@angular/platform-browser';
|
|
||||||
import { MatSnackBar } from '@angular/material';
|
|
||||||
import { FormGroup, FormBuilder } from '@angular/forms';
|
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
import { BaseViewComponent } from '../../../base/base-view';
|
|
||||||
import { ProjectorMessage } from 'app/shared/models/core/projector-message';
|
|
||||||
import { ViewProjectorMessage } from '../../models/view-projector-message';
|
|
||||||
import { ProjectorMessageRepositoryService } from 'app/core/repositories/projector/projector-message-repository.service';
|
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List view for the projector messages.
|
|
||||||
*/
|
|
||||||
@Component({
|
|
||||||
selector: 'os-projector-message-list',
|
|
||||||
templateUrl: './projector-message-list.component.html',
|
|
||||||
styleUrls: ['./projector-message-list.component.scss']
|
|
||||||
})
|
|
||||||
export class ProjectorMessageListComponent extends BaseViewComponent implements OnInit {
|
|
||||||
public messageToCreate: ProjectorMessage | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source of the Data
|
|
||||||
*/
|
|
||||||
public messages: ViewProjectorMessage[] = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current focussed formgroup
|
|
||||||
*/
|
|
||||||
public updateForm: FormGroup;
|
|
||||||
|
|
||||||
public createForm: FormGroup;
|
|
||||||
|
|
||||||
public openId: number | null;
|
|
||||||
public editId: number | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
public constructor(
|
|
||||||
titleService: Title,
|
|
||||||
protected translate: TranslateService, // protected required for ng-translate-extract
|
|
||||||
matSnackBar: MatSnackBar,
|
|
||||||
private repo: ProjectorMessageRepositoryService,
|
|
||||||
private formBuilder: FormBuilder,
|
|
||||||
private promptService: PromptService,
|
|
||||||
private santinizer: DomSanitizer
|
|
||||||
) {
|
|
||||||
super(titleService, translate, matSnackBar);
|
|
||||||
|
|
||||||
const form = {
|
|
||||||
message: ['']
|
|
||||||
};
|
|
||||||
this.createForm = this.formBuilder.group(form);
|
|
||||||
this.updateForm = this.formBuilder.group(form);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init function.
|
|
||||||
*
|
|
||||||
* Sets the title and gets/observes messages from DataStore
|
|
||||||
*/
|
|
||||||
public ngOnInit(): void {
|
|
||||||
super.setTitle('Messages');
|
|
||||||
this.messages = this.repo.getSortedViewModelList();
|
|
||||||
this.repo.getViewModelListObservable().subscribe(messages => (this.messages = messages));
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSafeMessage(message: ViewProjectorMessage): SafeHtml {
|
|
||||||
return this.santinizer.bypassSecurityTrustHtml(message.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new message.
|
|
||||||
*/
|
|
||||||
public onPlusButton(): void {
|
|
||||||
if (!this.messageToCreate) {
|
|
||||||
this.createForm.reset();
|
|
||||||
this.createForm.setValue({
|
|
||||||
message: ''
|
|
||||||
});
|
|
||||||
this.messageToCreate = new ProjectorMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler when clicking on create to create a new statute paragraph
|
|
||||||
*/
|
|
||||||
public create(): void {
|
|
||||||
if (this.createForm.valid) {
|
|
||||||
this.messageToCreate.patchValues(this.createForm.value as ProjectorMessage);
|
|
||||||
this.repo.create(this.messageToCreate).then(() => {
|
|
||||||
this.messageToCreate = null;
|
|
||||||
}, this.raiseError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executed on edit button
|
|
||||||
* @param message
|
|
||||||
*/
|
|
||||||
public onEditButton(message: ViewProjectorMessage): void {
|
|
||||||
this.editId = message.id;
|
|
||||||
|
|
||||||
this.updateForm.setValue({
|
|
||||||
message: message.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the message
|
|
||||||
* @param message The message to save
|
|
||||||
*/
|
|
||||||
public onSaveButton(message: ViewProjectorMessage): void {
|
|
||||||
if (this.updateForm.valid) {
|
|
||||||
this.repo.update(this.updateForm.value as Partial<ProjectorMessage>, message).then(() => {
|
|
||||||
this.openId = this.editId = null;
|
|
||||||
}, this.raiseError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is executed, when the delete button is pressed
|
|
||||||
*
|
|
||||||
* @param message The message to delete
|
|
||||||
*/
|
|
||||||
public async onDeleteButton(message: ViewProjectorMessage): Promise<void> {
|
|
||||||
const title = this.translate.instant('Are you sure you want to delete the selected message?');
|
|
||||||
if (await this.promptService.open(title, null)) {
|
|
||||||
this.repo.delete(message).then(() => (this.openId = this.editId = null), this.raiseError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is executed when a mat-extension-panel is closed
|
|
||||||
*
|
|
||||||
* @param message the message in the panel
|
|
||||||
*/
|
|
||||||
public panelClosed(message: ViewProjectorMessage): void {
|
|
||||||
this.openId = null;
|
|
||||||
if (this.editId) {
|
|
||||||
this.onSaveButton(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* clicking Shift and Enter will save automatically
|
|
||||||
* clicking Escape will cancel the process
|
|
||||||
*
|
|
||||||
* @param event has the code
|
|
||||||
*/
|
|
||||||
public onKeyDownCreate(event: KeyboardEvent): void {
|
|
||||||
if (event.key === 'Enter' && event.shiftKey) {
|
|
||||||
this.create();
|
|
||||||
}
|
|
||||||
if (event.key === 'Escape') {
|
|
||||||
this.onCancelCreate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the current form action
|
|
||||||
*/
|
|
||||||
public onCancelCreate(): void {
|
|
||||||
this.messageToCreate = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* clicking Shift and Enter will save automatically
|
|
||||||
* clicking Escape will cancel the process
|
|
||||||
*
|
|
||||||
* @param event has the code
|
|
||||||
*/
|
|
||||||
public onKeyDownUpdate(event: KeyboardEvent): void {
|
|
||||||
if (event.key === 'Enter' && event.shiftKey) {
|
|
||||||
const message = this.messages.find(x => x.id === this.editId);
|
|
||||||
this.onSaveButton(message);
|
|
||||||
}
|
|
||||||
if (event.key === 'Escape') {
|
|
||||||
this.onCancelUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the current form action
|
|
||||||
*/
|
|
||||||
public onCancelUpdate(): void {
|
|
||||||
this.editId = null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ import { NgModule } from '@angular/core';
|
|||||||
import { Routes, RouterModule } from '@angular/router';
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
||||||
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
||||||
import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component';
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -13,10 +12,6 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'detail/:id',
|
path: 'detail/:id',
|
||||||
component: ProjectorDetailComponent
|
component: ProjectorDetailComponent
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'messages',
|
|
||||||
component: ProjectorMessageListComponent
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -5,19 +5,21 @@ import { ProjectorRoutingModule } from './projector-routing.module';
|
|||||||
import { SharedModule } from '../../shared/shared.module';
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
||||||
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
||||||
import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component';
|
|
||||||
import { CountdownControlsComponent } from './components/countdown-controls/countdown-controls.component';
|
import { CountdownControlsComponent } from './components/countdown-controls/countdown-controls.component';
|
||||||
import { CountdownDialogComponent } from './components/countdown-dialog/countdown-dialog.component';
|
import { CountdownDialogComponent } from './components/countdown-dialog/countdown-dialog.component';
|
||||||
|
import { MessageControlsComponent } from './components/message-controls/message-controls.component';
|
||||||
|
import { MessageDialogComponent } from './components/message-dialog/message-dialog.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, ProjectorRoutingModule, SharedModule],
|
imports: [CommonModule, ProjectorRoutingModule, SharedModule],
|
||||||
declarations: [
|
declarations: [
|
||||||
ProjectorListComponent,
|
ProjectorListComponent,
|
||||||
ProjectorDetailComponent,
|
ProjectorDetailComponent,
|
||||||
ProjectorMessageListComponent,
|
|
||||||
CountdownControlsComponent,
|
CountdownControlsComponent,
|
||||||
CountdownDialogComponent
|
CountdownDialogComponent,
|
||||||
|
MessageControlsComponent,
|
||||||
|
MessageDialogComponent
|
||||||
],
|
],
|
||||||
entryComponents: [CountdownDialogComponent]
|
entryComponents: [CountdownDialogComponent, MessageDialogComponent]
|
||||||
})
|
})
|
||||||
export class ProjectorModule {}
|
export class ProjectorModule {}
|
||||||
|
Loading…
Reference in New Issue
Block a user