update tslint -> eslint

This commit is contained in:
2021-05-21 20:17:26 +02:00
parent 80260df71f
commit a195fafa6b
252 changed files with 3080 additions and 2420 deletions

View File

@@ -1,26 +1,29 @@
<app-card heading="Angehängte Dateien">
<div *ngIf="currentUpload">
<div class="progress">
<div [ngStyle]="{ 'width': currentUpload?.progress + '%' }" class="progress-bar progress-bar-animated"></div>
<div
[ngStyle]="{ width: currentUpload?.progress + '%' }"
class="progress-bar progress-bar-animated"
></div>
</div>
Progress: {{currentUpload?.name}} | {{currentUpload?.progress}}% Complete
Progress: {{ currentUpload?.name }} | {{ currentUpload?.progress }}%
Complete
</div>
<div class="upload">
<label>
<input (change)="detectFiles($event)" type="file">
<input (change)="detectFiles($event)" type="file"/>
</label>
<button (click)="uploadSingle()"
[disabled]="!selectedFiles"
mat-icon-button>
<button
(click)="uploadSingle()"
[disabled]="!selectedFiles"
mat-icon-button
>
<mat-icon>cloud_upload</mat-icon>
</button>
</div>
<p *ngFor="let file of (files$|async)">
<p *ngFor="let file of files$ | async">
<app-file [file]="file" [songId]="songId"></app-file>
</p>
</app-card>

View File

@@ -6,12 +6,13 @@ describe('EditFileComponent', () => {
let component: EditFileComponent;
let fixture: ComponentFixture<EditFileComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [EditFileComponent]
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [EditFileComponent],
}).compileComponents();
})
.compileComponents();
}));
);
beforeEach(() => {
fixture = TestBed.createComponent(EditFileComponent);
@@ -20,6 +21,6 @@ describe('EditFileComponent', () => {
});
it('should create', () => {
expect(component).toBeTruthy();
void expect(component).toBeTruthy();
});
});

View File

@@ -10,41 +10,33 @@ import {File} from '../../../services/file';
@Component({
selector: 'app-edit-file',
templateUrl: './edit-file.component.html',
styleUrls: ['./edit-file.component.less']
styleUrls: ['./edit-file.component.less'],
})
export class EditFileComponent {
public selectedFiles: FileList;
public currentUpload: Upload;
public songId: string;
public files$: Observable<File[]>;
constructor(
private activatedRoute: ActivatedRoute,
private uploadService: UploadService,
private fileService: FileDataService,
) {
this.activatedRoute.params.pipe(
map(param => param.songId),
).subscribe(songId => {
public constructor(private activatedRoute: ActivatedRoute, private uploadService: UploadService, private fileService: FileDataService) {
this.activatedRoute.params.pipe(map((param: {songId: string}) => param.songId)).subscribe(songId => {
this.songId = songId;
});
this.files$ = this.activatedRoute.params.pipe(
map(param => param.songId),
map((param: {songId: string}) => param.songId),
switchMap(songId => this.fileService.read$(songId))
);
}
detectFiles(event) {
this.selectedFiles = event.target.files;
public detectFiles(event: Event): void {
const target = event.target as HTMLInputElement;
this.selectedFiles = target.files;
}
public async uploadSingle() {
public uploadSingle(): void {
const file = this.selectedFiles.item(0);
this.currentUpload = new Upload(file as any);
await this.uploadService.pushUpload(this.songId, this.currentUpload);
this.currentUpload = new Upload(file);
this.uploadService.pushUpload(this.songId, this.currentUpload);
}
}

View File

@@ -2,4 +2,4 @@
<fa-icon [icon]="faTrash"></fa-icon>
</button>
<a [href]="url$|async" target="_blank">{{name}}</a>
<a [href]="url$ | async" target="_blank">{{ name }}</a>

View File

@@ -6,12 +6,13 @@ describe('FileComponent', () => {
let component: FileComponent;
let fixture: ComponentFixture<FileComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [FileComponent]
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [FileComponent],
}).compileComponents();
})
.compileComponents();
}));
);
beforeEach(() => {
fixture = TestBed.createComponent(FileComponent);
@@ -20,6 +21,6 @@ describe('FileComponent', () => {
});
it('should create', () => {
expect(component).toBeTruthy();
void expect(component).toBeTruthy();
});
});

