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
|
||||
*/
|
||||
@Input()
|
||||
public projector: Projector;
|
||||
public projector: Projector | null;
|
||||
|
||||
/**
|
||||
* The constructor
|
||||
@ -100,6 +100,9 @@ export class ProjectorButtonComponent implements OnInit {
|
||||
if (!this.object) {
|
||||
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;
|
||||
}
|
||||
|
||||
// could be in a shared scss
|
||||
.projector-button {
|
||||
grid-area: project;
|
||||
margin: auto 10px auto 0;
|
||||
@ -21,6 +22,7 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// could be in a shared scss
|
||||
.action-buttons {
|
||||
grid-area: buttons;
|
||||
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 { MessageControlsComponent } from './message-controls.component';
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
import { ProjectorMessageListComponent } from './projector-message-list.component';
|
||||
|
||||
describe('ProjectorMessageListComponent', () => {
|
||||
let component: ProjectorMessageListComponent;
|
||||
let fixture: ComponentFixture<ProjectorMessageListComponent>;
|
||||
describe('MessageControlsComponent', () => {
|
||||
let component: MessageControlsComponent;
|
||||
let fixture: ComponentFixture<MessageControlsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule],
|
||||
declarations: [ProjectorMessageListComponent]
|
||||
declarations: [MessageControlsComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProjectorMessageListComponent);
|
||||
fixture = TestBed.createComponent(MessageControlsComponent);
|
||||
component = fixture.componentInstance;
|
||||
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>
|
||||
</mat-expansion-panel-header>
|
||||
<mat-list>
|
||||
<mat-list-item
|
||||
*ngFor="let countdown of countdowns"
|
||||
class="larger-mat-list-item"
|
||||
[ngClass]="{ projected: isProjected(countdown) }"
|
||||
>
|
||||
<mat-list-item *ngFor="let countdown of countdowns" class="larger-mat-list-item">
|
||||
<os-countdown-controls
|
||||
class="dynamic-list-entry"
|
||||
[countdown]="countdown"
|
||||
@ -207,21 +203,25 @@
|
||||
</mat-expansion-panel>
|
||||
|
||||
<!-- messages -->
|
||||
<mat-expansion-panel *ngIf="messages.length">
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<span translate>Messages</span>
|
||||
</mat-expansion-panel-header>
|
||||
<mat-list>
|
||||
<mat-list-item *ngFor="let message of messages" [ngClass]="{ projected: isProjected(message) }">
|
||||
<button type="button" mat-icon-button (click)="project(message)">
|
||||
<mat-icon>videocam</mat-icon>
|
||||
</button>
|
||||
<span>{{ message.getPreview(40) }}</span>
|
||||
<mat-list-item *ngFor="let message of messages" class="larger-mat-list-item">
|
||||
<os-message-controls
|
||||
class="dynamic-list-entry"
|
||||
[message]="message"
|
||||
[projector]="projector.projector"
|
||||
(editEvent)="openMessagesDialog($event)"
|
||||
>
|
||||
</os-message-controls>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
<mat-action-row>
|
||||
<button type="button" mat-icon-button routerLink="/projectors/messages">
|
||||
<mat-icon>edit</mat-icon>
|
||||
<button type="button" mat-button (click)="openMessagesDialog()">
|
||||
<mat-icon>add</mat-icon>
|
||||
<span translate>Add message</span>
|
||||
</button>
|
||||
</mat-action-row>
|
||||
</mat-expansion-panel>
|
||||
|
@ -123,7 +123,7 @@
|
||||
}
|
||||
|
||||
.larger-mat-list-item {
|
||||
height: 90px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.larger-mat-list-item + .larger-mat-list-item {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { E2EImportsModule } from '../../../../../e2e-imports.module';
|
||||
import { ProjectorDetailComponent } from './projector-detail.component';
|
||||
import { ProjectorModule } from '../../projector.module';
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
describe('ProjectorDetailComponent', () => {
|
||||
let component: ProjectorDetailComponent;
|
||||
|
@ -18,13 +18,15 @@ import { CurrentSpeakerChyronSlideService } from '../../services/current-speaker
|
||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||
import { ProjectorService } from 'app/core/core-services/projector.service';
|
||||
import { moveItemInArray, CdkDragDrop } from '@angular/cdk/drag-drop';
|
||||
import { Projectable } from 'app/site/base/projectable';
|
||||
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 { CountdownRepositoryService } from 'app/core/repositories/projector/countdown-repository.service';
|
||||
import { ProjectorMessageRepositoryService } from 'app/core/repositories/projector/projector-message-repository.service';
|
||||
import { ViewProjectorMessage } from 'app/site/projector/models/view-projector-message';
|
||||
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.
|
||||
@ -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
|
||||
*/
|
||||
@ -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
|
||||
*
|
||||
@ -234,4 +267,22 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
||||
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">
|
||||
<h2 translate>Projectors</h2>
|
||||
</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>
|
||||
|
||||
<mat-card *ngIf="!projectorToCreate && projectors && projectors.length > 1">
|
||||
@ -172,11 +163,4 @@
|
||||
</ng-container>
|
||||
</os-meta-text-block>
|
||||
</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>
|
||||
</div>
|
@ -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 { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
||||
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
||||
import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -13,10 +12,6 @@ const routes: Routes = [
|
||||
{
|
||||
path: 'detail/:id',
|
||||
component: ProjectorDetailComponent
|
||||
},
|
||||
{
|
||||
path: 'messages',
|
||||
component: ProjectorMessageListComponent
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -5,19 +5,21 @@ import { ProjectorRoutingModule } from './projector-routing.module';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { ProjectorListComponent } from './components/projector-list/projector-list.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 { 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({
|
||||
imports: [CommonModule, ProjectorRoutingModule, SharedModule],
|
||||
declarations: [
|
||||
ProjectorListComponent,
|
||||
ProjectorDetailComponent,
|
||||
ProjectorMessageListComponent,
|
||||
CountdownControlsComponent,
|
||||
CountdownDialogComponent
|
||||
CountdownDialogComponent,
|
||||
MessageControlsComponent,
|
||||
MessageDialogComponent
|
||||
],
|
||||
entryComponents: [CountdownDialogComponent]
|
||||
entryComponents: [CountdownDialogComponent, MessageDialogComponent]
|
||||
})
|
||||
export class ProjectorModule {}
|
||||
|
Loading…
Reference in New Issue
Block a user