linting
This commit is contained in:
@@ -10,8 +10,7 @@
|
|||||||
],
|
],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"project": [
|
"project": [
|
||||||
"tsconfig.json",
|
"tsconfig.json"
|
||||||
"e2e/tsconfig.json"
|
|
||||||
],
|
],
|
||||||
"createDefaultProgram": true
|
"createDefaultProgram": true
|
||||||
},
|
},
|
||||||
|
|||||||
34
angular.json
34
angular.json
@@ -45,10 +45,6 @@
|
|||||||
"src/styles/shadow.less"
|
"src/styles/shadow.less"
|
||||||
],
|
],
|
||||||
"scripts": [],
|
"scripts": [],
|
||||||
"extractLicenses": false,
|
|
||||||
"sourceMap": true,
|
|
||||||
"optimization": false,
|
|
||||||
"namedChunks": true,
|
|
||||||
"allowedCommonJsDependencies": [
|
"allowedCommonJsDependencies": [
|
||||||
"lodash",
|
"lodash",
|
||||||
"docx",
|
"docx",
|
||||||
@@ -57,6 +53,13 @@
|
|||||||
"browser": "src/main.ts"
|
"browser": "src/main.ts"
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
|
"development": {
|
||||||
|
"aot": false,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"optimization": false,
|
||||||
|
"namedChunks": true
|
||||||
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"fileReplacements": [
|
"fileReplacements": [
|
||||||
{
|
{
|
||||||
@@ -84,18 +87,23 @@
|
|||||||
],
|
],
|
||||||
"serviceWorker": "ngsw-config.json"
|
"serviceWorker": "ngsw-config.json"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"defaultConfiguration": "production"
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
"options": {
|
"options": {
|
||||||
"buildTarget": "wgenerator:build"
|
"buildTarget": "wgenerator:build:development"
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
|
"development": {
|
||||||
|
"buildTarget": "wgenerator:build:development"
|
||||||
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"buildTarget": "wgenerator:build:production"
|
"buildTarget": "wgenerator:build:production"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"defaultConfiguration": "development"
|
||||||
},
|
},
|
||||||
"extract-i18n": {
|
"extract-i18n": {
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
@@ -127,18 +135,6 @@
|
|||||||
"src/**/*.html"
|
"src/**/*.html"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"e2e": {
|
|
||||||
"builder": "@angular-devkit/build-angular:protractor",
|
|
||||||
"options": {
|
|
||||||
"protractorConfig": "e2e/protractor.conf.js",
|
|
||||||
"devServerTarget": "wgenerator:serve"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"devServerTarget": "wgenerator:serve:production"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
// Protractor configuration file, see link for more information
|
|
||||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
|
||||||
|
|
||||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type { import("protractor").Config }
|
|
||||||
*/
|
|
||||||
exports.config = {
|
|
||||||
allScriptsTimeout: 11000,
|
|
||||||
specs: [
|
|
||||||
'./src/**/*.e2e-spec.ts'
|
|
||||||
],
|
|
||||||
capabilities: {
|
|
||||||
browserName: 'chrome'
|
|
||||||
},
|
|
||||||
directConnect: true,
|
|
||||||
baseUrl: 'http://localhost:4200/',
|
|
||||||
framework: 'jasmine',
|
|
||||||
jasmineNodeOpts: {
|
|
||||||
showColors: true,
|
|
||||||
defaultTimeoutInterval: 30000,
|
|
||||||
print: function() {}
|
|
||||||
},
|
|
||||||
onPrepare() {
|
|
||||||
require('ts-node').register({
|
|
||||||
project: require('path').join(__dirname, './tsconfig.json')
|
|
||||||
});
|
|
||||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import {AppPage} from './app.po';
|
|
||||||
import {browser, logging} from 'protractor';
|
|
||||||
|
|
||||||
describe('workspace-project App', () => {
|
|
||||||
let page: AppPage;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
page = new AppPage();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display welcome message', () => {
|
|
||||||
void page.navigateTo();
|
|
||||||
void expect(page.getTitleText()).toEqual('wgenerator app is running!');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
// Assert that there are no errors emitted from the browser
|
|
||||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
|
||||||
void expect(logs).not.toContain(
|
|
||||||
jasmine.objectContaining({
|
|
||||||
level: logging.Level.SEVERE,
|
|
||||||
} as logging.Entry)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { browser, by, element } from 'protractor';
|
|
||||||
|
|
||||||
export class AppPage {
|
|
||||||
navigateTo() {
|
|
||||||
return browser.get(browser.baseUrl) as Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
getTitleText() {
|
|
||||||
return element(by.css('app-root .content span')).getText() as Promise<string>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../out-tsc/e2e",
|
|
||||||
"module": "commonjs",
|
|
||||||
"target": "es2018",
|
|
||||||
"types": [
|
|
||||||
"jasmine",
|
|
||||||
"jasminewd2",
|
|
||||||
"node"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
"version": "1.6",
|
"version": "1.6",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
"build": "ng build",
|
"build": "ng build --configuration production",
|
||||||
|
"build:dev": "ng build --configuration development",
|
||||||
"deploy": "ng build --configuration production && firebase deploy",
|
"deploy": "ng build --configuration production && firebase deploy",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"lint": "ng lint --fix",
|
"lint": "ng lint --fix",
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export class MonitorComponent implements OnInit, OnDestroy {
|
|||||||
public config$: Observable<Config | null>;
|
public config$: Observable<Config | null>;
|
||||||
public presentationBackground: PresentationBackground = 'none';
|
public presentationBackground: PresentationBackground = 'none';
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
private songSwitchTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
const configService = this.configService;
|
const configService = this.configService;
|
||||||
@@ -97,7 +98,10 @@ export class MonitorComponent implements OnInit, OnDestroy {
|
|||||||
if (this.songId !== presentationSongId) {
|
if (this.songId !== presentationSongId) {
|
||||||
this.songId = 'empty';
|
this.songId = 'empty';
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
if (this.songSwitchTimeoutId) {
|
||||||
|
clearTimeout(this.songSwitchTimeoutId);
|
||||||
|
}
|
||||||
|
this.songSwitchTimeoutId = setTimeout(() => {
|
||||||
this.songId = presentationSongId;
|
this.songId = presentationSongId;
|
||||||
this.cRef.markForCheck();
|
this.cRef.markForCheck();
|
||||||
}, 600);
|
}, 600);
|
||||||
@@ -113,6 +117,9 @@ export class MonitorComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
public ngOnDestroy(): void {
|
||||||
|
if (this.songSwitchTimeoutId) {
|
||||||
|
clearTimeout(this.songSwitchTimeoutId);
|
||||||
|
}
|
||||||
this.destroy$.next();
|
this.destroy$.next();
|
||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,9 +120,11 @@ export class RemoteComponent implements OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.presentationDynamicCaptionChanged$
|
this.presentationDynamicCaptionChanged$
|
||||||
.pipe(debounceTime(1000))
|
.pipe(debounceTime(1000), takeUntil(this.destroy$))
|
||||||
.subscribe(_ => void this.showService.update$(_.showId, {presentationDynamicCaption: _.presentationDynamicCaption}));
|
.subscribe(_ => void this.showService.update$(_.showId, {presentationDynamicCaption: _.presentationDynamicCaption}));
|
||||||
this.presentationDynamicTextChanged$.pipe(debounceTime(1000)).subscribe(_ => void this.showService.update$(_.showId, {presentationDynamicText: _.presentationDynamicText}));
|
this.presentationDynamicTextChanged$
|
||||||
|
.pipe(debounceTime(1000), takeUntil(this.destroy$))
|
||||||
|
.subscribe(_ => void this.showService.update$(_.showId, {presentationDynamicText: _.presentationDynamicText}));
|
||||||
}
|
}
|
||||||
|
|
||||||
public trackBy(index: number, item: PresentationSong): string {
|
public trackBy(index: number, item: PresentationSong): string {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export interface ShareDialogData {
|
|||||||
export class ShareDialogComponent {
|
export class ShareDialogComponent {
|
||||||
public data = inject<ShareDialogData>(MAT_DIALOG_DATA);
|
public data = inject<ShareDialogData>(MAT_DIALOG_DATA);
|
||||||
|
|
||||||
public qrCode: string;
|
public qrCode = '';
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
const data = this.data;
|
const data = this.data;
|
||||||
@@ -35,10 +35,10 @@ export class ShareDialogComponent {
|
|||||||
light: '#ffffff',
|
light: '#ffffff',
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-return
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-return
|
||||||
}).then(_ => (this.qrCode = _));
|
}).then((qrCode: string) => (this.qrCode = qrCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async share() {
|
public async share(): Promise<void> {
|
||||||
if (navigator.clipboard) await navigator.clipboard.writeText(this.data.url);
|
if (navigator.clipboard) await navigator.clipboard.writeText(this.data.url);
|
||||||
|
|
||||||
if (navigator.share)
|
if (navigator.share)
|
||||||
|
|||||||
@@ -87,10 +87,15 @@ export class EditComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.showService.update$(this.form.value.id, {
|
const {id, date, showType} = this.form.getRawValue();
|
||||||
date: Timestamp.fromDate(this.form.value.date),
|
if (!id || !date || !showType) {
|
||||||
showType: this.form.value.showType,
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.showService.update$(id, {
|
||||||
|
date: Timestamp.fromDate(date),
|
||||||
|
showType,
|
||||||
} as Partial<Show>);
|
} as Partial<Show>);
|
||||||
await this.router.navigateByUrl(`/shows/${this.form.value.id ?? ''}`);
|
await this.router.navigateByUrl(`/shows/${id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {Component, Input, inject} from '@angular/core';
|
import {Component, DestroyRef, Input, inject} from '@angular/core';
|
||||||
import {KeyValue} from '@angular/common';
|
import {KeyValue} from '@angular/common';
|
||||||
import {ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup} from '@angular/forms';
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||||
import {FilterValues} from './filter-values';
|
import {FilterValues} from './filter-values';
|
||||||
import {Show} from '../../services/show';
|
import {Show} from '../../services/show';
|
||||||
import {ShowService} from '../../services/show.service';
|
import {ShowService} from '../../services/show.service';
|
||||||
@@ -24,13 +25,18 @@ export class FilterComponent {
|
|||||||
private showService = inject(ShowService);
|
private showService = inject(ShowService);
|
||||||
private userService = inject(UserService);
|
private userService = inject(UserService);
|
||||||
private filterStore = inject(FilterStoreService);
|
private filterStore = inject(FilterStoreService);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
@Input() public shows: Show[] = [];
|
@Input() public shows: Show[] = [];
|
||||||
|
|
||||||
public showTypePublic = ShowService.SHOW_TYPE_PUBLIC;
|
public showTypePublic = ShowService.SHOW_TYPE_PUBLIC;
|
||||||
public showTypePrivate = ShowService.SHOW_TYPE_PRIVATE;
|
public showTypePrivate = ShowService.SHOW_TYPE_PRIVATE;
|
||||||
|
|
||||||
public filterFormGroup: UntypedFormGroup;
|
public filterFormGroup: FormGroup<{
|
||||||
|
time: FormControl<number>;
|
||||||
|
owner: FormControl<string | null>;
|
||||||
|
showType: FormControl<string | null>;
|
||||||
|
}>;
|
||||||
public times: KeyValue<number, string>[] = [
|
public times: KeyValue<number, string>[] = [
|
||||||
{key: 1, value: 'letzter Monat'},
|
{key: 1, value: 'letzter Monat'},
|
||||||
{key: 3, value: 'letztes Quartal'},
|
{key: 3, value: 'letztes Quartal'},
|
||||||
@@ -41,15 +47,15 @@ export class FilterComponent {
|
|||||||
public owners: {key: string; value: string}[] = [];
|
public owners: {key: string; value: string}[] = [];
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
const fb = inject(UntypedFormBuilder);
|
const fb = inject(FormBuilder);
|
||||||
|
|
||||||
this.filterFormGroup = fb.group({
|
this.filterFormGroup = fb.group({
|
||||||
time: 1,
|
time: fb.nonNullable.control(1),
|
||||||
owner: null,
|
owner: fb.control<string | null>(null),
|
||||||
showType: null,
|
showType: fb.control<string | null>(null),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filterStore.showFilter$.subscribe(filterValues => {
|
this.filterStore.showFilter$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(filterValues => {
|
||||||
this.filterFormGroup.patchValue(
|
this.filterFormGroup.patchValue(
|
||||||
{
|
{
|
||||||
time: filterValues.time,
|
time: filterValues.time,
|
||||||
@@ -60,11 +66,13 @@ export class FilterComponent {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filterFormGroup.controls.time.valueChanges.subscribe(_ => this.filterValueChanged('time', (_ as number) ?? 1));
|
this.filterFormGroup.controls.time.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => this.filterValueChanged('time', value));
|
||||||
this.filterFormGroup.controls.owner.valueChanges.subscribe(_ => this.filterValueChanged('owner', (_ as string | null) ?? ''));
|
this.filterFormGroup.controls.owner.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => this.filterValueChanged('owner', value ?? ''));
|
||||||
this.filterFormGroup.controls.showType.valueChanges.subscribe(_ => this.filterValueChanged('showType', (_ as string | null) ?? ''));
|
this.filterFormGroup.controls.showType.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => this.filterValueChanged('showType', value ?? ''));
|
||||||
|
|
||||||
this.owners$().subscribe(owners => (this.owners = owners));
|
this.owners$()
|
||||||
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||||
|
.subscribe(owners => (this.owners = owners));
|
||||||
}
|
}
|
||||||
|
|
||||||
public owners$ = (): Observable<{key: string; value: string}[]> => {
|
public owners$ = (): Observable<{key: string; value: string}[]> => {
|
||||||
@@ -85,17 +93,15 @@ export class FilterComponent {
|
|||||||
this.userService.getUserbyId$(ownerId).pipe(
|
this.userService.getUserbyId$(ownerId).pipe(
|
||||||
map(user => ({
|
map(user => ({
|
||||||
key: ownerId,
|
key: ownerId,
|
||||||
value: user?.name,
|
value: user?.name ?? ownerId,
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
map(owners => {
|
map(owners => owners.sort(dynamicSort<{key: string; value: string}>('value'))),
|
||||||
return owners.sort(dynamicSort('value'));
|
|
||||||
}),
|
|
||||||
distinctUntilChanged((left, right) => this.sameOwners(left, right)),
|
distinctUntilChanged((left, right) => this.sameOwners(left, right)),
|
||||||
map(_ => _ as {key: string; value: string}[])
|
map(owners => owners as {key: string; value: string}[])
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {ShowDataService} from '../services/show-data.service';
|
|||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {Show} from '../services/show';
|
import {Show} from '../services/show';
|
||||||
import {ShowService} from '../services/show.service';
|
import {ShowService} from '../services/show.service';
|
||||||
import {ReactiveFormsModule, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms';
|
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {faSave} from '@fortawesome/free-solid-svg-icons';
|
import {faSave} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {CardComponent} from '../../../widget-modules/components/card/card.component';
|
import {CardComponent} from '../../../widget-modules/components/card/card.component';
|
||||||
@@ -46,9 +46,9 @@ export class NewComponent implements OnInit {
|
|||||||
public shows$: Observable<Show[]>;
|
public shows$: Observable<Show[]>;
|
||||||
public showTypePublic = ShowService.SHOW_TYPE_PUBLIC;
|
public showTypePublic = ShowService.SHOW_TYPE_PUBLIC;
|
||||||
public showTypePrivate = ShowService.SHOW_TYPE_PRIVATE;
|
public showTypePrivate = ShowService.SHOW_TYPE_PRIVATE;
|
||||||
public form: UntypedFormGroup = new UntypedFormGroup({
|
public form = new FormGroup({
|
||||||
date: new UntypedFormControl(null, Validators.required),
|
date: new FormControl<Date | null>(null, Validators.required),
|
||||||
showType: new UntypedFormControl(null, Validators.required),
|
showType: new FormControl<string | null>(null, Validators.required),
|
||||||
});
|
});
|
||||||
public faSave = faSave;
|
public faSave = faSave;
|
||||||
|
|
||||||
@@ -68,7 +68,11 @@ export class NewComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = await this.showService.new$(this.form.value as Partial<Show>);
|
const {date, showType} = this.form.getRawValue();
|
||||||
|
const id = await this.showService.new$({
|
||||||
|
date,
|
||||||
|
showType,
|
||||||
|
} as unknown as Partial<Show>);
|
||||||
await this.router.navigateByUrl(`/shows/${id ?? ''}`);
|
await this.router.navigateByUrl(`/shows/${id ?? ''}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {filter, map, shareReplay, switchMap, tap} from 'rxjs/operators';
|
|||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {ShowService} from '../services/show.service';
|
import {ShowService} from '../services/show.service';
|
||||||
import {Observable, of, Subscription} from 'rxjs';
|
import {Observable, of, Subscription} from 'rxjs';
|
||||||
|
import {take} from 'rxjs/operators';
|
||||||
import {Show} from '../services/show';
|
import {Show} from '../services/show';
|
||||||
import {SongService} from '../../songs/services/song.service';
|
import {SongService} from '../../songs/services/song.service';
|
||||||
import {Song} from '../../songs/services/song';
|
import {Song} from '../../songs/services/song';
|
||||||
@@ -112,12 +113,13 @@ export class ShowComponent implements OnInit, OnDestroy {
|
|||||||
public faRestore = faMinimize;
|
public faRestore = faMinimize;
|
||||||
public faMaximize = faMaximize;
|
public faMaximize = faMaximize;
|
||||||
public faNextSong = faChevronRight;
|
public faNextSong = faChevronRight;
|
||||||
public currentTime: Date;
|
public currentTime!: Date;
|
||||||
private subs: Subscription[] = [];
|
private subs: Subscription[] = [];
|
||||||
|
private clockIntervalId: ReturnType<typeof setInterval> | null = null;
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.currentTime = new Date();
|
this.currentTime = new Date();
|
||||||
setInterval(() => {
|
this.clockIntervalId = setInterval(() => {
|
||||||
this.currentTime = new Date();
|
this.currentTime = new Date();
|
||||||
}, 10000);
|
}, 10000);
|
||||||
this.show$ = this.activatedRoute.params.pipe(
|
this.show$ = this.activatedRoute.params.pipe(
|
||||||
@@ -155,6 +157,9 @@ export class ShowComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
public ngOnDestroy(): void {
|
public ngOnDestroy(): void {
|
||||||
this.subs.forEach(_ => _.unsubscribe());
|
this.subs.forEach(_ => _.unsubscribe());
|
||||||
|
if (this.clockIntervalId) {
|
||||||
|
clearInterval(this.clockIntervalId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onZoomIn() {
|
public onZoomIn() {
|
||||||
@@ -172,7 +177,7 @@ export class ShowComponent implements OnInit, OnDestroy {
|
|||||||
width: '350px',
|
width: '350px',
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((archive: boolean) => {
|
dialogRef.afterClosed().pipe(take(1)).subscribe((archive: boolean) => {
|
||||||
if (archive && this.showId != null) void this.showService.update$(this.showId, {archived});
|
if (archive && this.showId != null) void this.showService.update$(this.showId, {archived});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import {Component, Input, OnInit, ViewChild, inject} from '@angular/core';
|
import {Component, DestroyRef, Input, OnInit, ViewChild, inject} from '@angular/core';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
import {ShowSongService} from '../../services/show-song.service';
|
import {ShowSongService} from '../../services/show-song.service';
|
||||||
import {ShowSong} from '../../services/show-song';
|
import {ShowSong} from '../../services/show-song';
|
||||||
import {getScale} from '../../../songs/services/key.helper';
|
import {getScale} from '../../../songs/services/key.helper';
|
||||||
import {ReactiveFormsModule, UntypedFormControl} from '@angular/forms';
|
import {FormControl, ReactiveFormsModule} from '@angular/forms';
|
||||||
import {ChordMode, SongTextComponent} from '../../../../widget-modules/components/song-text/song-text.component';
|
import {ChordMode, SongTextComponent} from '../../../../widget-modules/components/song-text/song-text.component';
|
||||||
import {Show} from '../../services/show';
|
import {Show} from '../../services/show';
|
||||||
import {faEraser, faPenToSquare, faSave, faTrash} from '@fortawesome/free-solid-svg-icons';
|
import {faEraser, faPenToSquare, faSave, faTrash} from '@fortawesome/free-solid-svg-icons';
|
||||||
@@ -42,6 +43,7 @@ import {CdkDragHandle} from '@angular/cdk/drag-drop';
|
|||||||
})
|
})
|
||||||
export class SongComponent implements OnInit {
|
export class SongComponent implements OnInit {
|
||||||
private showSongService = inject(ShowSongService);
|
private showSongService = inject(ShowSongService);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
@Input() public show: Show | null = null;
|
@Input() public show: Show | null = null;
|
||||||
@Input() public showId: string | null = null;
|
@Input() public showId: string | null = null;
|
||||||
@@ -54,11 +56,11 @@ export class SongComponent implements OnInit {
|
|||||||
public faEdit = faPenToSquare;
|
public faEdit = faPenToSquare;
|
||||||
public faSave = faSave;
|
public faSave = faSave;
|
||||||
public faEraser = faEraser;
|
public faEraser = faEraser;
|
||||||
public keyFormControl: UntypedFormControl = new UntypedFormControl();
|
public keyFormControl = new FormControl<string>('', {nonNullable: true});
|
||||||
public iSong: ShowSong | null = null;
|
public iSong: ShowSong | null = null;
|
||||||
public edit = false;
|
public edit = false;
|
||||||
public editSongControl = new UntypedFormControl();
|
public editSongControl = new FormControl<string | null>(null);
|
||||||
@ViewChild('option') private keyOptions: MatSelect;
|
@ViewChild('option') private keyOptions!: MatSelect;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
public set showSong(song: ShowSong) {
|
public set showSong(song: ShowSong) {
|
||||||
@@ -68,8 +70,8 @@ export class SongComponent implements OnInit {
|
|||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
if (!this.iSong) return;
|
if (!this.iSong) return;
|
||||||
this.keyFormControl = new UntypedFormControl(this.iSong.key);
|
this.keyFormControl = new FormControl<string>(this.iSong.key, {nonNullable: true});
|
||||||
this.keyFormControl.valueChanges.subscribe((value: string) => {
|
this.keyFormControl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
|
||||||
if (!this.showId || !this.iSong) return;
|
if (!this.showId || !this.iSong) return;
|
||||||
void this.showSongService.update$(this.showId, this.iSong.id, {key: value});
|
void this.showSongService.update$(this.showId, this.iSong.id, {key: value});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -96,10 +96,11 @@ export class TextRenderingService {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const indices = {
|
const indices: Record<SectionType, number> = {
|
||||||
[SectionType.Bridge]: 0,
|
[SectionType.Bridge]: 0,
|
||||||
[SectionType.Chorus]: 0,
|
[SectionType.Chorus]: 0,
|
||||||
[SectionType.Verse]: 0,
|
[SectionType.Verse]: 0,
|
||||||
|
[SectionType.Comment]: 0,
|
||||||
};
|
};
|
||||||
const sections: Section[] = [];
|
const sections: Section[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {Component, Input, inject} from '@angular/core';
|
import {Component, DestroyRef, Input, inject} from '@angular/core';
|
||||||
import {ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup} from '@angular/forms';
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||||
import {SongService} from '../../services/song.service';
|
import {SongService} from '../../services/song.service';
|
||||||
import {FilterValues} from './filter-values';
|
import {FilterValues} from './filter-values';
|
||||||
import {Song} from '../../services/song';
|
import {Song} from '../../services/song';
|
||||||
@@ -22,17 +23,24 @@ import {SongTypePipe} from '../../../../widget-modules/pipes/song-type-translate
|
|||||||
})
|
})
|
||||||
export class FilterComponent {
|
export class FilterComponent {
|
||||||
private filterStore = inject(FilterStoreService);
|
private filterStore = inject(FilterStoreService);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
public filterFormGroup: UntypedFormGroup;
|
public filterFormGroup: FormGroup<{
|
||||||
|
q: FormControl<string>;
|
||||||
|
type: FormControl<string>;
|
||||||
|
key: FormControl<string>;
|
||||||
|
legalType: FormControl<string>;
|
||||||
|
flag: FormControl<string>;
|
||||||
|
}>;
|
||||||
@Input() public songs: Song[] = [];
|
@Input() public songs: Song[] = [];
|
||||||
public types = SongService.TYPES;
|
public types = SongService.TYPES;
|
||||||
public legalType = SongService.LEGAL_TYPE;
|
public legalType = SongService.LEGAL_TYPE;
|
||||||
public keys = KEYS;
|
public keys = KEYS;
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
const fb = inject(UntypedFormBuilder);
|
const fb = inject(FormBuilder);
|
||||||
|
|
||||||
this.filterFormGroup = fb.group({
|
this.filterFormGroup = fb.nonNullable.group({
|
||||||
q: '',
|
q: '',
|
||||||
type: '',
|
type: '',
|
||||||
key: '',
|
key: '',
|
||||||
@@ -40,15 +48,15 @@ export class FilterComponent {
|
|||||||
flag: '',
|
flag: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filterStore.songFilter$.subscribe(filterValues => {
|
this.filterStore.songFilter$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(filterValues => {
|
||||||
this.filterFormGroup.patchValue(filterValues, {emitEvent: false});
|
this.filterFormGroup.patchValue(filterValues, {emitEvent: false});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filterFormGroup.controls.q.valueChanges.subscribe(_ => this.filterValueChanged('q', (_ as string) ?? ''));
|
this.filterFormGroup.controls.q.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => this.filterValueChanged('q', value));
|
||||||
this.filterFormGroup.controls.key.valueChanges.subscribe(_ => this.filterValueChanged('key', (_ as string) ?? ''));
|
this.filterFormGroup.controls.key.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => this.filterValueChanged('key', value));
|
||||||
this.filterFormGroup.controls.type.valueChanges.subscribe(_ => this.filterValueChanged('type', (_ as string) ?? ''));
|
this.filterFormGroup.controls.type.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => this.filterValueChanged('type', value));
|
||||||
this.filterFormGroup.controls.legalType.valueChanges.subscribe(_ => this.filterValueChanged('legalType', (_ as string) ?? ''));
|
this.filterFormGroup.controls.legalType.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => this.filterValueChanged('legalType', value));
|
||||||
this.filterFormGroup.controls.flag.valueChanges.subscribe(_ => this.filterValueChanged('flag', (_ as string) ?? ''));
|
this.filterFormGroup.controls.flag.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => this.filterValueChanged('flag', value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public getFlags(): string[] {
|
public getFlags(): string[] {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {Component, inject} from '@angular/core';
|
import {Component, DestroyRef, inject} from '@angular/core';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
import {Upload} from '../../../services/upload';
|
import {Upload} from '../../../services/upload';
|
||||||
import {UploadService} from '../../../services/upload.service';
|
import {UploadService} from '../../../services/upload.service';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
@@ -22,6 +23,7 @@ export class EditFileComponent {
|
|||||||
private activatedRoute = inject(ActivatedRoute);
|
private activatedRoute = inject(ActivatedRoute);
|
||||||
private uploadService = inject(UploadService);
|
private uploadService = inject(UploadService);
|
||||||
private fileService = inject(FileDataService);
|
private fileService = inject(FileDataService);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
public selectedFiles: FileList | null = null;
|
public selectedFiles: FileList | null = null;
|
||||||
public currentUpload: Upload | null = null;
|
public currentUpload: Upload | null = null;
|
||||||
@@ -32,7 +34,8 @@ export class EditFileComponent {
|
|||||||
this.activatedRoute.params
|
this.activatedRoute.params
|
||||||
.pipe(
|
.pipe(
|
||||||
map(param => param as {songId: string}),
|
map(param => param as {songId: string}),
|
||||||
map(param => param.songId)
|
map(param => param.songId),
|
||||||
|
takeUntilDestroyed(this.destroyRef)
|
||||||
)
|
)
|
||||||
.subscribe(songId => {
|
.subscribe(songId => {
|
||||||
this.songId = songId;
|
this.songId = songId;
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import {Component, OnInit, inject} from '@angular/core';
|
import {Component, DestroyRef, inject, OnInit} from '@angular/core';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
import {Song} from '../../../services/song';
|
import {Song} from '../../../services/song';
|
||||||
import {ReactiveFormsModule, UntypedFormGroup} from '@angular/forms';
|
import {ReactiveFormsModule} from '@angular/forms';
|
||||||
import {ActivatedRoute, Router, RouterStateSnapshot} from '@angular/router';
|
import {ActivatedRoute, Router, RouterStateSnapshot} from '@angular/router';
|
||||||
import {SongService} from '../../../services/song.service';
|
import {SongService} from '../../../services/song.service';
|
||||||
import {EditService} from '../edit.service';
|
import {EditService, SongFormGroup} from '../edit.service';
|
||||||
import {first, map, switchMap} from 'rxjs/operators';
|
import {first, map, switchMap} from 'rxjs/operators';
|
||||||
import {startWith} from 'rxjs';
|
import {startWith} from 'rxjs';
|
||||||
import {KEYS} from '../../../services/key.helper';
|
import {KEYS} from '../../../services/key.helper';
|
||||||
@@ -62,15 +63,9 @@ import {StatusPipe} from '../../../../../widget-modules/pipes/status-translater/
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class EditSongComponent implements OnInit {
|
export class EditSongComponent implements OnInit {
|
||||||
private activatedRoute = inject(ActivatedRoute);
|
|
||||||
private songService = inject(SongService);
|
|
||||||
private editService = inject(EditService);
|
|
||||||
private router = inject(Router);
|
|
||||||
private textRenderingService = inject(TextRenderingService);
|
|
||||||
public dialog = inject(MatDialog);
|
public dialog = inject(MatDialog);
|
||||||
|
|
||||||
public song: Song | null = null;
|
public song: Song | null = null;
|
||||||
public form: UntypedFormGroup = new UntypedFormGroup({});
|
public form = {} as SongFormGroup;
|
||||||
public keys = KEYS;
|
public keys = KEYS;
|
||||||
public types = SongService.TYPES;
|
public types = SongService.TYPES;
|
||||||
public status = SongService.STATUS;
|
public status = SongService.STATUS;
|
||||||
@@ -83,6 +78,12 @@ export class EditSongComponent implements OnInit {
|
|||||||
public faLink = faExternalLinkAlt;
|
public faLink = faExternalLinkAlt;
|
||||||
public songtextFocus = false;
|
public songtextFocus = false;
|
||||||
public chordValidationIssues: ChordValidationIssue[] = [];
|
public chordValidationIssues: ChordValidationIssue[] = [];
|
||||||
|
private activatedRoute = inject(ActivatedRoute);
|
||||||
|
private songService = inject(SongService);
|
||||||
|
private editService = inject(EditService);
|
||||||
|
private router = inject(Router);
|
||||||
|
private textRenderingService = inject(TextRenderingService);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.activatedRoute.params
|
this.activatedRoute.params
|
||||||
@@ -90,23 +91,24 @@ export class EditSongComponent implements OnInit {
|
|||||||
map(param => param as {songId: string}),
|
map(param => param as {songId: string}),
|
||||||
map(param => param.songId),
|
map(param => param.songId),
|
||||||
switchMap(songId => this.songService.read$(songId)),
|
switchMap(songId => this.songService.read$(songId)),
|
||||||
first()
|
first(),
|
||||||
|
takeUntilDestroyed(this.destroyRef)
|
||||||
)
|
)
|
||||||
.subscribe(song => {
|
.subscribe(song => {
|
||||||
this.song = song;
|
this.song = song;
|
||||||
if (!song) return;
|
if (!song) return;
|
||||||
this.form = this.editService.createSongForm(song);
|
this.form = this.editService.createSongForm(song);
|
||||||
this.form.controls.flags.valueChanges.subscribe(_ => this.onFlagsChanged(_ as string));
|
this.form.controls.flags.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => this.onFlagsChanged(value));
|
||||||
this.form.controls.text.valueChanges.pipe(startWith(this.form.controls.text.value)).subscribe(text => {
|
this.form.controls.text.valueChanges.pipe(startWith(this.form.controls.text.value), takeUntilDestroyed(this.destroyRef)).subscribe(text => {
|
||||||
this.updateChordValidation(text as string);
|
this.updateChordValidation(text);
|
||||||
});
|
});
|
||||||
this.onFlagsChanged(this.form.controls.flags.value as string);
|
this.onFlagsChanged(this.form.controls.flags.value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onSave(): Promise<void> {
|
public async onSave(): Promise<void> {
|
||||||
if (!this.song || this.form.invalid) return;
|
if (!this.song || this.form.invalid) return;
|
||||||
const data = this.form.value as Partial<Song>;
|
const data = this.form.getRawValue() as Partial<Song>;
|
||||||
await this.songService.update$(this.song.id, data);
|
await this.songService.update$(this.song.id, data);
|
||||||
this.form.markAsPristine();
|
this.form.markAsPristine();
|
||||||
await this.router.navigateByUrl('songs/' + this.song.id);
|
await this.router.navigateByUrl('songs/' + this.song.id);
|
||||||
@@ -121,7 +123,6 @@ export class EditSongComponent implements OnInit {
|
|||||||
const input = event.input;
|
const input = event.input;
|
||||||
const value = event.value;
|
const value = event.value;
|
||||||
|
|
||||||
// Add our fruit
|
|
||||||
if ((value || '').trim()) {
|
if ((value || '').trim()) {
|
||||||
const flags = [...this.flags, value.trim()];
|
const flags = [...this.flags, value.trim()];
|
||||||
this.form.controls.flags.setValue(flags.join(';'));
|
this.form.controls.flags.setValue(flags.join(';'));
|
||||||
@@ -174,7 +175,7 @@ export class EditSongComponent implements OnInit {
|
|||||||
|
|
||||||
private async onSaveDialogAfterClosed(save: boolean, url: string) {
|
private async onSaveDialogAfterClosed(save: boolean, url: string) {
|
||||||
if (save && this.song && !this.form.invalid) {
|
if (save && this.song && !this.form.invalid) {
|
||||||
const data = this.form.value as Partial<Song>;
|
const data = this.form.getRawValue() as Partial<Song>;
|
||||||
await this.songService.update$(this.song.id, data);
|
await this.songService.update$(this.song.id, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,48 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {Song} from '../../services/song';
|
import {Song} from '../../services/song';
|
||||||
import {UntypedFormControl, UntypedFormGroup} from '@angular/forms';
|
import {FormControl, FormGroup} from '@angular/forms';
|
||||||
|
|
||||||
|
export type SongFormGroup = FormGroup<{
|
||||||
|
text: FormControl<string>;
|
||||||
|
title: FormControl<string>;
|
||||||
|
comment: FormControl<string>;
|
||||||
|
flags: FormControl<string>;
|
||||||
|
key: FormControl<string>;
|
||||||
|
tempo: FormControl<number>;
|
||||||
|
type: FormControl<Song['type']>;
|
||||||
|
status: FormControl<Song['status']>;
|
||||||
|
legalType: FormControl<Song['legalType']>;
|
||||||
|
legalOwner: FormControl<Song['legalOwner']>;
|
||||||
|
legalOwnerId: FormControl<string>;
|
||||||
|
artist: FormControl<string>;
|
||||||
|
label: FormControl<string>;
|
||||||
|
termsOfUse: FormControl<string>;
|
||||||
|
origin: FormControl<string>;
|
||||||
|
}>;
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class EditService {
|
export class EditService {
|
||||||
public createSongForm(song: Song): UntypedFormGroup {
|
public createSongForm(song: Song): SongFormGroup {
|
||||||
return new UntypedFormGroup({
|
return new FormGroup({
|
||||||
text: new UntypedFormControl(song.text),
|
text: new FormControl(song.text, {nonNullable: true}),
|
||||||
title: new UntypedFormControl(song.title),
|
title: new FormControl(song.title, {nonNullable: true}),
|
||||||
comment: new UntypedFormControl(song.comment),
|
comment: new FormControl(song.comment, {nonNullable: true}),
|
||||||
flags: new UntypedFormControl(song.flags),
|
flags: new FormControl(song.flags, {nonNullable: true}),
|
||||||
key: new UntypedFormControl(song.key),
|
key: new FormControl(song.key, {nonNullable: true}),
|
||||||
tempo: new UntypedFormControl(song.tempo),
|
tempo: new FormControl(song.tempo, {nonNullable: true}),
|
||||||
type: new UntypedFormControl(song.type),
|
type: new FormControl(song.type, {nonNullable: true}),
|
||||||
status: new UntypedFormControl(song.status ?? 'draft'),
|
status: new FormControl(song.status ?? 'draft', {nonNullable: true}),
|
||||||
|
|
||||||
legalType: new UntypedFormControl(song.legalType),
|
legalType: new FormControl(song.legalType, {nonNullable: true}),
|
||||||
legalOwner: new UntypedFormControl(song.legalOwner),
|
legalOwner: new FormControl(song.legalOwner, {nonNullable: true}),
|
||||||
legalOwnerId: new UntypedFormControl(song.legalOwnerId),
|
legalOwnerId: new FormControl(song.legalOwnerId, {nonNullable: true}),
|
||||||
|
|
||||||
artist: new UntypedFormControl(song.artist),
|
artist: new FormControl(song.artist, {nonNullable: true}),
|
||||||
label: new UntypedFormControl(song.label),
|
label: new FormControl(song.label, {nonNullable: true}),
|
||||||
termsOfUse: new UntypedFormControl(song.termsOfUse),
|
termsOfUse: new FormControl(song.termsOfUse, {nonNullable: true}),
|
||||||
origin: new UntypedFormControl(song.origin),
|
origin: new FormControl(song.origin, {nonNullable: true}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import {Component, OnDestroy, OnInit, inject} from '@angular/core';
|
import {Component, OnInit, inject} from '@angular/core';
|
||||||
import {faSave} from '@fortawesome/free-solid-svg-icons';
|
import {faSave} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {ReactiveFormsModule, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms';
|
import {DestroyRef} from '@angular/core';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
import {SongService} from '../../services/song.service';
|
import {SongService} from '../../services/song.service';
|
||||||
import {Song} from '../../services/song';
|
import {Song} from '../../services/song';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {take} from 'rxjs/operators';
|
import {take} from 'rxjs/operators';
|
||||||
import {CardComponent} from '../../../../widget-modules/components/card/card.component';
|
import {CardComponent} from '../../../../widget-modules/components/card/card.component';
|
||||||
import {MatFormField, MatLabel} from '@angular/material/form-field';
|
import {MatFormField, MatLabel} from '@angular/material/form-field';
|
||||||
@@ -18,39 +19,34 @@ import {ButtonComponent} from '../../../../widget-modules/components/button/butt
|
|||||||
styleUrls: ['./new.component.less'],
|
styleUrls: ['./new.component.less'],
|
||||||
imports: [CardComponent, ReactiveFormsModule, MatFormField, MatLabel, MatInput, ButtonRowComponent, ButtonComponent],
|
imports: [CardComponent, ReactiveFormsModule, MatFormField, MatLabel, MatInput, ButtonRowComponent, ButtonComponent],
|
||||||
})
|
})
|
||||||
export class NewComponent implements OnInit, OnDestroy {
|
export class NewComponent implements OnInit {
|
||||||
private songService = inject(SongService);
|
private songService = inject(SongService);
|
||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
public faSave = faSave;
|
public faSave = faSave;
|
||||||
public form: UntypedFormGroup = new UntypedFormGroup({
|
public form = new FormGroup({
|
||||||
number: new UntypedFormControl(null, Validators.required),
|
number: new FormControl<number | null>(null, Validators.required),
|
||||||
title: new UntypedFormControl(null, Validators.required),
|
title: new FormControl<string>('', {nonNullable: true, validators: [Validators.required]}),
|
||||||
});
|
});
|
||||||
private subs: Subscription[] = [];
|
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.form.reset();
|
this.form.reset();
|
||||||
|
|
||||||
this.subs.push(
|
|
||||||
this.songService
|
this.songService
|
||||||
.list$()
|
.list$()
|
||||||
.pipe(take(1))
|
.pipe(take(1), takeUntilDestroyed(this.destroyRef))
|
||||||
.subscribe(songs => {
|
.subscribe(songs => {
|
||||||
const freeSongnumber = this.getFreeSongNumber(songs);
|
const freeSongnumber = this.getFreeSongNumber(songs);
|
||||||
this.form.controls.number.setValue(freeSongnumber);
|
this.form.controls.number.setValue(freeSongnumber);
|
||||||
})
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
|
||||||
this.subs.forEach(_ => _.unsubscribe());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onSave(): Promise<void> {
|
public async onSave(): Promise<void> {
|
||||||
const value = this.form.value as {number: number; title: string};
|
const {number: songNumber, title} = this.form.getRawValue();
|
||||||
const songNumber = value.number;
|
if (songNumber == null) {
|
||||||
const title = value.title;
|
return;
|
||||||
|
}
|
||||||
const newSongId = await this.songService.new(songNumber, title);
|
const newSongId = await this.songService.new(songNumber, title);
|
||||||
await this.router.navigateByUrl('/songs/' + newSongId + '/edit');
|
await this.router.navigateByUrl('/songs/' + newSongId + '/edit');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,11 +75,12 @@ export class SongComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.song$ = this.activatedRoute.params.pipe(
|
const song$ = this.activatedRoute.params.pipe(
|
||||||
map(param => param as {songId: string}),
|
map(param => param as {songId: string}),
|
||||||
map(param => param.songId),
|
map(param => param.songId),
|
||||||
switchMap(songId => this.songService.read$(songId))
|
switchMap(songId => this.songService.read$(songId))
|
||||||
);
|
);
|
||||||
|
this.song$ = song$;
|
||||||
|
|
||||||
this.files$ = this.activatedRoute.params.pipe(
|
this.files$ = this.activatedRoute.params.pipe(
|
||||||
map(param => param as {songId: string}),
|
map(param => param as {songId: string}),
|
||||||
@@ -87,7 +88,7 @@ export class SongComponent implements OnInit {
|
|||||||
switchMap(songId => this.fileService.read$(songId))
|
switchMap(songId => this.fileService.read$(songId))
|
||||||
);
|
);
|
||||||
|
|
||||||
this.songCount$ = combineLatest([this.user$, this.song$]).pipe(
|
this.songCount$ = combineLatest([this.userService.user$, song$]).pipe(
|
||||||
map(([user, song]) => {
|
map(([user, song]) => {
|
||||||
if (!song) {
|
if (!song) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -111,10 +112,9 @@ export class SongComponent implements OnInit {
|
|||||||
await this.router.navigateByUrl('/songs');
|
await this.router.navigateByUrl('/songs');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async addSongToShow(show: Show, song: Song) {
|
public async addSongToShow(show: Show, song: Song): Promise<void> {
|
||||||
if (!show) return;
|
const newId = await this.showSongService.new$(show.id, song.id, false);
|
||||||
const newId = await this.showSongService.new$(show?.id, song.id, false);
|
await this.showService.update$(show.id, {order: [...show.order, newId ?? '']});
|
||||||
await this.showService.update$(show?.id, {order: [...show.order, newId ?? '']});
|
|
||||||
await this.router.navigateByUrl('/shows/' + show.id);
|
await this.router.navigateByUrl('/shows/' + show.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ export class AuthMessagePipe implements PipeTransform {
|
|||||||
return 'Benutzer wurde nicht gefunden';
|
return 'Benutzer wurde nicht gefunden';
|
||||||
case 'auth/wrong-password':
|
case 'auth/wrong-password':
|
||||||
return 'Passwort ist falsch';
|
return 'Passwort ist falsch';
|
||||||
|
case 'auth/email-already-in-use':
|
||||||
|
return 'Die E-Mail-Adresse wird bereits verwendet';
|
||||||
|
case 'auth/invalid-email':
|
||||||
|
return 'Die E-Mail-Adresse ist ungueltig';
|
||||||
|
case 'auth/weak-password':
|
||||||
|
return 'Das Passwort ist zu schwach';
|
||||||
|
case 'unknown_error':
|
||||||
|
return 'Unbekannter Fehler';
|
||||||
default:
|
default:
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Component, OnInit, inject} from '@angular/core';
|
import {Component, OnInit, inject} from '@angular/core';
|
||||||
import {ReactiveFormsModule, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms';
|
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
import {Router, RouterLink} from '@angular/router';
|
import {Router, RouterLink} from '@angular/router';
|
||||||
import {UserService} from '../../../services/user/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
import {faSignInAlt, faUserPlus} from '@fortawesome/free-solid-svg-icons';
|
import {faSignInAlt, faUserPlus} from '@fortawesome/free-solid-svg-icons';
|
||||||
@@ -20,9 +20,9 @@ export class LoginComponent implements OnInit {
|
|||||||
private userService = inject(UserService);
|
private userService = inject(UserService);
|
||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
|
|
||||||
public form: UntypedFormGroup = new UntypedFormGroup({
|
public form = new FormGroup({
|
||||||
user: new UntypedFormControl(null, [Validators.required, Validators.email]),
|
user: new FormControl<string>('', {nonNullable: true, validators: [Validators.required, Validators.email]}),
|
||||||
pass: new UntypedFormControl(null, [Validators.required]),
|
pass: new FormControl<string>('', {nonNullable: true, validators: [Validators.required]}),
|
||||||
});
|
});
|
||||||
public errorMessage = '';
|
public errorMessage = '';
|
||||||
public faSignIn = faSignInAlt;
|
public faSignIn = faSignInAlt;
|
||||||
@@ -36,12 +36,19 @@ export class LoginComponent implements OnInit {
|
|||||||
this.form.updateValueAndValidity();
|
this.form.updateValueAndValidity();
|
||||||
if (this.form.valid) {
|
if (this.form.valid) {
|
||||||
try {
|
try {
|
||||||
const value = this.form.value as {user: string; pass: string};
|
await this.userService.login(this.form.controls.user.value, this.form.controls.pass.value);
|
||||||
await this.userService.login(value.user, value.pass);
|
|
||||||
await this.router.navigateByUrl('/');
|
await this.router.navigateByUrl('/');
|
||||||
} catch ({code}) {
|
} catch (error) {
|
||||||
this.errorMessage = code as string;
|
this.errorMessage = this.errorCode(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private errorCode(error: unknown): string {
|
||||||
|
if (typeof error === 'object' && error !== null && 'code' in error && typeof error.code === 'string') {
|
||||||
|
return error.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown_error';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
<mat-label>Passwort</mat-label>
|
<mat-label>Passwort</mat-label>
|
||||||
<input formControlName="password" matInput type="password" />
|
<input formControlName="password" matInput type="password" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
@if (errorMessage) {
|
||||||
|
<p class="error">{{ errorMessage | authMessage }}</p>
|
||||||
|
}
|
||||||
|
|
||||||
<app-button-row>
|
<app-button-row>
|
||||||
<app-button (click)="onCreate()" [icon]="faNewUser"
|
<app-button (click)="onCreate()" [icon]="faNewUser"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Component, OnInit, inject} from '@angular/core';
|
import {Component, OnInit, inject} from '@angular/core';
|
||||||
import {ReactiveFormsModule, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms';
|
import {FormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
import {UserService} from '../../../services/user/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
import {faUserPlus} from '@fortawesome/free-solid-svg-icons';
|
import {faUserPlus} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {CardComponent} from '../../../widget-modules/components/card/card.component';
|
import {CardComponent} from '../../../widget-modules/components/card/card.component';
|
||||||
@@ -7,23 +7,25 @@ import {MatFormField, MatLabel} from '@angular/material/form-field';
|
|||||||
import {MatInput} from '@angular/material/input';
|
import {MatInput} from '@angular/material/input';
|
||||||
import {ButtonRowComponent} from '../../../widget-modules/components/button-row/button-row.component';
|
import {ButtonRowComponent} from '../../../widget-modules/components/button-row/button-row.component';
|
||||||
import {ButtonComponent} from '../../../widget-modules/components/button/button.component';
|
import {ButtonComponent} from '../../../widget-modules/components/button/button.component';
|
||||||
|
import {AuthMessagePipe} from '../login/auth-message.pipe';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-new',
|
selector: 'app-new',
|
||||||
templateUrl: './new.component.html',
|
templateUrl: './new.component.html',
|
||||||
styleUrls: ['./new.component.less'],
|
styleUrls: ['./new.component.less'],
|
||||||
imports: [CardComponent, ReactiveFormsModule, MatFormField, MatLabel, MatInput, ButtonRowComponent, ButtonComponent],
|
imports: [CardComponent, ReactiveFormsModule, MatFormField, MatLabel, MatInput, ButtonRowComponent, ButtonComponent, AuthMessagePipe],
|
||||||
})
|
})
|
||||||
export class NewComponent implements OnInit {
|
export class NewComponent implements OnInit {
|
||||||
private fb = inject(UntypedFormBuilder);
|
private fb = inject(FormBuilder);
|
||||||
private userService = inject(UserService);
|
private userService = inject(UserService);
|
||||||
|
|
||||||
public form: UntypedFormGroup = this.fb.group({
|
public form = this.fb.nonNullable.group({
|
||||||
email: new UntypedFormControl(null, [Validators.required, Validators.email]),
|
email: ['', [Validators.required, Validators.email]],
|
||||||
name: new UntypedFormControl(null, [Validators.required]),
|
name: ['', [Validators.required]],
|
||||||
password: new UntypedFormControl(null, [Validators.required, Validators.minLength(6)]),
|
password: ['', [Validators.required, Validators.minLength(6)]],
|
||||||
});
|
});
|
||||||
public faNewUser = faUserPlus;
|
public faNewUser = faUserPlus;
|
||||||
|
public errorMessage = '';
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.form.reset();
|
this.form.reset();
|
||||||
@@ -33,15 +35,20 @@ export class NewComponent implements OnInit {
|
|||||||
this.form.updateValueAndValidity();
|
this.form.updateValueAndValidity();
|
||||||
if (this.form.valid) {
|
if (this.form.valid) {
|
||||||
try {
|
try {
|
||||||
const value = this.form.value as {
|
this.errorMessage = '';
|
||||||
email: string;
|
const {email, name, password} = this.form.getRawValue();
|
||||||
name: string;
|
await this.userService.createNewUser(email, name, password);
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
await this.userService.createNewUser(value.email, value.name, value.password);
|
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
console.error(ex);
|
this.errorMessage = this.errorCode(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private errorCode(ex: unknown): string {
|
||||||
|
if (typeof ex === 'object' && ex !== null && 'code' in ex && typeof ex.code === 'string') {
|
||||||
|
return ex.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown_error';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Component, OnInit, inject} from '@angular/core';
|
import {Component, OnInit, inject} from '@angular/core';
|
||||||
import {ReactiveFormsModule, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms';
|
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {UserService} from '../../../services/user/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
import {faWindowRestore} from '@fortawesome/free-solid-svg-icons';
|
import {faWindowRestore} from '@fortawesome/free-solid-svg-icons';
|
||||||
@@ -21,8 +21,8 @@ export class PasswordComponent implements OnInit {
|
|||||||
public userService = inject(UserService);
|
public userService = inject(UserService);
|
||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
|
|
||||||
public form: UntypedFormGroup = new UntypedFormGroup({
|
public form = new FormGroup({
|
||||||
user: new UntypedFormControl(null, [Validators.required, Validators.email]),
|
user: new FormControl<string>('', {nonNullable: true, validators: [Validators.required, Validators.email]}),
|
||||||
});
|
});
|
||||||
|
|
||||||
public errorMessage = '';
|
public errorMessage = '';
|
||||||
@@ -36,12 +36,19 @@ export class PasswordComponent implements OnInit {
|
|||||||
this.form.updateValueAndValidity();
|
this.form.updateValueAndValidity();
|
||||||
if (this.form.valid) {
|
if (this.form.valid) {
|
||||||
try {
|
try {
|
||||||
const value = this.form.value as {user: string};
|
await this.userService.changePassword(this.form.controls.user.value);
|
||||||
await this.userService.changePassword(value.user);
|
|
||||||
await this.router.navigateByUrl('/user/password-send');
|
await this.router.navigateByUrl('/user/password-send');
|
||||||
} catch ({code}) {
|
} catch (error) {
|
||||||
this.errorMessage = code as string;
|
this.errorMessage = this.errorCode(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private errorCode(error: unknown): string {
|
||||||
|
if (typeof error === 'object' && error !== null && 'code' in error && typeof error.code === 'string') {
|
||||||
|
return error.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown_error';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,17 +16,17 @@ function normalize(input: string): string {
|
|||||||
|
|
||||||
export const onlyUnique = <T>(value: T, index: number, array: T[]) => array.indexOf(value) === index;
|
export const onlyUnique = <T>(value: T, index: number, array: T[]) => array.indexOf(value) === index;
|
||||||
|
|
||||||
export function dynamicSort(property: string) {
|
export function dynamicSort<T extends Record<string, string | number>>(property: keyof T | `-${string & keyof T}`) {
|
||||||
let sortOrder = 1;
|
let sortOrder = 1;
|
||||||
if (property[0] === '-') {
|
let resolvedProperty = property as string;
|
||||||
|
if (resolvedProperty[0] === '-') {
|
||||||
sortOrder = -1;
|
sortOrder = -1;
|
||||||
property = property.substr(1);
|
resolvedProperty = resolvedProperty.slice(1);
|
||||||
}
|
}
|
||||||
return function (a: unknown, b: unknown) {
|
return function (a: T, b: T): number {
|
||||||
/* next line works with strings and numbers,
|
const left = a[resolvedProperty as keyof T];
|
||||||
* and you may want to customize it to your needs
|
const right = b[resolvedProperty as keyof T];
|
||||||
*/
|
const result = left < right ? -1 : left > right ? 1 : 0;
|
||||||
const result = a[property] < b[property] ? -1 : a[property] > b[property] ? 1 : 0;
|
|
||||||
return result * sortOrder;
|
return result * sortOrder;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import {Directive, ElementRef, Input, OnInit, TemplateRef, ViewContainerRef, inject} from '@angular/core';
|
import {Directive, DestroyRef, Input, OnInit, TemplateRef, ViewContainerRef, inject} from '@angular/core';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
import {UserService} from './user.service';
|
import {UserService} from './user.service';
|
||||||
|
|
||||||
@Directive({selector: '[appOwner]'})
|
@Directive({selector: '[appOwner]'})
|
||||||
export class OwnerDirective implements OnInit {
|
export class OwnerDirective implements OnInit {
|
||||||
private element = inject(ElementRef);
|
|
||||||
private templateRef = inject<TemplateRef<unknown>>(TemplateRef);
|
private templateRef = inject<TemplateRef<unknown>>(TemplateRef);
|
||||||
private viewContainer = inject(ViewContainerRef);
|
private viewContainer = inject(ViewContainerRef);
|
||||||
private userService = inject(UserService);
|
private userService = inject(UserService);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
private currentUserId: string | null = null;
|
private currentUserId: string | null = null;
|
||||||
private iAppOwner: string | null = null;
|
private iAppOwner: string | null = null;
|
||||||
@@ -18,14 +19,14 @@ export class OwnerDirective implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.userService.userId$.subscribe(user => {
|
this.userService.userId$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(user => {
|
||||||
this.currentUserId = user;
|
this.currentUserId = user;
|
||||||
this.updateView();
|
this.updateView();
|
||||||
});
|
});
|
||||||
this.updateView();
|
this.updateView();
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateView() {
|
private updateView(): void {
|
||||||
this.viewContainer.clear();
|
this.viewContainer.clear();
|
||||||
if (this.currentUserId === this.iAppOwner) {
|
if (this.currentUserId === this.iAppOwner) {
|
||||||
this.viewContainer.createEmbeddedView(this.templateRef);
|
this.viewContainer.createEmbeddedView(this.templateRef);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {ChangeDetectorRef, Directive, ElementRef, Input, OnInit, TemplateRef, ViewContainerRef, inject} from '@angular/core';
|
import {ChangeDetectorRef, DestroyRef, Directive, Input, OnInit, TemplateRef, ViewContainerRef, inject} from '@angular/core';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
import {roles} from './roles';
|
import {roles} from './roles';
|
||||||
import {UserService} from './user.service';
|
import {UserService} from './user.service';
|
||||||
import {User} from './user';
|
import {User} from './user';
|
||||||
@@ -6,11 +7,11 @@ import {combineLatest} from 'rxjs';
|
|||||||
|
|
||||||
@Directive({selector: '[appRole]'})
|
@Directive({selector: '[appRole]'})
|
||||||
export class RoleDirective implements OnInit {
|
export class RoleDirective implements OnInit {
|
||||||
private element = inject(ElementRef);
|
|
||||||
private templateRef = inject<TemplateRef<unknown>>(TemplateRef);
|
private templateRef = inject<TemplateRef<unknown>>(TemplateRef);
|
||||||
private viewContainer = inject(ViewContainerRef);
|
private viewContainer = inject(ViewContainerRef);
|
||||||
private userService = inject(UserService);
|
private userService = inject(UserService);
|
||||||
private changeDetection = inject(ChangeDetectorRef);
|
private changeDetection = inject(ChangeDetectorRef);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
@Input() public appRole: roles[] = [];
|
@Input() public appRole: roles[] = [];
|
||||||
private currentUser: User | null = null;
|
private currentUser: User | null = null;
|
||||||
@@ -18,14 +19,16 @@ export class RoleDirective implements OnInit {
|
|||||||
private currentViewState = false;
|
private currentViewState = false;
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
combineLatest([this.userService.user$, this.userService.loggedIn$()]).subscribe(_ => {
|
combineLatest([this.userService.user$, this.userService.loggedIn$()])
|
||||||
this.currentUser = _[0];
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||||
this.loggedIn = _[1];
|
.subscribe(([user, loggedIn]) => {
|
||||||
|
this.currentUser = user;
|
||||||
|
this.loggedIn = loggedIn;
|
||||||
this.updateView();
|
this.updateView();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateView() {
|
private updateView(): void {
|
||||||
const viewState = this.loggedIn && this.checkPermission();
|
const viewState = this.loggedIn && this.checkPermission();
|
||||||
if (this.currentViewState !== viewState) {
|
if (this.currentViewState !== viewState) {
|
||||||
if (!viewState) this.viewContainer.clear();
|
if (!viewState) this.viewContainer.clear();
|
||||||
@@ -35,7 +38,7 @@ export class RoleDirective implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkPermission() {
|
private checkPermission(): boolean {
|
||||||
if (this.currentUser && this.currentUser.role) {
|
if (this.currentUser && this.currentUser.role) {
|
||||||
if (this.currentUser.role === 'admin') {
|
if (this.currentUser.role === 'admin') {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {ChangeDetectionStrategy, Component, Input, inject} from '@angular/core';
|
import {ChangeDetectionStrategy, Component, Input, inject} from '@angular/core';
|
||||||
import {ReactiveFormsModule, UntypedFormControl} from '@angular/forms';
|
import {FormControl, ReactiveFormsModule} from '@angular/forms';
|
||||||
import {filterSong} from '../../../services/filter.helper';
|
import {filterSong} from '../../../services/filter.helper';
|
||||||
import {MatFormField, MatLabel, MatOption, MatSelect, MatSelectChange} from '@angular/material/select';
|
import {MatFormField, MatLabel, MatOption, MatSelect, MatSelectChange} from '@angular/material/select';
|
||||||
import {Song} from '../../../modules/songs/services/song';
|
import {Song} from '../../../modules/songs/services/song';
|
||||||
@@ -25,7 +25,7 @@ export class AddSongComponent {
|
|||||||
@Input() public showSongs: ShowSong[] | null = null;
|
@Input() public showSongs: ShowSong[] | null = null;
|
||||||
@Input() public show: Show | null = null;
|
@Input() public show: Show | null = null;
|
||||||
@Input() public addedLive = false;
|
@Input() public addedLive = false;
|
||||||
public filteredSongsControl = new UntypedFormControl();
|
public filteredSongsControl = new FormControl<string>('', {nonNullable: true});
|
||||||
|
|
||||||
public filteredSongs(): Song[] {
|
public filteredSongs(): Song[] {
|
||||||
if (!this.songs) return [];
|
if (!this.songs) return [];
|
||||||
@@ -44,7 +44,7 @@ export class AddSongComponent {
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const filterValue = this.filteredSongsControl.value as string;
|
const filterValue = this.filteredSongsControl.value;
|
||||||
return filterValue ? songs.filter(_ => filterSong(_, filterValue)) : songs;
|
return filterValue ? songs.filter(_ => filterSong(_, filterValue)) : songs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {Component, inject} from '@angular/core';
|
import {Component, DestroyRef, inject} from '@angular/core';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
import {ActivatedRoute, Params, Router} from '@angular/router';
|
import {ActivatedRoute, Params, Router} from '@angular/router';
|
||||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||||
|
|
||||||
@@ -10,13 +11,14 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
|||||||
})
|
})
|
||||||
export class FilterComponent {
|
export class FilterComponent {
|
||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
public value = '';
|
public value = '';
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
const activatedRoute = inject(ActivatedRoute);
|
const activatedRoute = inject(ActivatedRoute);
|
||||||
|
|
||||||
activatedRoute.queryParams.subscribe((params: Params) => {
|
activatedRoute.queryParams.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params: Params) => {
|
||||||
const typedParams = params as {q: string};
|
const typedParams = params as {q: string};
|
||||||
if (typedParams.q) this.value = typedParams.q;
|
if (typedParams.q) this.value = typedParams.q;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren, inject} from '@angular/core';
|
import {ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, QueryList, ViewChildren, inject} from '@angular/core';
|
||||||
import {TextRenderingService} from '../../../modules/songs/services/text-rendering.service';
|
import {TextRenderingService} from '../../../modules/songs/services/text-rendering.service';
|
||||||
import {faGripLines} from '@fortawesome/free-solid-svg-icons';
|
import {faGripLines} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {songSwitch} from './animation';
|
import {songSwitch} from './animation';
|
||||||
@@ -27,7 +27,7 @@ interface DisplaySegment {
|
|||||||
animations: [songSwitch],
|
animations: [songSwitch],
|
||||||
imports: [MatIconButton, FaIconComponent],
|
imports: [MatIconButton, FaIconComponent],
|
||||||
})
|
})
|
||||||
export class SongTextComponent implements OnInit {
|
export class SongTextComponent implements OnInit, OnDestroy {
|
||||||
private textRenderingService = inject(TextRenderingService);
|
private textRenderingService = inject(TextRenderingService);
|
||||||
private elRef = inject<ElementRef<HTMLElement>>(ElementRef);
|
private elRef = inject<ElementRef<HTMLElement>>(ElementRef);
|
||||||
private cRef = inject(ChangeDetectorRef);
|
private cRef = inject(ChangeDetectorRef);
|
||||||
@@ -47,6 +47,7 @@ export class SongTextComponent implements OnInit {
|
|||||||
private invalidChordIssuesByLine = new Map<number, ChordValidationIssue[]>();
|
private invalidChordIssuesByLine = new Map<number, ChordValidationIssue[]>();
|
||||||
private iText = '';
|
private iText = '';
|
||||||
private iTranspose: TransposeMode | null = null;
|
private iTranspose: TransposeMode | null = null;
|
||||||
|
private offsetIntervalId: ReturnType<typeof setInterval> | null = null;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
public set chordMode(value: ChordMode) {
|
public set chordMode(value: ChordMode) {
|
||||||
@@ -73,7 +74,7 @@ export class SongTextComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
setInterval(() => {
|
this.offsetIntervalId = setInterval(() => {
|
||||||
if (!this.fullscreen || this.index === -1 || !this.viewSections?.toArray()[this.index]) {
|
if (!this.fullscreen || this.index === -1 || !this.viewSections?.toArray()[this.index]) {
|
||||||
this.offset = 0;
|
this.offset = 0;
|
||||||
this.cRef.markForCheck();
|
this.cRef.markForCheck();
|
||||||
@@ -84,6 +85,12 @@ export class SongTextComponent implements OnInit {
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
if (this.offsetIntervalId) {
|
||||||
|
clearInterval(this.offsetIntervalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getLines(section: Section): Line[] {
|
public getLines(section: Section): Line[] {
|
||||||
return section.lines.filter(_ => {
|
return section.lines.filter(_ => {
|
||||||
if (_.type !== LineType.chord) {
|
if (_.type !== LineType.chord) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {Injectable, inject} from '@angular/core';
|
|||||||
import {ActivatedRouteSnapshot, Router, UrlTree} from '@angular/router';
|
import {ActivatedRouteSnapshot, Router, UrlTree} from '@angular/router';
|
||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {UserService} from '../../services/user/user.service';
|
import {UserService} from '../../services/user/user.service';
|
||||||
import {map} from 'rxjs/operators';
|
import {map, take} from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -18,8 +18,11 @@ export class RoleGuard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this.userService.user$.pipe(
|
return this.userService.user$.pipe(
|
||||||
|
take(1),
|
||||||
map(user => {
|
map(user => {
|
||||||
if (!user) return false;
|
if (!user) {
|
||||||
|
return this.router.createUrlTree(['brand', 'new-user']);
|
||||||
|
}
|
||||||
const roles = user.role?.split(';') ?? [];
|
const roles = user.role?.split(';') ?? [];
|
||||||
if (roles.indexOf('admin') !== -1) {
|
if (roles.indexOf('admin') !== -1) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ export class SectionTypePipe implements PipeTransform {
|
|||||||
return 'Refrain';
|
return 'Refrain';
|
||||||
case SectionType.Bridge:
|
case SectionType.Bridge:
|
||||||
return 'Bridge';
|
return 'Bridge';
|
||||||
|
case SectionType.Comment:
|
||||||
|
return 'Kommentar';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"strict": false,
|
"strict": true,
|
||||||
"typeRoots": ["node_modules/@types"],
|
"typeRoots": ["node_modules/@types"],
|
||||||
"useDefineForClassFields": false
|
"useDefineForClassFields": false
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user