View File

@@ -7,20 +7,20 @@ import {FileService} from '../../../../services/file.service';
@Component({
selector: 'app-file',
templateUrl: './file.component.html',
styleUrls: ['./file.component.less']
styleUrls: ['./file.component.less'],
})
export class FileComponent {
public url$: Observable<string>;
public name: string;
public faTrash = faTrashAlt;
@Input() songId: string;
@Input() public songId: string;
private fileId: string;
private path: string;
constructor(private fileService: FileService) {
}
public constructor(private fileService: FileService) {}
@Input() set file(file: File) {
@Input()
public set file(file: File) {
this.url$ = this.fileService.getDownloadUrl(file.path + '/' + file.name);
this.name = file.name;
this.fileId = file.id;

View File

@@ -6,11 +6,11 @@ describe('EditSongGuard', () => {
let guard: EditSongGuard;
beforeEach(() => {
TestBed.configureTestingModule({});
void TestBed.configureTestingModule({});
guard = TestBed.inject(EditSongGuard);
});
it('should be created', () => {
expect(guard).toBeTruthy();
void expect(guard).toBeTruthy();
});
});

View File

@@ -4,16 +4,15 @@ import {Observable} from 'rxjs';
import {EditComponent} from './edit.component';
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class EditSongGuard implements CanDeactivate<unknown> {
canDeactivate(
public canDeactivate(
component: EditComponent,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
nextState?: RouterStateSnapshot
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return component.editSongComponent.askForSave(nextState);
}
}

View File

@@ -1,48 +1,58 @@
<app-card *ngIf="song" [heading]="song.number + ' bearbeiten'" closeLink="../">
<form [formGroup]="form" class="form">
<mat-form-field appearance="outline">
<mat-label>Titel</mat-label>
<input formControlName="title" matInput>
<input formControlName="title" matInput/>
</mat-form-field>
<div class="fourth">
<mat-form-field appearance="outline">
<mat-label>Typ</mat-label>
<mat-select formControlName="type">
<mat-option *ngFor="let type of types" [value]="type">{{type | songType}}</mat-option>
<mat-option *ngFor="let type of types" [value]="type">{{
type | songType
}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Tonart</mat-label>
<mat-select formControlName="key">
<mat-option *ngFor="let key of keys" [value]="key">{{key|key}}</mat-option>
<mat-option *ngFor="let key of keys" [value]="key">{{
key | key
}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Tempo</mat-label>
<input formControlName="tempo" matInput>
<input formControlName="tempo" matInput/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Status</mat-label>
<mat-select formControlName="status">
<mat-option *ngFor="let status of status" [value]="status">{{status | status}}</mat-option>
<mat-option *ngFor="let status of status" [value]="status">{{
status | status
}}</mat-option>
</mat-select>
</mat-form-field>
</div>
<mat-form-field appearance="outline">
<mat-label>Songtext</mat-label>
<textarea (focus)="songtextFocus=true" (focusout)="songtextFocus=false" [mat-autosize]="true"
formControlName="text" matInput></textarea>
<textarea
(focus)="songtextFocus = true"
(focusout)="songtextFocus = false"
[mat-autosize]="true"
formControlName="text"
matInput
></textarea>
</mat-form-field>
<div *ngIf="songtextFocus" class="song-text-help">
<h3>Vorschau</h3>
<app-song-text [text]="form.value.text" chordMode="show"></app-song-text>
<h3>Hinweise zur Bearbeitung</h3>
<h4>Aufbau</h4>
Der Liedtext wird hintereinander weg geschrieben. Dabei werden Strophen, Refrain und Bridge jeweils durch
eine zusätzliche Zeile Markiert. z.B.
Der Liedtext wird hintereinander weg geschrieben. Dabei werden Strophen,
Refrain und Bridge jeweils durch eine zusätzliche Zeile Markiert. z.B.
<pre>
Strophe
Text der ersten Strophe
@@ -54,9 +64,10 @@
Und hier der Refrain
</pre>
<h3>Akkorde</h3>
Die Akktorde werden jeweils in der Zeile über dem jeweiligen Liedtext geschrieben.
Sie werden jeweils durch Leerzeichen an die entsprechende Position gebracht.
Bitte keine Tabulatoren verwenden! Folgende Schreibweisen sind erlaubt:
Die Akktorde werden jeweils in der Zeile über dem jeweiligen Liedtext
geschrieben. Sie werden jeweils durch Leerzeichen an die entsprechende
Position gebracht. Bitte keine Tabulatoren verwenden! Folgende
Schreibweisen sind erlaubt:
<pre>
Dur: C D E
Moll: c d e
@@ -76,21 +87,31 @@
<mat-form-field appearance="outline">
<mat-label>Kommentar</mat-label>
<textarea [mat-autosize]="true" formControlName="comment" matInput></textarea>
<textarea
[mat-autosize]="true"
formControlName="comment"
matInput
></textarea>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-chip-list #chipList>
<mat-chip (removed)="removeFlag(flag)" *ngFor="let flag of flags"
[removable]="true" [selectable]="false">
{{flag}}&nbsp;
<mat-chip
(removed)="removeFlag(flag)"
*ngFor="let flag of flags"
[removable]="true"
[selectable]="false"
>
{{ flag }}&nbsp;
<fa-icon (click)="removeFlag(flag)" [icon]="faRemove"></fa-icon>
</mat-chip>
<input (matChipInputTokenEnd)="addFlag($event)"
[matChipInputAddOnBlur]="true"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
placeholder="Attribute">
<input
(matChipInputTokenEnd)="addFlag($event)"
[matChipInputAddOnBlur]="true"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
placeholder="Attribute"
/>
</mat-chip-list>
</mat-form-field>
@@ -98,54 +119,62 @@
<mat-form-field appearance="outline">
<mat-label>Rechtlicher Status</mat-label>
<mat-select formControlName="legalType">
<mat-option *ngFor="let key of legalType" [value]="key">{{key|legalType}}</mat-option>
<mat-option *ngFor="let key of legalType" [value]="key">{{
key | legalType
}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Rechteinhaber</mat-label>
<mat-select formControlName="legalOwner">
<mat-option *ngFor="let key of legalOwner" [value]="key">{{key|legalOwner}}</mat-option>
<mat-option *ngFor="let key of legalOwner" [value]="key">{{
key | legalOwner
}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Rechteinhaber ID (z.B. CCLI Liednummer)</mat-label>
<input formControlName="legalOwnerId" matInput>
<a *ngIf="form.value.legalOwner==='CCLI'" class="link-ccli"
href="https://songselect.ccli.com/Songs/{{form.value.legalOwnerId}}"
matSuffix
matTooltip="CCLI Link: https://songselect.ccli.com/Songs/{{form.value.legalOwnerId}}"
matTooltipPosition="before" target="_blank">
<input formControlName="legalOwnerId" matInput/>
<a
*ngIf="form.value.legalOwner === 'CCLI'"
class="link-ccli"
href="https://songselect.ccli.com/Songs/{{ form.value.legalOwnerId }}"
matSuffix
matTooltip="CCLI Link: https://songselect.ccli.com/Songs/{{
form.value.legalOwnerId
}}"
matTooltipPosition="before"
target="_blank"
>
<fa-icon [icon]="faLink"></fa-icon>
</a>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Künstler</mat-label>
<input formControlName="artist" matInput>
<input formControlName="artist" matInput/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Verlag</mat-label>
<input formControlName="label" matInput>
<input formControlName="label" matInput/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Nutzungsbedingungen</mat-label>
<input formControlName="termsOfUse" matInput>
<input formControlName="termsOfUse" matInput/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>abweichende Quelle</mat-label>
<input formControlName="origin" matInput>
<input formControlName="origin" matInput/>
</mat-form-field>
</div>
</form>
<app-button-row>
<app-button (click)="onSave()" [icon]="faSave">Speichern</app-button>
</app-button-row>
</app-card>

View File

@@ -6,12 +6,13 @@ describe('EditSongComponent', () => {
let component: EditSongComponent;
let fixture: ComponentFixture<EditSongComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [EditSongComponent]
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [EditSongComponent],
}).compileComponents();
})
.compileComponents();
}));
);
beforeEach(() => {
fixture = TestBed.createComponent(EditSongComponent);
@@ -20,6 +21,6 @@ describe('EditSongComponent', () => {
});
it('should create', () => {
expect(component).toBeTruthy();
void expect(component).toBeTruthy();
});
});

