migrate angular 21
This commit is contained in:
@@ -1,14 +1,27 @@
|
||||
<ng-container *ngIf="song">
|
||||
<p *ngIf="song.artist">{{ song.artist }}</p>
|
||||
<p *ngIf="song.label">{{ song.label }}</p>
|
||||
<p *ngIf="song.termsOfUse" class="terms-of-use">{{ song.termsOfUse }}</p>
|
||||
<p *ngIf="song.origin">{{ song.origin }}</p>
|
||||
|
||||
<div *ngIf="song.legalOwnerId">
|
||||
<p *ngIf="song.legalOwner === 'CCLI' && config">
|
||||
CCLI-Liednummer {{ song.legalOwnerId }}, CCLI-Lizenznummer
|
||||
{{ config.ccliLicenseId }}
|
||||
</p>
|
||||
<p *ngIf="song.legalOwner !== 'CCLI'">Liednummer {{ song.legalOwnerId }}</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
@if (song) {
|
||||
@if (song.artist) {
|
||||
<p>{{ song.artist }}</p>
|
||||
}
|
||||
@if (song.label) {
|
||||
<p>{{ song.label }}</p>
|
||||
}
|
||||
@if (song.termsOfUse) {
|
||||
<p class="terms-of-use">{{ song.termsOfUse }}</p>
|
||||
}
|
||||
@if (song.origin) {
|
||||
<p>{{ song.origin }}</p>
|
||||
}
|
||||
@if (song.legalOwnerId) {
|
||||
<div>
|
||||
@if (song.legalOwner === 'CCLI' && config) {
|
||||
<p>
|
||||
CCLI-Liednummer {{ song.legalOwnerId }}, CCLI-Lizenznummer
|
||||
{{ config.ccliLicenseId }}
|
||||
</p>
|
||||
}
|
||||
@if (song.legalOwner !== 'CCLI') {
|
||||
<p>Liednummer {{ song.legalOwnerId }}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {Song} from '../../../songs/services/song';
|
||||
import {Config} from '../../../../services/config';
|
||||
import {NgIf} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-legal',
|
||||
templateUrl: './legal.component.html',
|
||||
styleUrls: ['./legal.component.less'],
|
||||
imports: [NgIf],
|
||||
imports: [],
|
||||
})
|
||||
export class LegalComponent {
|
||||
@Input() public song: Song | null = null;
|
||||
|
||||
@@ -1,48 +1,50 @@
|
||||
<div class="fullscreen background"></div>
|
||||
<div *ngIf="showType" [style.font-size.px]="zoom" class="fullscreen background">
|
||||
|
||||
<div [class.visible]="presentationBackground==='blue'" class="bg-blue fullscreen bg-image"></div>
|
||||
<div [class.visible]="presentationBackground==='green'" class="bg-green fullscreen bg-image"></div>
|
||||
<div [class.visible]="presentationBackground==='leder'" class="bg-leder fullscreen bg-image"></div>
|
||||
<div [class.visible]="presentationBackground==='praise'" class="bg-praise fullscreen bg-image"></div>
|
||||
<div [class.visible]="presentationBackground==='bible'" class="bg-bible fullscreen bg-image"></div>
|
||||
|
||||
|
||||
<div
|
||||
[@songSwitch]="songId"
|
||||
[class.blur]="songId === 'title' || songId === 'dynamicText'"
|
||||
[class.hide]="songId !== 'title' && songId !== 'empty' && songId !== 'dynamicText'"
|
||||
[class.no-logo]="presentationBackground!=='none'"
|
||||
class="start fullscreen logo"
|
||||
>
|
||||
<app-logo></app-logo>
|
||||
@if (showType) {
|
||||
<div [style.font-size.px]="zoom" class="fullscreen background">
|
||||
<div [class.visible]="presentationBackground==='blue'" class="bg-blue fullscreen bg-image"></div>
|
||||
<div [class.visible]="presentationBackground==='green'" class="bg-green fullscreen bg-image"></div>
|
||||
<div [class.visible]="presentationBackground==='leder'" class="bg-leder fullscreen bg-image"></div>
|
||||
<div [class.visible]="presentationBackground==='praise'" class="bg-praise fullscreen bg-image"></div>
|
||||
<div [class.visible]="presentationBackground==='bible'" class="bg-bible fullscreen bg-image"></div>
|
||||
<div
|
||||
[@songSwitch]="songId"
|
||||
[class.blur]="songId === 'title' || songId === 'dynamicText'"
|
||||
[class.hide]="songId !== 'title' && songId !== 'empty' && songId !== 'dynamicText'"
|
||||
[class.no-logo]="presentationBackground!=='none'"
|
||||
class="start fullscreen logo"
|
||||
>
|
||||
<app-logo></app-logo>
|
||||
</div>
|
||||
@if (songId === 'title') {
|
||||
<div @songSwitch class="start fullscreen">
|
||||
<div>{{ showType | showType }}</div>
|
||||
<div class="date">{{ date | date: "dd.MM.yyyy" }}</div>
|
||||
</div>
|
||||
}
|
||||
@if (songId === 'dynamicText') {
|
||||
<div @songSwitch class="start fullscreen dynamic-text">
|
||||
<div>{{ presentationDynamicCaption }}</div>
|
||||
<div class="date">{{ presentationDynamicText }}</div>
|
||||
</div>
|
||||
}
|
||||
@if (song && songId !== 'title' && songId !== 'empty' && songId !== 'dynamicText') {
|
||||
<app-song-text
|
||||
[@songSwitch]="songId"
|
||||
[fullscreen]="true"
|
||||
[header]="song.title"
|
||||
[index]="index??0"
|
||||
[showComments]="false"
|
||||
[showSwitch]="false"
|
||||
[text]="song.text"
|
||||
chordMode="hide"
|
||||
></app-song-text>
|
||||
}
|
||||
@if (song && songId !== 'title' && songId !== 'empty' && songId !== 'dynamicText') {
|
||||
<app-legal
|
||||
[@songSwitch]="songId"
|
||||
[config]="config$ | async"
|
||||
[song]="song"
|
||||
></app-legal>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div *ngIf="songId === 'title'" @songSwitch class="start fullscreen">
|
||||
<div>{{ showType | showType }}</div>
|
||||
<div class="date">{{ date | date: "dd.MM.yyyy" }}</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="songId === 'dynamicText'" @songSwitch class="start fullscreen dynamic-text">
|
||||
<div>{{ presentationDynamicCaption }}</div>
|
||||
<div class="date">{{ presentationDynamicText }}</div>
|
||||
</div>
|
||||
|
||||
<app-song-text
|
||||
*ngIf="song && songId !== 'title' && songId !== 'empty' && songId !== 'dynamicText'"
|
||||
[@songSwitch]="songId"
|
||||
[fullscreen]="true"
|
||||
[header]="song.title"
|
||||
[index]="index??0"
|
||||
[showComments]="false"
|
||||
[showSwitch]="false"
|
||||
[text]="song.text"
|
||||
chordMode="hide"
|
||||
></app-song-text>
|
||||
<app-legal
|
||||
*ngIf="song && songId !== 'title' && songId !== 'empty' && songId !== 'dynamicText'"
|
||||
[@songSwitch]="songId"
|
||||
[config]="config$ | async"
|
||||
[song]="song"
|
||||
></app-legal>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {PresentationBackground, Show} from '../../shows/services/show';
|
||||
import {ShowSong} from '../../shows/services/show-song';
|
||||
import {ShowSongService} from '../../shows/services/show-song.service';
|
||||
import {openFullscreen} from '../../../services/fullscreen';
|
||||
import {AsyncPipe, DatePipe, NgIf} from '@angular/common';
|
||||
import {AsyncPipe, DatePipe} from '@angular/common';
|
||||
import {LogoComponent} from './logo/logo.component';
|
||||
import {SongTextComponent} from '../../../widget-modules/components/song-text/song-text.component';
|
||||
import {LegalComponent} from './legal/legal.component';
|
||||
@@ -22,7 +22,7 @@ import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/s
|
||||
templateUrl: './monitor.component.html',
|
||||
styleUrls: ['./monitor.component.less'],
|
||||
animations: [songSwitch],
|
||||
imports: [NgIf, LogoComponent, SongTextComponent, LegalComponent, AsyncPipe, DatePipe, ShowTypePipe],
|
||||
imports: [LogoComponent, SongTextComponent, LegalComponent, AsyncPipe, DatePipe, ShowTypePipe],
|
||||
})
|
||||
export class MonitorComponent implements OnInit, OnDestroy {
|
||||
public song: Song | null = null;
|
||||
@@ -50,26 +50,24 @@ export class MonitorComponent implements OnInit, OnDestroy {
|
||||
|
||||
public ngOnInit(): void {
|
||||
openFullscreen();
|
||||
const currentShowId$ = this.globalSettingsService.get$
|
||||
.pipe(
|
||||
filter((settings): settings is NonNullable<typeof settings> => !!settings),
|
||||
map(settings => settings.currentShow),
|
||||
filter((showId): showId is string => !!showId),
|
||||
distinctUntilChanged(),
|
||||
tap(_ => (this.currentShowId = _)),
|
||||
takeUntil(this.destroy$)
|
||||
);
|
||||
const currentShowId$ = this.globalSettingsService.get$.pipe(
|
||||
filter((settings): settings is NonNullable<typeof settings> => !!settings),
|
||||
map(settings => settings.currentShow),
|
||||
filter((showId): showId is string => !!showId),
|
||||
distinctUntilChanged(),
|
||||
tap(_ => (this.currentShowId = _)),
|
||||
takeUntil(this.destroy$)
|
||||
);
|
||||
|
||||
const show$ = currentShowId$
|
||||
.pipe(
|
||||
switchMap(showId => this.showService.read$(showId)),
|
||||
filter((show): show is Show => !!show),
|
||||
shareReplay({
|
||||
bufferSize: 1,
|
||||
refCount: true,
|
||||
}),
|
||||
takeUntil(this.destroy$)
|
||||
);
|
||||
const show$ = currentShowId$.pipe(
|
||||
switchMap(showId => this.showService.read$(showId)),
|
||||
filter((show): show is Show => !!show),
|
||||
shareReplay({
|
||||
bufferSize: 1,
|
||||
refCount: true,
|
||||
}),
|
||||
takeUntil(this.destroy$)
|
||||
);
|
||||
|
||||
show$
|
||||
.pipe(
|
||||
|
||||
@@ -1,126 +1,128 @@
|
||||
<div *ngIf="show" @fade>
|
||||
<app-card [closeIcon]="faIcon" [heading]="show.showType | showType"
|
||||
[subheading]="show.date.toDate() | date:'dd.MM.yyyy'" closeLink="/presentation/select">
|
||||
|
||||
<ng-container *ngIf="!progress">
|
||||
<div class="song">
|
||||
<div *ngIf="show" class="song-parts">
|
||||
<div
|
||||
(click)="onSectionClick('title', -1, show.id)"
|
||||
[class.active]="show.presentationSongId === 'title'"
|
||||
class="song-part"
|
||||
>
|
||||
<div class="head">Veranstaltung</div>
|
||||
</div>
|
||||
<div
|
||||
(click)="onSectionClick('empty', -1, show.id)"
|
||||
[class.active]="show.presentationSongId === 'empty'"
|
||||
class="song-part"
|
||||
>
|
||||
<div class="head">Leer</div>
|
||||
</div>
|
||||
@if (show) {
|
||||
<div @fade>
|
||||
<app-card [closeIcon]="faIcon" [heading]="show.showType | showType"
|
||||
[subheading]="show.date.toDate() | date:'dd.MM.yyyy'" closeLink="/presentation/select">
|
||||
@if (!progress) {
|
||||
<div class="song">
|
||||
@if (show) {
|
||||
<div class="song-parts">
|
||||
<div
|
||||
(click)="onSectionClick('title', -1, show.id)"
|
||||
[class.active]="show.presentationSongId === 'title'"
|
||||
class="song-part"
|
||||
>
|
||||
<div class="head">Veranstaltung</div>
|
||||
</div>
|
||||
<div
|
||||
(click)="onSectionClick('empty', -1, show.id)"
|
||||
[class.active]="show.presentationSongId === 'empty'"
|
||||
class="song-part"
|
||||
>
|
||||
<div class="head">Leer</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let song of presentationSongs; trackBy: trackBy" class="song">
|
||||
<div *ngIf="show"
|
||||
[class.active]="show.presentationSongId === song.id"
|
||||
class="title song-part"
|
||||
>
|
||||
<div (click)="onSectionClick(song.id, -1, show.id)" class="head">
|
||||
{{ song.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="show" class="song-parts">
|
||||
<div
|
||||
(click)="onSectionClick(song.id, i, show.id)"
|
||||
*ngFor="let section of song.sections; index as i"
|
||||
@for (song of presentationSongs; track trackBy($index, song)) {
|
||||
<div class="song">
|
||||
@if (show) {
|
||||
<div
|
||||
[class.active]="show.presentationSongId === song.id"
|
||||
class="title song-part"
|
||||
>
|
||||
<div (click)="onSectionClick(song.id, -1, show.id)" class="head">
|
||||
{{ song.title }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (show) {
|
||||
<div class="song-parts">
|
||||
@for (section of song.sections; track section; let i = $index) {
|
||||
<div
|
||||
(click)="onSectionClick(song.id, i, show.id)"
|
||||
[class.active]="
|
||||
show.presentationSongId === song.id &&
|
||||
show.presentationSection === i
|
||||
"
|
||||
class="song-part"
|
||||
>
|
||||
<div class="head">
|
||||
{{ section.type | sectionType }} {{ section.number + 1 }}
|
||||
class="song-part"
|
||||
>
|
||||
<div class="head">
|
||||
{{ section.type | sectionType }} {{ section.number + 1 }}
|
||||
</div>
|
||||
<div class="fragment">{{ getFirstLine(section) }}</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="song">
|
||||
@if (show) {
|
||||
<div
|
||||
[class.active]="show.presentationSongId === 'dynamicText'"
|
||||
class="title song-part"
|
||||
>
|
||||
<div (click)="onSectionClick('dynamicText', -1, show.id)" class="head">
|
||||
Freier Text
|
||||
</div>
|
||||
</div>
|
||||
<div class="fragment">{{ getFirstLine(section) }}</div>
|
||||
</div>
|
||||
}
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Überschrift</mat-label>
|
||||
<input (ngModelChange)="onDynamicCaption($event, show.id)" [ngModel]="show.presentationDynamicCaption"
|
||||
autocomplete="off" id="dynamic-caption"
|
||||
matInput
|
||||
type="text">
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Text</mat-label>
|
||||
<textarea (ngModelChange)="onDynamicText($event, show.id)" [ngModel]="show.presentationDynamicText"
|
||||
autocomplete="off" id="dynamic-text"
|
||||
matInput></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="song">
|
||||
<div *ngIf="show"
|
||||
[class.active]="show.presentationSongId === 'dynamicText'"
|
||||
class="title song-part"
|
||||
>
|
||||
<div (click)="onSectionClick('dynamicText', -1, show.id)" class="head">
|
||||
Freier Text
|
||||
@if (show) {
|
||||
<div class="div-bottom">
|
||||
<button class="btn-start-presentation" mat-button routerLink="/presentation/monitor">
|
||||
<fa-icon [icon]="faDesktop"></fa-icon>
|
||||
Präsentation starten
|
||||
</button>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Hintergrund</mat-label>
|
||||
<mat-select
|
||||
(ngModelChange)="onBackground($event, show.id)"
|
||||
[ngModel]="show.presentationBackground">
|
||||
<mat-option value="none">kein Hintergrund</mat-option>
|
||||
<mat-option value="blue">Sternenhimmel</mat-option>
|
||||
<mat-option value="green">Blätter</mat-option>
|
||||
<mat-option value="leder">Leder</mat-option>
|
||||
<mat-option value="praise">Lobpreis</mat-option>
|
||||
<mat-option value="bible">Bibel</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-slider
|
||||
#slider
|
||||
[max]="100"
|
||||
[min]="10"
|
||||
[step]="2"
|
||||
class="zoom-slider"
|
||||
color="primary"
|
||||
ngDefaultControl
|
||||
><input (ngModelChange)="onZoom($event, show.id)"
|
||||
[ngModel]="show.presentationZoom"
|
||||
matSliderThumb>
|
||||
</mat-slider>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Überschrift</mat-label>
|
||||
<input (ngModelChange)="onDynamicCaption($event, show.id)" [ngModel]="show.presentationDynamicCaption"
|
||||
autocomplete="off" id="dynamic-caption"
|
||||
matInput
|
||||
type="text">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Text</mat-label>
|
||||
<textarea (ngModelChange)="onDynamicText($event, show.id)" [ngModel]="show.presentationDynamicText"
|
||||
autocomplete="off" id="dynamic-text"
|
||||
matInput></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
|
||||
<div *ngIf="show" class="div-bottom">
|
||||
|
||||
<button class="btn-start-presentation" mat-button routerLink="/presentation/monitor">
|
||||
<fa-icon [icon]="faDesktop"></fa-icon>
|
||||
Präsentation starten
|
||||
</button>
|
||||
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Hintergrund</mat-label>
|
||||
<mat-select
|
||||
(ngModelChange)="onBackground($event, show.id)"
|
||||
[ngModel]="show.presentationBackground">
|
||||
<mat-option value="none">kein Hintergrund</mat-option>
|
||||
<mat-option value="blue">Sternenhimmel</mat-option>
|
||||
<mat-option value="green">Blätter</mat-option>
|
||||
<mat-option value="leder">Leder</mat-option>
|
||||
<mat-option value="praise">Lobpreis</mat-option>
|
||||
<mat-option value="bible">Bibel</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-slider
|
||||
#slider
|
||||
[max]="100"
|
||||
[min]="10"
|
||||
[step]="2"
|
||||
class="zoom-slider"
|
||||
color="primary"
|
||||
ngDefaultControl
|
||||
><input (ngModelChange)="onZoom($event, show.id)"
|
||||
[ngModel]="show.presentationZoom"
|
||||
matSliderThumb>
|
||||
</mat-slider>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<app-add-song
|
||||
*ngIf="show"
|
||||
[addedLive]="true"
|
||||
[showSongs]="showSongs"
|
||||
[show]="show"
|
||||
[songs]="songs$|async"
|
||||
></app-add-song>
|
||||
</ng-container>
|
||||
</app-card>
|
||||
</div>
|
||||
}
|
||||
@if (show) {
|
||||
<app-add-song
|
||||
[addedLive]="true"
|
||||
[showSongs]="showSongs"
|
||||
[show]="show"
|
||||
[songs]="songs$|async"
|
||||
></app-add-song>
|
||||
}
|
||||
}
|
||||
</app-card>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {fade} from '../../../animations';
|
||||
import {TextRenderingService} from '../../songs/services/text-rendering.service';
|
||||
import {Section} from '../../songs/services/section';
|
||||
import {LineType} from '../../songs/services/line-type';
|
||||
import {AsyncPipe, DatePipe, NgFor, NgIf} from '@angular/common';
|
||||
import {AsyncPipe, DatePipe} from '@angular/common';
|
||||
import {CardComponent} from '../../../widget-modules/components/card/card.component';
|
||||
import {MatFormField, MatLabel} from '@angular/material/form-field';
|
||||
import {MatInput} from '@angular/material/input';
|
||||
@@ -40,9 +40,7 @@ export interface PresentationSong {
|
||||
animations: [fade],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
NgIf,
|
||||
CardComponent,
|
||||
NgFor,
|
||||
MatFormField,
|
||||
MatLabel,
|
||||
MatInput,
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
<div *ngIf="shows$ | async as shows" @fade>
|
||||
<app-card *ngIf="visible" heading="Bitte eine Veranstaltung auswählen">
|
||||
<p *ngIf="!shows.length">
|
||||
Es ist derzeit keine Veranstaltung vorhanden
|
||||
</p>
|
||||
|
||||
<div *ngIf="shows.length>0" class="list">
|
||||
<button (click)="selectShow(show)" *ngFor="let show of shows" mat-stroked-button>
|
||||
<app-user-name [userId]="show.owner"></app-user-name>
|
||||
,
|
||||
{{ show.showType | showType }},
|
||||
{{ show.date.toDate() | date: "dd.MM.yyyy" }}
|
||||
</button>
|
||||
</div>
|
||||
</app-card>
|
||||
</div>
|
||||
@if (shows$ | async; as shows) {
|
||||
<div @fade>
|
||||
@if (visible) {
|
||||
<app-card heading="Bitte eine Veranstaltung auswählen">
|
||||
@if (!shows.length) {
|
||||
<p>
|
||||
Es ist derzeit keine Veranstaltung vorhanden
|
||||
</p>
|
||||
}
|
||||
@if (shows.length>0) {
|
||||
<div class="list">
|
||||
@for (show of shows; track show) {
|
||||
<button (click)="selectShow(show)" mat-stroked-button>
|
||||
<app-user-name [userId]="show.owner"></app-user-name>
|
||||
,
|
||||
{{ show.showType | showType }},
|
||||
{{ show.date.toDate() | date: "dd.MM.yyyy" }}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</app-card>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {Show} from '../../shows/services/show';
|
||||
import {GlobalSettingsService} from '../../../services/global-settings.service';
|
||||
import {Router} from '@angular/router';
|
||||
import {fade} from '../../../animations';
|
||||
import {AsyncPipe, DatePipe, NgFor, NgIf} from '@angular/common';
|
||||
import {AsyncPipe, DatePipe} from '@angular/common';
|
||||
import {CardComponent} from '../../../widget-modules/components/card/card.component';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
import {UserNameComponent} from '../../../services/user/user-name/user-name.component';
|
||||
@@ -16,7 +16,7 @@ import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/s
|
||||
templateUrl: './select.component.html',
|
||||
styleUrls: ['./select.component.less'],
|
||||
animations: [fade],
|
||||
imports: [NgIf, CardComponent, NgFor, MatButton, UserNameComponent, AsyncPipe, DatePipe, ShowTypePipe],
|
||||
imports: [CardComponent, MatButton, UserNameComponent, AsyncPipe, DatePipe, ShowTypePipe],
|
||||
})
|
||||
export class SelectComponent implements OnInit {
|
||||
public visible = false;
|
||||
|
||||
Reference in New Issue
Block a user