View File

@@ -17,7 +17,7 @@ import {SaveDialogComponent} from './save-dialog/save-dialog.component';
@Component({
selector: 'app-edit-song',
templateUrl: './edit-song.component.html',
styleUrls: ['./edit-song.component.less']
styleUrls: ['./edit-song.component.less'],
})
export class EditSongComponent implements OnInit {
public song: Song;
@@ -28,36 +28,31 @@ export class EditSongComponent implements OnInit {
public legalOwner = SongService.LEGAL_OWNER;
public legalType = SongService.LEGAL_TYPE;
public flags: string[] = [];
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
public readonly separatorKeysCodes: number[] = [ENTER, COMMA];
public faRemove = faTimesCircle;
public faSave = faSave;
public faLink = faExternalLinkAlt;
public songtextFocus = false;
constructor(
private activatedRoute: ActivatedRoute,
private songService: SongService,
private editService: EditService,
private router: Router,
public dialog: MatDialog
) {
}
public constructor(private activatedRoute: ActivatedRoute, private songService: SongService, private editService: EditService, private router: Router, public dialog: MatDialog) {}
public ngOnInit(): void {
this.activatedRoute.params.pipe(
map(param => param.songId),
switchMap(songId => this.songService.read$(songId)),
first()
).subscribe(song => {
this.song = song;
this.form = this.editService.createSongForm(song);
this.form.controls.flags.valueChanges.subscribe(_ => this.onFlagsChanged(_));
this.onFlagsChanged(this.form.controls.flags.value);
});
this.activatedRoute.params
.pipe(
map((param: {songId: string}) => param.songId),
switchMap(songId => this.songService.read$(songId)),
first()
)
.subscribe(song => {
this.song = song;
this.form = this.editService.createSongForm(song);
this.form.controls.flags.valueChanges.subscribe(_ => this.onFlagsChanged(_));
this.onFlagsChanged(this.form.controls.flags.value);
});
}
public async onSave(): Promise<void> {
const data = this.form.value;
const data = this.form.value as Partial<Song>;
await this.songService.update$(this.song.id, data);
this.form.markAsPristine();
await this.router.navigateByUrl('songs/' + this.song.id);
@@ -83,6 +78,22 @@ export class EditSongComponent implements OnInit {
}
}
public askForSave(nextState?: RouterStateSnapshot): boolean {
if (!this.form.dirty) {
return true;
}
const dialogRef = this.dialog.open(SaveDialogComponent, {
width: '350px',
});
dialogRef.afterClosed().subscribe((save: boolean) => {
void this.onSaveDialogAfterClosed(save, nextState.url).then();
});
return false;
}
private onFlagsChanged(flagArray: string): void {
if (!flagArray) {
this.flags = [];
@@ -92,30 +103,13 @@ export class EditSongComponent implements OnInit {
this.flags = flagArray.split(';').filter(_ => !!_);
}
public askForSave(nextState?: RouterStateSnapshot): boolean {
if (!this.form.dirty) {
return true;
}
const dialogRef = this.dialog.open(SaveDialogComponent, {
width: '350px'
});
dialogRef.afterClosed().subscribe((save: boolean) => {
this.onSaveDialogAfterClosed(save, nextState.url).then();
});
return false;
}
private async onSaveDialogAfterClosed(save: boolean, url: string) {
if (save) {
const data = this.form.value;
const data = this.form.value as Partial<Song>;
await this.songService.update$(this.song.id, data);
}
this.form.markAsPristine();
await this.router.navigateByUrl(url);
}
}

View File

@@ -4,5 +4,7 @@
</div>
<div mat-dialog-actions>
<button [mat-dialog-close]="false" mat-button>Änderungen verwerfen</button>
<button [mat-dialog-close]="true" cdkFocusInitial mat-button>Speichern</button>
<button [mat-dialog-close]="true" cdkFocusInitial mat-button>
Speichern
</button>
</div>

View File

@@ -6,12 +6,13 @@ describe('SaveDialogComponent', () => {
let component: SaveDialogComponent;
let fixture: ComponentFixture<SaveDialogComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [SaveDialogComponent]
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [SaveDialogComponent],
}).compileComponents();
})
.compileComponents();
}));
);
beforeEach(() => {
fixture = TestBed.createComponent(SaveDialogComponent);
@@ -20,6 +21,6 @@ describe('SaveDialogComponent', () => {
});
it('should create', () => {
expect(component).toBeTruthy();
void expect(component).toBeTruthy();
});
});

View File

@@ -1,16 +1,8 @@
import {Component, OnInit} from '@angular/core';
import {Component} from '@angular/core';
@Component({
selector: 'app-save-dialog',
templateUrl: './save-dialog.component.html',
styleUrls: ['./save-dialog.component.less']
styleUrls: ['./save-dialog.component.less'],
})
export class SaveDialogComponent implements OnInit {
constructor() {
}
ngOnInit(): void {
}
}
export class SaveDialogComponent {}

View File

@@ -6,12 +6,13 @@ describe('EditComponent', () => {
let component: EditComponent;
let fixture: ComponentFixture<EditComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [EditComponent]
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [EditComponent],
}).compileComponents();
})
.compileComponents();
}));
);
beforeEach(() => {
fixture = TestBed.createComponent(EditComponent);
@@ -20,6 +21,6 @@ describe('EditComponent', () => {
});
it('should create', () => {
expect(component).toBeTruthy();
void expect(component).toBeTruthy();
});
});

View File

@@ -4,7 +4,7 @@ import {EditSongComponent} from './edit-song/edit-song.component';
@Component({
selector: 'app-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.less']
styleUrls: ['./edit.component.less'],
})
export class EditComponent {
@ViewChild(EditSongComponent) public editSongComponent: EditSongComponent;

View File

@@ -27,7 +27,6 @@ import {MatDialogModule} from '@angular/material/dialog';
import {HistoryComponent} from './history/history.component';
import {SongTextModule} from '../../../../widget-modules/components/song-text/song-text.module';
@NgModule({
declarations: [EditComponent, EditSongComponent, EditFileComponent, FileComponent, SaveDialogComponent, HistoryComponent],
exports: [EditComponent],
@@ -56,8 +55,6 @@ import {SongTextModule} from '../../../../widget-modules/components/song-text/so
MatTooltipModule,
MatDialogModule,
SongTextModule,
]
],
})
export class EditModule {
}
export class EditModule {}

View File

@@ -3,10 +3,10 @@ import {TestBed} from '@angular/core/testing';
import {EditService} from './edit.service';
describe('EditService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
beforeEach(() => void TestBed.configureTestingModule({}));
it('should be created', () => {
const service: EditService = TestBed.get(EditService);
expect(service).toBeTruthy();
const service: EditService = TestBed.inject(EditService);
void expect(service).toBeTruthy();
});
});

View File

@@ -3,13 +3,9 @@ import {Song} from '../../services/song';
import {FormControl, FormGroup} from '@angular/forms';
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class EditService {
constructor() {
}
public createSongForm(song: Song): FormGroup {
return new FormGroup({
text: new FormControl(song.text),

View File

@@ -1,6 +1,6 @@
<app-card *ngIf="song && song.edits" heading="letzte Änderungen">
<div *ngFor="let edit of song.edits" class="list">
<div>{{edit.username}}</div>
<div>{{edit.timestamp.toDate()|date:'dd.MM.yyyy'}}</div>
<div>{{ edit.username }}</div>
<div>{{ edit.timestamp.toDate() | date: "dd.MM.yyyy" }}</div>
</div>
</app-card>

View File

@@ -6,12 +6,13 @@ describe('HistoryComponent', () => {
let component: HistoryComponent;
let fixture: ComponentFixture<HistoryComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [HistoryComponent]
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [HistoryComponent],
}).compileComponents();
})
.compileComponents();
}));
);
beforeEach(() => {
fixture = TestBed.createComponent(HistoryComponent);
@@ -20,6 +21,6 @@ describe('HistoryComponent', () => {
});
it('should create', () => {
expect(component).toBeTruthy();
void expect(component).toBeTruthy();
});
});

View File

@@ -7,25 +7,22 @@ import {Song} from '../../../services/song';
@Component({
selector: 'app-history',
templateUrl: './history.component.html',
styleUrls: ['./history.component.less']
styleUrls: ['./history.component.less'],
})
export class HistoryComponent implements OnInit {
public song: Song;
constructor(
private activatedRoute: ActivatedRoute,
private songService: SongService,
) {
}
public constructor(private activatedRoute: ActivatedRoute, private songService: SongService) {}
public ngOnInit(): void {
this.activatedRoute.params.pipe(
map(param => param.songId),
switchMap(songId => this.songService.read$(songId)),
first()
).subscribe(song => {
this.song = song;
});
this.activatedRoute.params
.pipe(
map((param: {songId: string}) => param.songId),
switchMap(songId => this.songService.read$(songId)),
first()
)
.subscribe(song => {
this.song = song;
});
}
}

View File

@@ -1,3 +1,3 @@
<a [href]="url$|async" target="_blank">
{{name}}
<a [href]="url$ | async" target="_blank">
{{ name }}
</a>

View File

@@ -6,12 +6,13 @@ describe('FileComponent', () => {
let component: FileComponent;
let fixture: ComponentFixture<FileComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [FileComponent]
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [FileComponent],
}).compileComponents();
})
.compileComponents();
}));
);
beforeEach(() => {
fixture = TestBed.createComponent(FileComponent);
@@ -20,6 +21,6 @@ describe('FileComponent', () => {
});
it('should create', () => {
expect(component).toBeTruthy();
void expect(component).toBeTruthy();
});
});

View File

@@ -1,4 +1,4 @@
import {Component, Input, OnInit} from '@angular/core';
import {Component, Input} from '@angular/core';
import {File} from '../../services/file';
import {AngularFireStorage} from '@angular/fire/storage';
import {Observable} from 'rxjs';
@@ -6,24 +6,18 @@ import {Observable} from 'rxjs';
@Component({
selector: 'app-file',
templateUrl: './file.component.html',
styleUrls: ['./file.component.less']
styleUrls: ['./file.component.less'],
})
export class FileComponent implements OnInit {
export class FileComponent {
public url$: Observable<string>;
public name: string;
constructor(private storage: AngularFireStorage) {
}
@Input() set file(file: File) {
public constructor(private storage: AngularFireStorage) {}
@Input()
public set file(file: File) {
const ref = this.storage.ref(file.path + '/' + file.name);
this.url$ = ref.getDownloadURL();
this.url$ = ref.getDownloadURL() as Observable<string>;
this.name = file.name;
}
ngOnInit(): void {
}
}

View File

@@ -1,13 +1,12 @@
<app-card closeLink="../" heading="Neues Lied">
<div [formGroup]="form" class="split">
<mat-form-field appearance="outline">
<mat-label>Nummer</mat-label>
<input formControlName="number" matInput>
<input formControlName="number" matInput/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Titel</mat-label>
<input autofocus formControlName="title" matInput>
<input autofocus formControlName="title" matInput/>
</mat-form-field>
</div>
@@ -15,4 +14,3 @@
<app-button (click)="onSave()" [icon]="faSave">Anlegen</app-button>
</app-button-row>
</app-card>

View File

@@ -6,12 +6,13 @@ describe('NewComponent', () => {
let component: NewComponent;
let fixture: ComponentFixture<NewComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [NewComponent]
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [NewComponent],
}).compileComponents();
})
.compileComponents();
}));
);
beforeEach(() => {
fixture = TestBed.createComponent(NewComponent);
@@ -20,6 +21,6 @@ describe('NewComponent', () => {
});
it('should create', () => {
expect(component).toBeTruthy();
void expect(component).toBeTruthy();
});
});

View File

@@ -10,14 +10,13 @@ import {Router} from '@angular/router';
@Component({
selector: 'app-new',
templateUrl: './new.component.html',
styleUrls: ['./new.component.less']
styleUrls: ['./new.component.less'],
})
export class NewComponent implements OnInit {
public faSave = faSave;
public form: FormGroup;
constructor(private songService: SongService, private router: Router) {
}
public constructor(private songService: SongService, private router: Router) {}
public ngOnInit(): void {
this.form = new FormGroup({
@@ -25,23 +24,27 @@ export class NewComponent implements OnInit {
title: new FormControl(null, Validators.required),
});
this.songService.list$().pipe(autoComplete(this)).subscribe(songs => {
const freeSongnumber = this.getFreeSongNumber(songs);
this.form.controls.number.setValue(freeSongnumber);
});
this.songService
.list$()
.pipe(autoComplete(this))
.subscribe(songs => {
const freeSongnumber = this.getFreeSongNumber(songs);
this.form.controls.number.setValue(freeSongnumber);
});
}
public async onSave(): Promise<void> {
const number = this.form.value.number;
const title = this.form.value.title;
const newSongId = await this.songService.new(number, title);
const value = this.form.value as {number: number; title: string};
const songNumber = value.number;
const title = value.title;
const newSongId = await this.songService.new(songNumber, title);
await this.router.navigateByUrl('/songs/' + newSongId + '/edit');
}
private getFreeSongNumber(songs: Song[]): Number {
const numbers = songs.map(_ => _.number);
private getFreeSongNumber(songs: Song[]): number {
const songNumber = songs.map(_ => _.number);
for (let i = 1; i < Number.MAX_SAFE_INTEGER; i++) {
if (!numbers.some(_ => _ === i)) {
if (!songNumber.some(_ => _ === i)) {
return i;
}
}

View File

@@ -9,19 +9,8 @@ import {ButtonRowModule} from '../../../../widget-modules/components/button-row/
import {ButtonModule} from '../../../../widget-modules/components/button/button.module';
import {AutofocusModule} from '../../../../widget-modules/directives/autofocus/autofocus.module';
@NgModule({
declarations: [NewComponent],
imports: [
CommonModule,
CardModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
ButtonRowModule,
ButtonModule,
AutofocusModule
]
imports: [CommonModule, CardModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, ButtonRowModule, ButtonModule, AutofocusModule],
})
export class NewModule {
}
export class NewModule {}

View File

@@ -1,47 +1,74 @@
<div class="split">
<app-card *ngIf="song$ | async as song" [heading]="song.number + ' - ' + song.title" closeLink="../">
<app-card
*ngIf="song$ | async as song"
[heading]="song.number + ' - ' + song.title"
closeLink="../"
>
<div class="song">
<div>
<div *appRole="['leader', 'contributor']" class="detail">
<div>Typ: {{song.type | songType}}</div>
<div>Tonart: {{song.key}}</div>
<div>Tempo: {{song.tempo}}</div>
<div>Status: {{(song.status|status) || 'entwurf'}}</div>
<div *ngIf="song.legalOwner">Rechteinhaber: {{song.legalOwner|legalOwner}}</div>
<div *ngIf="song.legalOwnerId && song.legalOwner==='CCLI'">
<a href="https://songselect.ccli.com/Songs/{{song.legalOwnerId}}" target="_blank">
CCLI Nummer: {{song.legalOwnerId}}
<div>Typ: {{ song.type | songType }}</div>
<div>Tonart: {{ song.key }}</div>
<div>Tempo: {{ song.tempo }}</div>
<div>Status: {{ (song.status | status) || "entwurf" }}</div>
<div *ngIf="song.legalOwner">
Rechteinhaber: {{ song.legalOwner | legalOwner }}
</div>
<div *ngIf="song.legalOwnerId && song.legalOwner === 'CCLI'">
<a
href="https://songselect.ccli.com/Songs/{{ song.legalOwnerId }}"
target="_blank"
>
CCLI Nummer: {{ song.legalOwnerId }}
</a>
</div>
<div *ngIf="song.legalOwnerId && song.legalOwner!=='CCLI'">Rechteinhaber ID: {{song.legalOwnerId}}</div>
<div *ngIf="song.artist">Künstler: {{song.artist}}</div>
<div *ngIf="song.label">Verlag: {{song.label}}</div>
<div *ngIf="song.origin">Quelle: {{song.origin}}</div>
<div *ngIf="song.origin">Quelle: {{song.origin}}</div>
<div *ngIf="song.legalOwnerId && song.legalOwner !== 'CCLI'">
Rechteinhaber ID: {{ song.legalOwnerId }}
</div>
<div *ngIf="song.artist">Künstler: {{ song.artist }}</div>
<div *ngIf="song.label">Verlag: {{ song.label }}</div>
<div *ngIf="song.origin">Quelle: {{ song.origin }}</div>
<div *ngIf="song.origin">Quelle: {{ song.origin }}</div>
</div>
</div>
<!-- <div class="text">{{song.text}}</div>-->
<app-song-text *ngIf="user$|async as user" [chordMode]="user.chordMode" [showSwitch]="true"
[text]="song.text"></app-song-text>
<app-song-text
*ngIf="user$ | async as user"
[chordMode]="user.chordMode"
[showSwitch]="true"
[text]="song.text"
></app-song-text>
<mat-chip-list *appRole="['leader', 'contributor']" aria-label="Attribute">
<mat-chip *ngFor="let flag of getFlags(song.flags)">{{flag}}</mat-chip>
<mat-chip-list
*appRole="['leader', 'contributor']"
aria-label="Attribute"
>
<mat-chip *ngFor="let flag of getFlags(song.flags)">{{
flag
}}</mat-chip>
</mat-chip-list>
<div *appRole="['leader', 'contributor']" class="text">{{song.comment}}</div>
<div *appRole="['leader', 'contributor']" class="text">
{{ song.comment }}
</div>
</div>
<app-button-row>
<app-button (click)="onDelete(song.id)" *appRole="['admin']" [icon]="faDelete">Löschen</app-button>
<app-button *appRole="['contributor']" [icon]="faEdit" routerLink="edit">Bearbeiten</app-button>
<app-button
(click)="onDelete(song.id)"
*appRole="['admin']"
[icon]="faDelete"
>Löschen
</app-button>
<app-button *appRole="['contributor']" [icon]="faEdit" routerLink="edit"
>Bearbeiten
</app-button>
</app-button-row>
</app-card>
<ng-container *ngIf="(files$|async) as files">
<app-card *ngIf="files.length>0" heading="Anhänge">
<p *ngFor="let file of (files$|async)">
<ng-container *ngIf="files$ | async as files">
<app-card *ngIf="files.length > 0" heading="Anhänge">
<p *ngFor="let file of files$ | async">
<app-file [file]="file"></app-file>
</p>
</app-card>

View File

@@ -9,18 +9,17 @@ describe('SongComponent', () => {
let fixture: ComponentFixture<SongComponent>;
const mockActivatedRoute = {
params: of({songId: '4711'})
params: of({songId: '4711'}),
};
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [SongComponent],
providers: [
{provide: ActivatedRoute, useValue: mockActivatedRoute}
]
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [SongComponent],
providers: [{provide: ActivatedRoute, useValue: mockActivatedRoute}],
}).compileComponents();
})
.compileComponents();
}));
);
beforeEach(() => {
fixture = TestBed.createComponent(SongComponent);
@@ -29,7 +28,6 @@ describe('SongComponent', () => {
});
it('should create', () => {
expect(component).toBeTruthy();
void expect(component).toBeTruthy();
});
});

View File

@@ -14,7 +14,7 @@ import {faTrash} from '@fortawesome/free-solid-svg-icons/faTrash';
@Component({
selector: 'app-song',
templateUrl: './song.component.html',
styleUrls: ['./song.component.less']
styleUrls: ['./song.component.less'],
})
export class SongComponent implements OnInit {
public song$: Observable<Song>;
@@ -23,24 +23,18 @@ export class SongComponent implements OnInit {
public faEdit = faEdit;
public faDelete = faTrash;
constructor(
private activatedRoute: ActivatedRoute,
private songService: SongService,
private fileService: FileDataService,
private userService: UserService,
private router: Router,
) {
public constructor(private activatedRoute: ActivatedRoute, private songService: SongService, private fileService: FileDataService, private userService: UserService, private router: Router) {
this.user$ = userService.user$;
}
public ngOnInit(): void {
this.song$ = this.activatedRoute.params.pipe(
map(param => param.songId),
map((param: {songId: string}) => param.songId),
switchMap(songId => this.songService.read$(songId))
);
this.files$ = this.activatedRoute.params.pipe(
map(param => param.songId),
map((param: {songId: string}) => param.songId),
switchMap(songId => this.fileService.read$(songId))
);
}
@@ -50,7 +44,7 @@ export class SongComponent implements OnInit {
return [];
}
return flags.split(';').filter(_ => !!_);
}
};
public async onDelete(songId: string): Promise<void> {
await this.songService.delete(songId);

View File

@@ -14,7 +14,6 @@ import {StatusTranslaterModule} from '../../../widget-modules/pipes/status-trans
import {ButtonModule} from '../../../widget-modules/components/button/button.module';
import {FileComponent} from './file/file.component';
@NgModule({
declarations: [SongComponent, FileComponent],
exports: [SongComponent],
@@ -32,7 +31,6 @@ import {FileComponent} from './file/file.component';
RoleModule,
StatusTranslaterModule,
ButtonModule,
]
],
})
export class SongModule {
}
export class SongModule {}