From cb2c028ca47bdd9af2a5a7e79274d5f58f19ff52 Mon Sep 17 00:00:00 2001 From: smuddyx Date: Sat, 22 May 2021 15:30:04 +0200 Subject: [PATCH] activated typescript strict mode --- .prettierrc | 2 +- package-lock.json | 5 -- package.json | 1 - src/app/app.component.ts | 6 +-- .../brand/new-user/new-user.component.ts | 2 +- src/app/modules/guest/guest.component.ts | 26 ++++++++-- .../monitor/legal/legal.component.html | 24 ++++----- .../monitor/legal/legal.component.ts | 4 +- .../monitor/monitor.component.html | 2 +- .../presentation/monitor/monitor.component.ts | 47 +++++++++++------- .../presentation/remote/remote.component.html | 2 +- .../presentation/remote/remote.component.ts | 26 +++++----- .../list/list-item/list-item.component.html | 2 +- .../list/list-item/list-item.component.ts | 2 +- src/app/modules/shows/new/new.component.ts | 12 ++--- .../modules/shows/services/docx.service.ts | 49 +++++++++++++++---- .../shows/services/show-data.service.ts | 5 +- .../shows/services/show-song-data.service.ts | 12 +++-- .../shows/services/show-song.service.ts | 15 ++++-- .../modules/shows/services/show.service.ts | 37 +++++++++++--- src/app/modules/shows/show/show.component.ts | 34 +++++++------ .../shows/show/song/song.component.html | 2 +- .../modules/shows/show/song/song.component.ts | 26 ++++++---- src/app/modules/songs/services/chord.ts | 4 +- src/app/modules/songs/services/line.ts | 2 +- .../songs/services/song-data.service.ts | 8 +-- .../modules/songs/services/song.service.ts | 6 ++- .../songs/services/text-rendering.service.ts | 37 ++++++++------ .../songs/services/transpose.service.ts | 23 ++++++--- .../modules/songs/services/upload.service.ts | 2 +- src/app/modules/songs/services/upload.ts | 7 ++- .../song-list/filter/filter.component.ts | 7 +-- .../list-item/list-item.component.html | 2 +- .../list-item/list-item.component.ts | 2 +- .../songs/song-list/song-list.component.ts | 8 ++- .../edit/edit-file/edit-file.component.ts | 28 ++++++++--- .../edit/edit-file/file/file.component.ts | 12 ++--- .../songs/song/edit/edit-song.guard.ts | 6 +-- .../edit/edit-song/edit-song.component.ts | 21 +++++--- .../modules/songs/song/edit/edit.component.ts | 2 +- .../modules/songs/song/edit/edit.module.ts | 9 +++- .../song/edit/history/history.component.ts | 5 +- .../modules/songs/song/file/file.component.ts | 4 +- .../modules/songs/song/new/new.component.ts | 32 ++++++------ src/app/modules/songs/song/new/new.module.ts | 11 ++++- src/app/modules/songs/song/song.component.ts | 20 +++++--- src/app/modules/user/info/info.component.ts | 2 +- src/app/modules/user/info/role.pipe.ts | 2 + .../user/info/users/user/user.component.ts | 6 +-- src/app/modules/user/login/login.component.ts | 12 ++--- src/app/modules/user/new/new.component.ts | 12 ++--- .../user/password/password.component.ts | 15 +++--- src/app/modules/user/user.module.ts | 13 ++++- src/app/services/config.service.ts | 4 +- src/app/services/config.ts | 1 + src/app/services/db.service.ts | 7 ++- src/app/services/filter.helper.ts | 4 +- src/app/services/global-settings.service.ts | 2 +- src/app/services/scroll.service.ts | 2 +- src/app/services/user/owner.directive.ts | 12 +++-- src/app/services/user/role.directive.ts | 11 +++-- .../user/user-name/user-name.component.ts | 4 +- src/app/services/user/user.service.ts | 31 ++++++------ .../components/add-song/add-song.component.ts | 8 +-- .../navigation/filter/filter.component.ts | 11 ++--- .../navigation/link/link.component.ts | 7 +-- .../navigation/navigation.component.html | 2 +- .../navigation/navigation.component.ts | 6 ++- .../components/button/button.component.ts | 3 +- .../components/card/card.component.ts | 4 +- .../menu-button/menu-button.component.ts | 3 +- .../song-text/song-text.component.ts | 17 ++++--- src/app/widget-modules/guards/role.guard.ts | 1 + .../show-type-translater/show-type.pipe.ts | 2 + src/styles/styles.less | 1 + tsconfig.json | 1 + 76 files changed, 511 insertions(+), 296 deletions(-) diff --git a/.prettierrc b/.prettierrc index a1bafcd..3b0e330 100644 --- a/.prettierrc +++ b/.prettierrc @@ -8,7 +8,7 @@ "bracketSpacing": false, "arrowParens": "avoid", "jsxBracketSameLine": false, - "printWidth": 200, + "printWidth": 120, "overrides": [ { "files": "*.component.html", diff --git a/package-lock.json b/package-lock.json index 64e9095..2c0bb46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12546,11 +12546,6 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, - "ngx-hocs-unsubscriber": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/ngx-hocs-unsubscriber/-/ngx-hocs-unsubscriber-1.1.7.tgz", - "integrity": "sha512-FPasZvptGjzcnWzlUJcVhqq+mFi0bGt4/txTHe0XX6kDlIRX+9yfnPPDXvUvSSj6yXeuWDfnh9kn5Fr74W9Ohw==" - }, "ngx-mat-select-search": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-3.3.0.tgz", diff --git a/package.json b/package.json index 625e49c..70c5e89 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "@fortawesome/free-solid-svg-icons": "^5.13.0", "docx": "^6.0.3", "firebase": "^7.24.0", - "ngx-hocs-unsubscriber": "^1.1.7", "ngx-mat-select-search": "^3.3.0", "ngx-perfect-scrollbar": "^10.1.1", "ngx-swiper-wrapper": "^10.0.0", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index ff0513c..88c5550 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -10,15 +10,15 @@ import {PerfectScrollbarComponent} from 'ngx-perfect-scrollbar'; animations: [fader], }) export class AppComponent implements OnInit { - @ViewChild('scrollbar', {static: false}) public scrollbar: PerfectScrollbarComponent; + @ViewChild('scrollbar', {static: false}) public scrollbar: PerfectScrollbarComponent | null = null; public constructor(private scrollService: ScrollService) { scrollService.restoreScrollPosition$.subscribe(pos => { - if (this.scrollbar && pos) this.scrollbar.directiveRef.scrollTo(0, pos, 300); + if (this.scrollbar && pos) this.scrollbar.directiveRef?.scrollTo(0, pos, 300); }); } - public static hideLoader: () => void = () => document.querySelector('#load-bg').classList.add('hidden'); + public static hideLoader: () => void = () => document.querySelector('#load-bg')?.classList.add('hidden'); public ngOnInit(): void { setTimeout(() => AppComponent.hideLoader(), 800); diff --git a/src/app/modules/brand/new-user/new-user.component.ts b/src/app/modules/brand/new-user/new-user.component.ts index 39ceb1c..52737dc 100644 --- a/src/app/modules/brand/new-user/new-user.component.ts +++ b/src/app/modules/brand/new-user/new-user.component.ts @@ -9,7 +9,7 @@ import {User} from '../../../services/user/user'; styleUrls: ['./new-user.component.less'], }) export class NewUserComponent { - public user$: Observable; + public user$: Observable | null = null; public constructor(private userService: UserService) { this.user$ = userService.user$; diff --git a/src/app/modules/guest/guest.component.ts b/src/app/modules/guest/guest.component.ts index 09ea42e..16523a2 100644 --- a/src/app/modules/guest/guest.component.ts +++ b/src/app/modules/guest/guest.component.ts @@ -2,8 +2,10 @@ import {Component, OnInit} from '@angular/core'; import {SongService} from '../songs/services/song.service'; import {GlobalSettingsService} from '../../services/global-settings.service'; import {Observable} from 'rxjs'; -import {map, switchMap} from 'rxjs/operators'; +import {filter, map, switchMap} from 'rxjs/operators'; import {ShowSongService} from '../shows/services/show-song.service'; +import {GlobalSettings} from '../../services/global-settings'; +import {Song} from '../songs/services/song'; @Component({ selector: 'app-guest', @@ -11,15 +13,31 @@ import {ShowSongService} from '../shows/services/show-song.service'; styleUrls: ['./guest.component.less'], }) export class GuestComponent implements OnInit { - public songs$: Observable[]>; + public songs$: Observable[]> | null = null; - public constructor(private songService: SongService, private globalSettingsService: GlobalSettingsService, private showSongService: ShowSongService) {} + public constructor( + private songService: SongService, + private globalSettingsService: GlobalSettingsService, + private showSongService: ShowSongService + ) {} public ngOnInit(): void { this.songs$ = this.globalSettingsService.get$.pipe( + filter(_ => !!_), + map(_ => _ as GlobalSettings), map(_ => _.currentShow), switchMap(_ => this.showSongService.list$(_)), - map(_ => _.sort((x, y) => x.order - y.order).map(showSong => this.songService.read$(showSong.songId).pipe(map(song => song.text)))) + filter(_ => !!_), + map(_ => _), + map(_ => + _.sort((x, y) => x.order - y.order).map(showSong => + this.songService.read$(showSong.songId).pipe( + filter(_ => !!_), + map(_ => _ as Song), + map(song => song.text) + ) + ) + ) ); } } diff --git a/src/app/modules/presentation/monitor/legal/legal.component.html b/src/app/modules/presentation/monitor/legal/legal.component.html index 7e6073f..ef90744 100644 --- a/src/app/modules/presentation/monitor/legal/legal.component.html +++ b/src/app/modules/presentation/monitor/legal/legal.component.html @@ -1,12 +1,14 @@ -

{{ song.artist }}

-

{{ song.label }}

-

{{ song.termsOfUse }}

-

{{ song.origin }}

+ +

{{ song.artist }}

+

{{ song.label }}

+

{{ song.termsOfUse }}

+

{{ song.origin }}

-
-

- CCLI-Liednummer {{ song.legalOwnerId }}, CCLI-Lizenznummer - {{ config.ccliLicenseId }} -

-

Liednummer {{ song.legalOwnerId }}

-
+
+

+ CCLI-Liednummer {{ song.legalOwnerId }}, CCLI-Lizenznummer + {{ config.ccliLicenseId }} +

+

Liednummer {{ song.legalOwnerId }}

+
+
diff --git a/src/app/modules/presentation/monitor/legal/legal.component.ts b/src/app/modules/presentation/monitor/legal/legal.component.ts index 18cb8ec..c03296c 100644 --- a/src/app/modules/presentation/monitor/legal/legal.component.ts +++ b/src/app/modules/presentation/monitor/legal/legal.component.ts @@ -8,6 +8,6 @@ import {Config} from '../../../../services/config'; styleUrls: ['./legal.component.less'], }) export class LegalComponent { - @Input() public song: Song; - @Input() public config: Config; + @Input() public song: Song | null = null; + @Input() public config: Config | null = null; } diff --git a/src/app/modules/presentation/monitor/monitor.component.html b/src/app/modules/presentation/monitor/monitor.component.html index f917fb2..51acadb 100644 --- a/src/app/modules/presentation/monitor/monitor.component.html +++ b/src/app/modules/presentation/monitor/monitor.component.html @@ -1,4 +1,4 @@ -
+
; + public song: Song | null = null; + public zoom = 10; + public currentShowId: string | null = null; + public songId: string | null = null; + public index: number | null = null; + public showType: string | null = null; + public date: Date | null = null; + public config$: Observable; // private sections: Section[]; @@ -42,18 +43,28 @@ export class MonitorComponent implements OnInit { public ngOnInit(): void { this.globalSettingsService.get$ .pipe( + filter(_ => !!_), + map(_ => _ as GlobalSettings), map(_ => _.currentShow), distinctUntilChanged(), - tap(_ => (this.currentShowId = _)), - switchMap(_ => this.showService.read$(_)), - tap(_ => (this.showType = _.showType)), - tap(_ => (this.date = _.date.toDate())), - tap(_ => (this.songId = _.presentationSongId)), - tap(_ => (this.index = _.presentationSection)), - tap(_ => (this.zoom = _.presentationZoom ?? 30)), - switchMap((_: Show) => this.songService.read$(_.presentationSongId)) + tap(_ => (this.currentShowId = _)) ) - .subscribe((_: Song) => { + .pipe( + switchMap(_ => this.showService.read$(_)), + filter(_ => !!_), + map(_ => _ as Show), + tap(_ => (this.showType = _.showType)), + tap(_ => (this.date = _.date.toDate())), + tap(_ => (this.songId = _.presentationSongId)), + tap(_ => (this.index = _.presentationSection)), + tap(_ => (this.zoom = _.presentationZoom ?? 30)) + ) + .pipe( + switchMap((_: Show) => this.songService.read$(_.presentationSongId)), + filter(_ => !!_), + map(_ => _ as Song) + ) + .subscribe(_ => { this.song = _; // this.sections = this.textRenderingService.parse(_.text, null); }); diff --git a/src/app/modules/presentation/remote/remote.component.html b/src/app/modules/presentation/remote/remote.component.html index 195257e..d6729de 100644 --- a/src/app/modules/presentation/remote/remote.component.html +++ b/src/app/modules/presentation/remote/remote.component.html @@ -33,7 +33,7 @@
-
diff --git a/src/app/modules/presentation/remote/remote.component.ts b/src/app/modules/presentation/remote/remote.component.ts index f16dbe3..211c9c1 100644 --- a/src/app/modules/presentation/remote/remote.component.ts +++ b/src/app/modules/presentation/remote/remote.component.ts @@ -9,11 +9,12 @@ import {ShowService} from '../../shows/services/show.service'; import {ShowSong} from '../../shows/services/show-song'; import {GlobalSettingsService} from '../../../services/global-settings.service'; import {FormControl} from '@angular/forms'; -import {distinctUntilChanged, map} from 'rxjs/operators'; +import {distinctUntilChanged, filter, map} from 'rxjs/operators'; import {fade} from '../../../animations'; import {delay} from '../../../services/delay'; import {TextRenderingService} from '../../songs/services/text-rendering.service'; import {Section} from '../../songs/services/section'; +import {GlobalSettings} from '../../../services/global-settings'; export interface PresentationSong { id: string; @@ -29,11 +30,11 @@ export interface PresentationSong { }) export class RemoteComponent { public shows$: Observable; - public show: Show; - public showSongs: ShowSong[]; - public songs: Song[]; - public presentationSongs: PresentationSong[]; - public currentShowId: string; + public show: Show | null = null; + public showSongs: ShowSong[] = []; + public songs: Song[] = []; + public presentationSongs: PresentationSong[] = []; + public currentShowId: string | null = null; public progress = false; public faDesktop = faDesktop; @@ -51,6 +52,8 @@ export class RemoteComponent { globalSettingsService.get$ .pipe( + filter(_ => !!_), + map(_ => _ as GlobalSettings), map(_ => _.currentShow), distinctUntilChanged() ) @@ -88,13 +91,14 @@ export class RemoteComponent { } public async onSectionClick(id: string, index: number): Promise { - await this.showService.update$(this.currentShowId, { - presentationSongId: id, - presentationSection: index, - }); + if (this.currentShowId != null) + await this.showService.update$(this.currentShowId, { + presentationSongId: id, + presentationSection: index, + }); } public async onZoom(zoom: number): Promise { - await this.showService.update$(this.currentShowId, {presentationZoom: zoom}); + if (this.currentShowId != null) await this.showService.update$(this.currentShowId, {presentationZoom: zoom}); } } diff --git a/src/app/modules/shows/list/list-item/list-item.component.html b/src/app/modules/shows/list/list-item/list-item.component.html index ffa634b..2dbac2f 100644 --- a/src/app/modules/shows/list/list-item/list-item.component.html +++ b/src/app/modules/shows/list/list-item/list-item.component.html @@ -1,4 +1,4 @@ -
+
{{ show.date.toDate() | date: "dd.MM.yyyy" }}
{{ show.showType | showType }}
diff --git a/src/app/modules/shows/list/list-item/list-item.component.ts b/src/app/modules/shows/list/list-item/list-item.component.ts index 466ae9a..fa48af7 100644 --- a/src/app/modules/shows/list/list-item/list-item.component.ts +++ b/src/app/modules/shows/list/list-item/list-item.component.ts @@ -7,5 +7,5 @@ import {Show} from '../../services/show'; styleUrls: ['./list-item.component.less'], }) export class ListItemComponent { - @Input() public show: Show; + @Input() public show: Show | null = null; } diff --git a/src/app/modules/shows/new/new.component.ts b/src/app/modules/shows/new/new.component.ts index e52a6d7..e0551bb 100644 --- a/src/app/modules/shows/new/new.component.ts +++ b/src/app/modules/shows/new/new.component.ts @@ -16,7 +16,10 @@ export class NewComponent implements OnInit { public shows$: Observable; public showTypePublic = ShowService.SHOW_TYPE_PUBLIC; public showTypePrivate = ShowService.SHOW_TYPE_PRIVATE; - public form: FormGroup; + public form: FormGroup = new FormGroup({ + date: new FormControl(null, Validators.required), + showType: new FormControl(null, Validators.required), + }); public faSave = faSave; public constructor(private showService: ShowService, showDataService: ShowDataService, private router: Router) { @@ -24,10 +27,7 @@ export class NewComponent implements OnInit { } public ngOnInit(): void { - this.form = new FormGroup({ - date: new FormControl(null, Validators.required), - showType: new FormControl(null, Validators.required), - }); + this.form.reset(); } public async onSave(): Promise { @@ -37,6 +37,6 @@ export class NewComponent implements OnInit { } const id = await this.showService.new$(this.form.value); - await this.router.navigateByUrl('/shows/' + id); + await this.router.navigateByUrl(`/shows/${id ?? ''}`); } } diff --git a/src/app/modules/shows/services/docx.service.ts b/src/app/modules/shows/services/docx.service.ts index 3f850f7..e4672fe 100644 --- a/src/app/modules/shows/services/docx.service.ts +++ b/src/app/modules/shows/services/docx.service.ts @@ -37,7 +37,9 @@ export class DocxService { ) {} public async create(showId: string, options: DownloadOptions = {}): Promise { - const {show, songs, user, config} = await this.prepareData(showId); + const data = await this.prepareData(showId); + if (!data) return; + const {show, songs, user, config} = data; const type = new ShowTypePipe().transform(show.showType); const title = `${type} ${show.date.toDate().toLocaleDateString()}`; @@ -61,7 +63,12 @@ export class DocxService { this.saveAs(blob, `${title}.docx`); } - private prepareNewDocument(type: string, name: string, options: DownloadOptions, sections: ISectionOptions[]): Document { + private prepareNewDocument( + type: string, + name: string, + options: DownloadOptions, + sections: ISectionOptions[] + ): Document { return new Document({ creator: name, title: type, @@ -92,16 +99,29 @@ export class DocxService { }); } - private renderSongs(songs: {showSong: ShowSong; song: Song; sections: Section[]}[], options: DownloadOptions, config: Config): Paragraph[] { - return songs.reduce((p: Paragraph[], song) => [...p, ...this.renderSong(song.showSong, song.song, song.sections, options, config)], []); + private renderSongs( + songs: {showSong: ShowSong; song: Song; sections: Section[]}[], + options: DownloadOptions, + config: Config + ): Paragraph[] { + return songs.reduce( + (p: Paragraph[], song) => [...p, ...this.renderSong(song.showSong, song.song, song.sections, options, config)], + [] + ); } - private renderSong(showSong: ShowSong, song: Song, sections: Section[], options: DownloadOptions, config: Config): Paragraph[] { + private renderSong( + showSong: ShowSong, + song: Song, + sections: Section[], + options: DownloadOptions, + config: Config + ): Paragraph[] { const songTitle = this.renderSongTitle(song); const copyright = this.renderCopyright(song, options, config); const songText = this.renderSongText(sections, options?.chordMode ?? showSong.chordMode); - return [songTitle, copyright, ...songText].filter(_ => _); + return copyright ? [songTitle, copyright, ...songText] : [songTitle, ...songText]; } private renderSongText(sections: Section[], chordMode: ChordMode): Paragraph[] { @@ -119,7 +139,7 @@ export class DocxService { }); } - private renderCopyright(song: Song, options: DownloadOptions, config: Config): Paragraph { + private renderCopyright(song: Song, options: DownloadOptions, config: Config): Paragraph | null { if (!options?.copyright) { return null; } @@ -128,7 +148,10 @@ export class DocxService { const artist = song.artist ? song.artist + ', ' : ''; const termsOfUse = song.termsOfUse ? song.termsOfUse + ', ' : ''; const origin = song.origin ? song.origin + ', ' : ''; - const licence = song.legalOwner === 'CCLI' ? 'CCLI-Liednummer: ' + song.legalOwnerId + ', CCLI-Lizenz: ' + config.ccliLicenseId : 'CCLI-Liednummer: ' + song.legalOwnerId; + const licence = + song.legalOwner === 'CCLI' + ? 'CCLI-Liednummer: ' + song.legalOwnerId + ', CCLI-Lizenz: ' + config.ccliLicenseId + : 'CCLI-Liednummer: ' + song.legalOwnerId; return new Paragraph({ text: artist + label + termsOfUse + origin + licence, @@ -178,14 +201,18 @@ export class DocxService { show: Show; user: User; config: Config; - }> { + } | null> { const show = await this.showService.read$(showId).pipe(first()).toPromise(); + if (!show) return null; const user = await this.userService.getUserbyId(show.owner); + if (!user) return null; const config = await this.configService.get(); + if (!config) return null; const showSongs = await this.showSongService.list(showId); const songsAsync = showSongs.map(async showSong => { const song = await this.songService.read(showSong.songId); + if (!song) return null; const sections = this.textRenderingService.parse(song.text, { baseKey: showSong.keyOriginal, targetKey: showSong.key, @@ -196,7 +223,9 @@ export class DocxService { sections, }; }); - const songs = await Promise.all(songsAsync); + const songs = (await Promise.all(songsAsync)) + .filter(_ => !!_) + .map(_ => _ as {showSong: ShowSong; song: Song; sections: Section[]}); return {songs, show, user, config}; } diff --git a/src/app/modules/shows/services/show-data.service.ts b/src/app/modules/shows/services/show-data.service.ts index 2d507a5..40d4c4f 100644 --- a/src/app/modules/shows/services/show-data.service.ts +++ b/src/app/modules/shows/services/show-data.service.ts @@ -13,7 +13,8 @@ export class ShowDataService { public constructor(private dbService: DbService) {} public list$ = (queryFn?: QueryFn): Observable => this.dbService.col$(this.collection, queryFn); - public read$ = (showId: string): Observable => this.dbService.doc$(`${this.collection}/${showId}`); - public update = async (showId: string, data: Partial): Promise => await this.dbService.doc(`${this.collection}/${showId}`).update(data); + public read$ = (showId: string): Observable => this.dbService.doc$(`${this.collection}/${showId}`); + public update = async (showId: string, data: Partial): Promise => + await this.dbService.doc(`${this.collection}/${showId}`).update(data); public add = async (data: Partial): Promise => (await this.dbService.col(this.collection).add(data)).id; } diff --git a/src/app/modules/shows/services/show-song-data.service.ts b/src/app/modules/shows/services/show-song-data.service.ts index 7f9887f..8b064a7 100644 --- a/src/app/modules/shows/services/show-song-data.service.ts +++ b/src/app/modules/shows/services/show-song-data.service.ts @@ -13,10 +13,14 @@ export class ShowSongDataService { public constructor(private dbService: DbService) {} - public list$ = (showId: string, queryFn?: QueryFn): Observable => this.dbService.col$(`${this.collection}/${showId}/${this.subCollection}`, queryFn); - public read$ = (showId: string, songId: string): Observable => this.dbService.doc$(`${this.collection}/${showId}/${this.subCollection}/${songId}`); + public list$ = (showId: string, queryFn?: QueryFn): Observable => + this.dbService.col$(`${this.collection}/${showId}/${this.subCollection}`, queryFn); + public read$ = (showId: string, songId: string): Observable => + this.dbService.doc$(`${this.collection}/${showId}/${this.subCollection}/${songId}`); public update$ = async (showId: string, songId: string, data: Partial): Promise => await this.dbService.doc(`${this.collection}/${showId}/${this.subCollection}/${songId}`).update(data); - public delete = async (showId: string, songId: string): Promise => await this.dbService.doc(`${this.collection}/${showId}/${this.subCollection}/${songId}`).delete(); - public add = async (showId: string, data: Partial): Promise => (await this.dbService.col(`${this.collection}/${showId}/${this.subCollection}`).add(data)).id; + public delete = async (showId: string, songId: string): Promise => + await this.dbService.doc(`${this.collection}/${showId}/${this.subCollection}/${songId}`).delete(); + public add = async (showId: string, data: Partial): Promise => + (await this.dbService.col(`${this.collection}/${showId}/${this.subCollection}`).add(data)).id; } diff --git a/src/app/modules/shows/services/show-song.service.ts b/src/app/modules/shows/services/show-song.service.ts index 65695fd..92903b8 100644 --- a/src/app/modules/shows/services/show-song.service.ts +++ b/src/app/modules/shows/services/show-song.service.ts @@ -10,11 +10,16 @@ import {UserService} from '../../../services/user/user.service'; providedIn: 'root', }) export class ShowSongService { - public constructor(private showSongDataService: ShowSongDataService, private songDataService: SongDataService, private userService: UserService) {} + public constructor( + private showSongDataService: ShowSongDataService, + private songDataService: SongDataService, + private userService: UserService + ) {} - public async new$(showId: string, songId: string, order: number, addedLive = false): Promise { + public async new$(showId: string, songId: string, order: number, addedLive = false): Promise { const song = await this.songDataService.read$(songId).pipe(take(1)).toPromise(); const user = await this.userService.user$.pipe(take(1)).toPromise(); + if (!song || !user) return null; const data: Partial = { songId, order, @@ -26,8 +31,10 @@ export class ShowSongService { return await this.showSongDataService.add(showId, data); } - public list$ = (showId: string): Observable => this.showSongDataService.list$(showId, _ => _.orderBy('order')); + public list$ = (showId: string): Observable => + this.showSongDataService.list$(showId, _ => _.orderBy('order')); public list = (showId: string): Promise => this.list$(showId).pipe(first()).toPromise(); public delete$ = (showId: string, songId: string): Promise => this.showSongDataService.delete(showId, songId); - public update$ = async (showId: string, songId: string, data: Partial): Promise => await this.showSongDataService.update$(showId, songId, data); + public update$ = async (showId: string, songId: string, data: Partial): Promise => + await this.showSongDataService.update$(showId, songId, data); } diff --git a/src/app/modules/shows/services/show.service.ts b/src/app/modules/shows/services/show.service.ts index ba0ff79..3a9c324 100644 --- a/src/app/modules/shows/services/show.service.ts +++ b/src/app/modules/shows/services/show.service.ts @@ -10,30 +10,51 @@ import {User} from '../../../services/user/user'; providedIn: 'root', }) export class ShowService { - public static SHOW_TYPE = ['service-worship', 'service-praise', 'home-group-big', 'home-group', 'prayer-group', 'teens-group', 'kids-group', 'misc-public', 'misc-private']; - public static SHOW_TYPE_PUBLIC = ['service-worship', 'service-praise', 'home-group-big', 'teens-group', 'kids-group', 'misc-public']; + public static SHOW_TYPE = [ + 'service-worship', + 'service-praise', + 'home-group-big', + 'home-group', + 'prayer-group', + 'teens-group', + 'kids-group', + 'misc-public', + 'misc-private', + ]; + public static SHOW_TYPE_PUBLIC = [ + 'service-worship', + 'service-praise', + 'home-group-big', + 'teens-group', + 'kids-group', + 'misc-public', + ]; public static SHOW_TYPE_PRIVATE = ['home-group', 'prayer-group', 'misc-private']; - private user: User; + private user: User | null = null; public constructor(private showDataService: ShowDataService, private userService: UserService) { userService.user$.subscribe(_ => (this.user = _)); } - public read$ = (showId: string): Observable => this.showDataService.read$(showId); + public read$ = (showId: string): Observable => this.showDataService.read$(showId); public list$(publishedOnly = false): Observable { return this.userService.user$.pipe( switchMap( () => this.showDataService.list$(), - (user: User, shows: Show[]) => ({user, shows}) + (user: User | null, shows: Show[]) => ({user, shows}) ), - map(s => s.shows.filter(_ => !_.archived).filter(show => show.published || (show.owner === s.user.id && !publishedOnly))) + map(s => + s.shows.filter(_ => !_.archived).filter(show => show.published || (show.owner === s.user?.id && !publishedOnly)) + ) ); } - public update$ = async (showId: string, data: Partial): Promise => this.showDataService.update(showId, data); + public update$ = async (showId: string, data: Partial): Promise => + this.showDataService.update(showId, data); - public async new$(data: Partial): Promise { + public async new$(data: Partial): Promise { + if (!data.showType || !this.user) return null; const calculatedData: Partial = { ...data, owner: this.user.id, diff --git a/src/app/modules/shows/show/show.component.ts b/src/app/modules/shows/show/show.component.ts index e508dc5..f87d46e 100644 --- a/src/app/modules/shows/show/show.component.ts +++ b/src/app/modules/shows/show/show.component.ts @@ -23,11 +23,11 @@ import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers'; styleUrls: ['./show.component.less'], }) export class ShowComponent implements OnInit { - public show$: Observable; - public songs: Song[]; - public showSongs: ShowSong[]; - public showId: string; - public showText: boolean; + public show$: Observable | null = null; + public songs: Song[] | null = null; + public showSongs: ShowSong[] | null = null; + public showId: string | null = null; + public showText = false; public faBox = faBox; public faBoxOpen = faBoxOpen; @@ -47,13 +47,15 @@ export class ShowComponent implements OnInit { public ngOnInit(): void { this.show$ = this.activatedRoute.params.pipe( - map((param: {showId: string}) => param.showId), + map(param => param as {showId: string}), + map(param => param.showId), tap((_: string) => (this.showId = _)), switchMap((showId: string) => this.showService.read$(showId)) ); this.activatedRoute.params .pipe( - map((param: {showId: string}) => param.showId), + map(param => param as {showId: string}), + map(param => param.showId), switchMap(showId => this.showSongService.list$(showId)), filter(_ => !!_) ) @@ -64,17 +66,18 @@ export class ShowComponent implements OnInit { .subscribe(_ => (this.songs = _)); } - public getSong(songId: string): Song { + public getSong(songId: string): Song | null { + if (!this.songs) return null; const filtered = this.songs.filter(_ => _.id === songId); return filtered.length > 0 ? filtered[0] : null; } public async onArchive(archived: boolean): Promise { - await this.showService.update$(this.showId, {archived}); + if (this.showId != null) await this.showService.update$(this.showId, {archived}); } public async onPublish(published: boolean): Promise { - await this.showService.update$(this.showId, {published}); + if (this.showId != null) await this.showService.update$(this.showId, {published}); } public getStatus(show: Show): string { @@ -88,13 +91,14 @@ export class ShowComponent implements OnInit { } public async onDownload(): Promise { - await this.docxService.create(this.showId); + if (this.showId != null) await this.docxService.create(this.showId); } public async onDownloadHandout(): Promise { - await this.docxService.create(this.showId, { - chordMode: 'hide', - copyright: true, - }); + if (this.showId != null) + await this.docxService.create(this.showId, { + chordMode: 'hide', + copyright: true, + }); } } diff --git a/src/app/modules/shows/show/song/song.component.html b/src/app/modules/shows/show/song/song.component.html index a02cb09..a5d15a4 100644 --- a/src/app/modules/shows/show/song/song.component.html +++ b/src/app/modules/shows/show/song/song.component.html @@ -1,4 +1,4 @@ -
+
{{ iSong.title }}
diff --git a/src/app/modules/shows/show/song/song.component.ts b/src/app/modules/shows/show/song/song.component.ts index 3fa9255..091181e 100644 --- a/src/app/modules/shows/show/song/song.component.ts +++ b/src/app/modules/shows/show/song/song.component.ts @@ -16,18 +16,18 @@ import {Show} from '../../services/show'; styleUrls: ['./song.component.less'], }) export class SongComponent implements OnInit { - @Input() public show: Show; - @Input() public showSong: ShowSong; - @Input() public showSongs: ShowSong[]; - @Input() public showId: string; - @Input() public showText: boolean; + @Input() public show: Show | null = null; + @Input() public showSong: ShowSong | null = null; + @Input() public showSongs: ShowSong[] | null = null; + @Input() public showId: string | null = null; + @Input() public showText: boolean | null = null; - public keys: string[]; + public keys: string[] = []; public faDelete = faTrash; public faUp = faCaretUp; public faDown = faCaretDown; - public keyFormControl: FormControl; - public iSong: Song; + public keyFormControl: FormControl = new FormControl(); + public iSong: Song | null = null; public constructor(private showSongService: ShowSongService) {} @@ -38,13 +38,16 @@ export class SongComponent implements OnInit { } public ngOnInit(): void { + if (!this.showSong) return; this.keyFormControl = new FormControl(this.showSong.key); this.keyFormControl.valueChanges.subscribe((value: string) => { + if (!this.showId || !this.showSong) return; void this.showSongService.update$(this.showId, this.showSong.id, {key: value}); }); } public async onDelete(): Promise { + if (!this.showId || !this.showSong) return; await this.showSongService.delete$(this.showId, this.showSong.id); } @@ -57,7 +60,8 @@ export class SongComponent implements OnInit { } public async reorderUp(): Promise { - const index = this.showSongs.findIndex(_ => _.songId === this.iSong.id); + if (!this.showSongs || !this.showId) return; + const index = this.showSongs.findIndex(_ => _.songId === this.iSong?.id); if (index === 0) { return; } @@ -74,7 +78,8 @@ export class SongComponent implements OnInit { } public async reorderDown(): Promise { - const index = this.showSongs.findIndex(_ => _.songId === this.iSong.id); + if (!this.showSongs || !this.showId) return; + const index = this.showSongs.findIndex(_ => _.songId === this.iSong?.id); if (index === this.showSongs.length - 1) { return; } @@ -91,6 +96,7 @@ export class SongComponent implements OnInit { } public async onChordModeChanged(value: ChordMode): Promise { + if (!this.showId || !this.showSong) return; await this.showSongService.update$(this.showId, this.showSong.id, { chordMode: value, }); diff --git a/src/app/modules/songs/services/chord.ts b/src/app/modules/songs/services/chord.ts index 3e2f61e..32f651d 100644 --- a/src/app/modules/songs/services/chord.ts +++ b/src/app/modules/songs/services/chord.ts @@ -2,6 +2,6 @@ export interface Chord { chord: string; length: number; position: number; - slashChord?: string; - add?: string; + slashChord: string | null; + add: string | null; } diff --git a/src/app/modules/songs/services/line.ts b/src/app/modules/songs/services/line.ts index b8fe24d..2916ad2 100644 --- a/src/app/modules/songs/services/line.ts +++ b/src/app/modules/songs/services/line.ts @@ -4,5 +4,5 @@ import {Chord} from './chord'; export interface Line { type: LineType; text: string; - chords?: Chord[]; + chords: Chord[] | null; } diff --git a/src/app/modules/songs/services/song-data.service.ts b/src/app/modules/songs/services/song-data.service.ts index 9c65bf7..d18d15a 100644 --- a/src/app/modules/songs/services/song-data.service.ts +++ b/src/app/modules/songs/services/song-data.service.ts @@ -12,8 +12,10 @@ export class SongDataService { public constructor(private dbService: DbService) {} public list$ = (): Observable => this.dbService.col$(this.collection); - public read$ = (songId: string): Observable => this.dbService.doc$(this.collection + '/' + songId); - public update$ = async (songId: string, data: Partial): Promise => await this.dbService.doc(this.collection + '/' + songId).update(data); + public read$ = (songId: string): Observable => this.dbService.doc$(this.collection + '/' + songId); + public update$ = async (songId: string, data: Partial): Promise => + await this.dbService.doc(this.collection + '/' + songId).update(data); public add = async (data: Partial): Promise => (await this.dbService.col(this.collection).add(data)).id; - public delete = async (songId: string): Promise => await this.dbService.doc(this.collection + '/' + songId).delete(); + public delete = async (songId: string): Promise => + await this.dbService.doc(this.collection + '/' + songId).delete(); } diff --git a/src/app/modules/songs/services/song.service.ts b/src/app/modules/songs/services/song.service.ts index d2e66c7..91af449 100644 --- a/src/app/modules/songs/services/song.service.ts +++ b/src/app/modules/songs/services/song.service.ts @@ -26,13 +26,15 @@ export class SongService { } public list$ = (): Observable => this.songDataService.list$(); //.pipe(tap(_ => (this.list = _))); - public read$ = (songId: string): Observable => this.songDataService.read$(songId); - public read = (songId: string): Promise => this.read$(songId).pipe(first()).toPromise(); + public read$ = (songId: string): Observable => this.songDataService.read$(songId); + public read = (songId: string): Promise => this.read$(songId).pipe(first()).toPromise(); public async update$(songId: string, data: Partial): Promise { const song = await this.read(songId); + if (!song) return; const edits = song.edits ?? []; const user = await this.userService.currentUser(); + if (!user) return; edits.push({username: user.name, timestamp: Timestamp.now()}); await this.songDataService.update$(songId, {...data, edits}); } diff --git a/src/app/modules/songs/services/text-rendering.service.ts b/src/app/modules/songs/services/text-rendering.service.ts index b4ca903..b1e8708 100644 --- a/src/app/modules/songs/services/text-rendering.service.ts +++ b/src/app/modules/songs/services/text-rendering.service.ts @@ -15,7 +15,7 @@ export class TextRenderingService { public constructor(private transposeService: TransposeService) {} - public parse(text: string, transpose: TransposeMode): Section[] { + public parse(text: string, transpose: TransposeMode | null): Section[] { if (!text) { return []; } @@ -27,22 +27,21 @@ export class TextRenderingService { }; return arrayOfLines.reduce((array, line) => { const type = this.getSectionTypeOfLine(line); - if (this.regexSection.exec(line)) { - return [ - ...array, - { - type, - number: indices[type]++, - lines: [], - }, - ]; + if (this.regexSection.exec(line) && type !== null) { + const section: Section = { + type, + number: indices[type]++, + lines: [], + }; + return [...array, section]; } - array[array.length - 1].lines.push(this.getLineOfLineText(line, transpose)); + const lineOfLineText = this.getLineOfLineText(line, transpose); + if (lineOfLineText) array[array.length - 1].lines.push(lineOfLineText); return array; }, [] as Section[]); } - private getLineOfLineText(text: string, transpose: TransposeMode): Line { + private getLineOfLineText(text: string, transpose: TransposeMode | null): Line | null { if (!text) { return null; } @@ -50,11 +49,13 @@ export class TextRenderingService { const hasMatches = cords.length > 0; const type = hasMatches ? LineType.chord : LineType.text; - const line = {type, text, chords: hasMatches ? cords : undefined}; - return transpose ? this.transposeService.transpose(line, transpose.baseKey, transpose.targetKey) : this.transposeService.renderChords(line); + const line: Line = {type, text, chords: hasMatches ? cords : null}; + return transpose + ? this.transposeService.transpose(line, transpose.baseKey, transpose.targetKey) + : this.transposeService.renderChords(line); } - private getSectionTypeOfLine(line: string): SectionType { + private getSectionTypeOfLine(line: string): SectionType | null { if (!line) { return null; } @@ -71,10 +72,12 @@ export class TextRenderingService { case 'Bridge': return SectionType.Bridge; } + + return null; } private readChords(chordLine: string): Chord[] { - let match: string[]; + let match: string[] | null; const chords: Chord[] = []; // https://regex101.com/r/68jMB8/5 @@ -86,6 +89,8 @@ export class TextRenderingService { chord: match[1], length: match[0].length, position: regex.lastIndex - match[0].length, + slashChord: null, + add: null, }; if (match[3]) { chord.slashChord = match[3]; diff --git a/src/app/modules/songs/services/transpose.service.ts b/src/app/modules/songs/services/transpose.service.ts index 818ba25..847e0ea 100644 --- a/src/app/modules/songs/services/transpose.service.ts +++ b/src/app/modules/songs/services/transpose.service.ts @@ -17,8 +17,9 @@ export class TransposeService { const difference = this.getDistance(baseKey, targetKey); const map = this.getMap(baseKey, difference); - const chords = difference > 0 ? line.chords.map(chord => this.transposeChord(chord, map)) : line.chords; - const renderedLine = this.renderLine(chords); + const chords = + difference > 0 && line.chords && map ? line.chords.map(chord => this.transposeChord(chord, map)) : line.chords; + const renderedLine = this.renderLine(chords ?? []); return {...line, text: renderedLine, chords}; } @@ -28,13 +29,16 @@ export class TransposeService { return line; } - const renderedLine = this.renderLine(line.chords); + const renderedLine = this.renderLine(line.chords ?? []); return {...line, text: renderedLine}; } public getDistance(baseKey: string, targetKey: string): number { const scale = getScaleType(baseKey); - return scale ? (scale[0].indexOf(targetKey) - scale[0].indexOf(baseKey) ?? scale[1].indexOf(targetKey) - scale[1].indexOf(baseKey)) % 12 : 0; + return scale + ? (scale[0].indexOf(targetKey) - scale[0].indexOf(baseKey) ?? + scale[1].indexOf(targetKey) - scale[1].indexOf(baseKey)) % 12 + : 0; } public getMap(baseKey: string, difference: number): TransposeMap | null { @@ -42,7 +46,7 @@ export class TransposeService { if (!scale) { return null; } - const map = {}; + const map: {[key: string]: string} = {}; for (let i = 0; i < 12; i++) { const source = scale[0][i]; const mappedIndex = (i + difference) % 12; @@ -68,7 +72,8 @@ export class TransposeService { } private renderLine(chords: Chord[]): string { - let template = ' '; + let template = + ' '; chords.forEach(chord => { const pos = chord.position; @@ -85,6 +90,10 @@ export class TransposeService { } private renderChord(chord: Chord) { - return scaleMapping[chord.chord] + (chord.add ? chord.add : '') + (chord.slashChord ? '/' + scaleMapping[chord.slashChord] : ''); + return ( + scaleMapping[chord.chord] + + (chord.add ? chord.add : '') + + (chord.slashChord ? '/' + scaleMapping[chord.slashChord] : '') + ); } } diff --git a/src/app/modules/songs/services/upload.service.ts b/src/app/modules/songs/services/upload.service.ts index 59d4da7..89bfb5f 100644 --- a/src/app/modules/songs/services/upload.service.ts +++ b/src/app/modules/songs/services/upload.service.ts @@ -22,7 +22,7 @@ export class UploadService extends FileBase { const ref = this.angularFireStorage.ref(filePath); const task = ref.put(upload.file); - task.percentageChanges().subscribe(percent => (upload.progress = percent)); + task.percentageChanges().subscribe(percent => (upload.progress = percent ?? 0)); task .snapshotChanges() .pipe(finalize(() => void this.saveFileData(songId, upload))) diff --git a/src/app/modules/songs/services/upload.ts b/src/app/modules/songs/services/upload.ts index 771258f..0359061 100644 --- a/src/app/modules/songs/services/upload.ts +++ b/src/app/modules/songs/services/upload.ts @@ -1,9 +1,8 @@ export class Upload { - public $key: string; public file: File; - public name: string; - public path: string; - public progress: number; + public name = ''; + public path = ''; + public progress = 0; public createdAt: Date = new Date(); public constructor(file: File) { diff --git a/src/app/modules/songs/song-list/filter/filter.component.ts b/src/app/modules/songs/song-list/filter/filter.component.ts index cc3acf7..83595e2 100644 --- a/src/app/modules/songs/song-list/filter/filter.component.ts +++ b/src/app/modules/songs/song-list/filter/filter.component.ts @@ -13,8 +13,8 @@ import {KEYS} from '../../services/key.helper'; }) export class FilterComponent { public filterFormGroup: FormGroup; - @Input() public route: string; - @Input() public songs: Song[]; + @Input() public route = '/'; + @Input() public songs: Song[] = []; public types = SongService.TYPES; public legalType = SongService.LEGAL_TYPE; public keys = KEYS; @@ -28,7 +28,8 @@ export class FilterComponent { flag: '', }); - activatedRoute.queryParams.subscribe((filterValues: FilterValues) => { + activatedRoute.queryParams.subscribe(params => { + const filterValues = params as FilterValues; if (filterValues.q) this.filterFormGroup.controls.q.setValue(filterValues.q); if (filterValues.type) this.filterFormGroup.controls.type.setValue(filterValues.type); if (filterValues.key) this.filterFormGroup.controls.key.setValue(filterValues.key); diff --git a/src/app/modules/songs/song-list/list-item/list-item.component.html b/src/app/modules/songs/song-list/list-item/list-item.component.html index 8631a64..d469fee 100644 --- a/src/app/modules/songs/song-list/list-item/list-item.component.html +++ b/src/app/modules/songs/song-list/list-item/list-item.component.html @@ -1,4 +1,4 @@ -
+
{{ song.number }}
{{ song.title }}
diff --git a/src/app/modules/songs/song-list/list-item/list-item.component.ts b/src/app/modules/songs/song-list/list-item/list-item.component.ts index aa12f27..eddde16 100644 --- a/src/app/modules/songs/song-list/list-item/list-item.component.ts +++ b/src/app/modules/songs/song-list/list-item/list-item.component.ts @@ -10,7 +10,7 @@ import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'; styleUrls: ['./list-item.component.less'], }) export class ListItemComponent { - @Input() public song: Song; + @Input() public song: Song | null = null; public faLegal = faBalanceScaleRight; public faDraft = faPencilRuler; public faFinal = faCheck; diff --git a/src/app/modules/songs/song-list/song-list.component.ts b/src/app/modules/songs/song-list/song-list.component.ts index 93fa4b4..b88cfdc 100644 --- a/src/app/modules/songs/song-list/song-list.component.ts +++ b/src/app/modules/songs/song-list/song-list.component.ts @@ -16,10 +16,14 @@ import {ScrollService} from '../../../services/scroll.service'; animations: [fade], }) export class SongListComponent implements OnInit, OnDestroy { - public songs$: Observable; + public songs$: Observable | null = null; public anyFilterActive = false; - public constructor(private songService: SongService, private activatedRoute: ActivatedRoute, private scrollService: ScrollService) {} + public constructor( + private songService: SongService, + private activatedRoute: ActivatedRoute, + private scrollService: ScrollService + ) {} public ngOnInit(): void { const filter$ = this.activatedRoute.queryParams.pipe( diff --git a/src/app/modules/songs/song/edit/edit-file/edit-file.component.ts b/src/app/modules/songs/song/edit/edit-file/edit-file.component.ts index c45b37d..744eb5e 100644 --- a/src/app/modules/songs/song/edit/edit-file/edit-file.component.ts +++ b/src/app/modules/songs/song/edit/edit-file/edit-file.component.ts @@ -13,18 +13,28 @@ import {File} from '../../../services/file'; styleUrls: ['./edit-file.component.less'], }) export class EditFileComponent { - public selectedFiles: FileList; - public currentUpload: Upload; - public songId: string; + public selectedFiles: FileList | null = null; + public currentUpload: Upload | null = null; + public songId: string | null = null; public files$: Observable; - 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; - }); + public constructor( + private activatedRoute: ActivatedRoute, + private uploadService: UploadService, + private fileService: FileDataService + ) { + this.activatedRoute.params + .pipe( + map(param => param as {songId: string}), + map(param => param.songId) + ) + .subscribe(songId => { + this.songId = songId; + }); this.files$ = this.activatedRoute.params.pipe( - map((param: {songId: string}) => param.songId), + map(param => param as {songId: string}), + map(param => param.songId), switchMap(songId => this.fileService.read$(songId)) ); } @@ -35,7 +45,9 @@ export class EditFileComponent { } public uploadSingle(): void { + if (!this.selectedFiles || !this.songId) return; const file = this.selectedFiles.item(0); + if (!file) return; this.currentUpload = new Upload(file); this.uploadService.pushUpload(this.songId, this.currentUpload); } diff --git a/src/app/modules/songs/song/edit/edit-file/file/file.component.ts b/src/app/modules/songs/song/edit/edit-file/file/file.component.ts index 2cab6b7..a6530e8 100644 --- a/src/app/modules/songs/song/edit/edit-file/file/file.component.ts +++ b/src/app/modules/songs/song/edit/edit-file/file/file.component.ts @@ -10,12 +10,12 @@ import {FileService} from '../../../../services/file.service'; styleUrls: ['./file.component.less'], }) export class FileComponent { - public url$: Observable; - public name: string; + public url$: Observable | null = null; + public name = ''; public faTrash = faTrashAlt; - @Input() public songId: string; - private fileId: string; - private path: string; + @Input() public songId: string | null = null; + private fileId: string | null = null; + private path: string | null = null; public constructor(private fileService: FileService) {} @@ -28,6 +28,6 @@ export class FileComponent { } public async onDelete(): Promise { - await this.fileService.delete(this.path, this.songId, this.fileId); + if (this.path && this.songId && this.fileId) await this.fileService.delete(this.path, this.songId, this.fileId); } } diff --git a/src/app/modules/songs/song/edit/edit-song.guard.ts b/src/app/modules/songs/song/edit/edit-song.guard.ts index 3d5126c..f2f5d3b 100644 --- a/src/app/modules/songs/song/edit/edit-song.guard.ts +++ b/src/app/modules/songs/song/edit/edit-song.guard.ts @@ -6,13 +6,13 @@ import {EditComponent} from './edit.component'; @Injectable({ providedIn: 'root', }) -export class EditSongGuard implements CanDeactivate { +export class EditSongGuard implements CanDeactivate { public canDeactivate( component: EditComponent, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, - nextState?: RouterStateSnapshot + nextState: RouterStateSnapshot ): Observable | Promise | boolean | UrlTree { - return component.editSongComponent.askForSave(nextState); + return component.editSongComponent ? component.editSongComponent.askForSave(nextState) : true; } } diff --git a/src/app/modules/songs/song/edit/edit-song/edit-song.component.ts b/src/app/modules/songs/song/edit/edit-song/edit-song.component.ts index c1688b8..19cd68a 100644 --- a/src/app/modules/songs/song/edit/edit-song/edit-song.component.ts +++ b/src/app/modules/songs/song/edit/edit-song/edit-song.component.ts @@ -20,8 +20,8 @@ import {SaveDialogComponent} from './save-dialog/save-dialog.component'; styleUrls: ['./edit-song.component.less'], }) export class EditSongComponent implements OnInit { - public song: Song; - public form: FormGroup; + public song: Song | null = null; + public form: FormGroup = new FormGroup({}); public keys = KEYS; public types = SongService.TYPES; public status = SongService.STATUS; @@ -34,17 +34,25 @@ export class EditSongComponent implements OnInit { public faLink = faExternalLinkAlt; public songtextFocus = false; - public 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: {songId: string}) => param.songId), + map(param => param as {songId: string}), + map(param => param.songId), switchMap(songId => this.songService.read$(songId)), first() ) .subscribe(song => { this.song = song; + if (!song) return; this.form = this.editService.createSongForm(song); this.form.controls.flags.valueChanges.subscribe(_ => this.onFlagsChanged(_)); this.onFlagsChanged(this.form.controls.flags.value); @@ -52,6 +60,7 @@ export class EditSongComponent implements OnInit { } public async onSave(): Promise { + if (!this.song) return; const data = this.form.value as Partial; await this.songService.update$(this.song.id, data); this.form.markAsPristine(); @@ -78,7 +87,7 @@ export class EditSongComponent implements OnInit { } } - public askForSave(nextState?: RouterStateSnapshot): boolean { + public askForSave(nextState: RouterStateSnapshot): boolean { if (!this.form.dirty) { return true; } @@ -104,7 +113,7 @@ export class EditSongComponent implements OnInit { } private async onSaveDialogAfterClosed(save: boolean, url: string) { - if (save) { + if (save && this.song) { const data = this.form.value as Partial; await this.songService.update$(this.song.id, data); } diff --git a/src/app/modules/songs/song/edit/edit.component.ts b/src/app/modules/songs/song/edit/edit.component.ts index 70d9760..9b4258d 100644 --- a/src/app/modules/songs/song/edit/edit.component.ts +++ b/src/app/modules/songs/song/edit/edit.component.ts @@ -7,5 +7,5 @@ import {EditSongComponent} from './edit-song/edit-song.component'; styleUrls: ['./edit.component.less'], }) export class EditComponent { - @ViewChild(EditSongComponent) public editSongComponent: EditSongComponent; + @ViewChild(EditSongComponent) public editSongComponent: EditSongComponent | null = null; } diff --git a/src/app/modules/songs/song/edit/edit.module.ts b/src/app/modules/songs/song/edit/edit.module.ts index 64e54f6..71a20e1 100644 --- a/src/app/modules/songs/song/edit/edit.module.ts +++ b/src/app/modules/songs/song/edit/edit.module.ts @@ -28,7 +28,14 @@ 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], + declarations: [ + EditComponent, + EditSongComponent, + EditFileComponent, + FileComponent, + SaveDialogComponent, + HistoryComponent, + ], exports: [EditComponent], bootstrap: [SaveDialogComponent], imports: [ diff --git a/src/app/modules/songs/song/edit/history/history.component.ts b/src/app/modules/songs/song/edit/history/history.component.ts index 3d2ef52..459d6e7 100644 --- a/src/app/modules/songs/song/edit/history/history.component.ts +++ b/src/app/modules/songs/song/edit/history/history.component.ts @@ -10,14 +10,15 @@ import {Song} from '../../../services/song'; styleUrls: ['./history.component.less'], }) export class HistoryComponent implements OnInit { - public song: Song; + public song: Song | null = null; public constructor(private activatedRoute: ActivatedRoute, private songService: SongService) {} public ngOnInit(): void { this.activatedRoute.params .pipe( - map((param: {songId: string}) => param.songId), + map(param => param as {songId: string}), + map(param => param.songId), switchMap(songId => this.songService.read$(songId)), first() ) diff --git a/src/app/modules/songs/song/file/file.component.ts b/src/app/modules/songs/song/file/file.component.ts index 36b1736..26242bd 100644 --- a/src/app/modules/songs/song/file/file.component.ts +++ b/src/app/modules/songs/song/file/file.component.ts @@ -9,8 +9,8 @@ import {Observable} from 'rxjs'; styleUrls: ['./file.component.less'], }) export class FileComponent { - public url$: Observable; - public name: string; + public url$: Observable | null = null; + public name = ''; public constructor(private storage: AngularFireStorage) {} diff --git a/src/app/modules/songs/song/new/new.component.ts b/src/app/modules/songs/song/new/new.component.ts index e42438c..20331ce 100644 --- a/src/app/modules/songs/song/new/new.component.ts +++ b/src/app/modules/songs/song/new/new.component.ts @@ -1,36 +1,39 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnDestroy, OnInit} from '@angular/core'; import {faSave} from '@fortawesome/free-solid-svg-icons/faSave'; import {FormControl, FormGroup, Validators} from '@angular/forms'; -import {autoComplete, Unsubscriber} from 'ngx-hocs-unsubscriber'; import {SongService} from '../../services/song.service'; import {Song} from '../../services/song'; import {Router} from '@angular/router'; +import {Subscription} from 'rxjs'; -@Unsubscriber() @Component({ selector: 'app-new', templateUrl: './new.component.html', styleUrls: ['./new.component.less'], }) -export class NewComponent implements OnInit { +export class NewComponent implements OnInit, OnDestroy { public faSave = faSave; - public form: FormGroup; + public form: FormGroup = new FormGroup({ + number: new FormControl(null, Validators.required), + title: new FormControl(null, Validators.required), + }); public constructor(private songService: SongService, private router: Router) {} public ngOnInit(): void { - this.form = new FormGroup({ - number: new FormControl(null, Validators.required), - title: new FormControl(null, Validators.required), - }); + this.form.reset(); - this.songService - .list$() - .pipe(autoComplete(this)) - .subscribe(songs => { + this.subs.push( + this.songService.list$().subscribe(songs => { const freeSongnumber = this.getFreeSongNumber(songs); this.form.controls.number.setValue(freeSongnumber); - }); + }) + ); + } + + private subs: Subscription[] = []; + public ngOnDestroy(): void { + this.subs.forEach(_ => _.unsubscribe()); } public async onSave(): Promise { @@ -48,5 +51,6 @@ export class NewComponent implements OnInit { return i; } } + return Number.MAX_SAFE_INTEGER; } } diff --git a/src/app/modules/songs/song/new/new.module.ts b/src/app/modules/songs/song/new/new.module.ts index 7cebdf3..fe9135b 100644 --- a/src/app/modules/songs/song/new/new.module.ts +++ b/src/app/modules/songs/song/new/new.module.ts @@ -11,6 +11,15 @@ import {AutofocusModule} from '../../../../widget-modules/directives/autofocus/a @NgModule({ declarations: [NewComponent], - imports: [CommonModule, CardModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, ButtonRowModule, ButtonModule, AutofocusModule], + imports: [ + CommonModule, + CardModule, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + ButtonRowModule, + ButtonModule, + AutofocusModule, + ], }) export class NewModule {} diff --git a/src/app/modules/songs/song/song.component.ts b/src/app/modules/songs/song/song.component.ts index 0c8b4b4..689743d 100644 --- a/src/app/modules/songs/song/song.component.ts +++ b/src/app/modules/songs/song/song.component.ts @@ -17,24 +17,32 @@ import {faTrash} from '@fortawesome/free-solid-svg-icons/faTrash'; styleUrls: ['./song.component.less'], }) export class SongComponent implements OnInit { - public song$: Observable; - public files$: Observable; - public user$: Observable; + public song$: Observable | null = null; + public files$: Observable | null = null; + public user$: Observable | null = null; public faEdit = faEdit; public faDelete = faTrash; - public 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: {songId: string}) => param.songId), + map(param => param as {songId: string}), + map(param => param.songId), switchMap(songId => this.songService.read$(songId)) ); this.files$ = this.activatedRoute.params.pipe( - map((param: {songId: string}) => param.songId), + map(param => param as {songId: string}), + map(param => param.songId), switchMap(songId => this.fileService.read$(songId)) ); } diff --git a/src/app/modules/user/info/info.component.ts b/src/app/modules/user/info/info.component.ts index db9ae53..e1e9a5a 100644 --- a/src/app/modules/user/info/info.component.ts +++ b/src/app/modules/user/info/info.component.ts @@ -11,7 +11,7 @@ import {faSignOutAlt} from '@fortawesome/free-solid-svg-icons/faSignOutAlt'; styleUrls: ['./info.component.less'], }) export class InfoComponent implements OnInit { - public user$: Observable; + public user$: Observable | null = null; public faSignOut = faSignOutAlt; public constructor(private userService: UserService) {} diff --git a/src/app/modules/user/info/role.pipe.ts b/src/app/modules/user/info/role.pipe.ts index 80c9c4e..1a9df59 100644 --- a/src/app/modules/user/info/role.pipe.ts +++ b/src/app/modules/user/info/role.pipe.ts @@ -19,5 +19,7 @@ export class RolePipe implements PipeTransform { case 'presenter': return 'Präsentator'; } + + return 'keine Rolle'; } } diff --git a/src/app/modules/user/info/users/user/user.component.ts b/src/app/modules/user/info/users/user/user.component.ts index 003a9ab..8ec249c 100644 --- a/src/app/modules/user/info/users/user/user.component.ts +++ b/src/app/modules/user/info/users/user/user.component.ts @@ -10,9 +10,9 @@ import {faTimes} from '@fortawesome/free-solid-svg-icons/faTimes'; styleUrls: ['./user.component.less'], }) export class UserComponent { - public id: string; - public name: string; - public roles: string[]; + public id = ''; + public name = ''; + public roles: string[] = []; public ROLE_TYPES = ROLE_TYPES; public edit = false; public faClose = faTimes; diff --git a/src/app/modules/user/login/login.component.ts b/src/app/modules/user/login/login.component.ts index 04d5cd6..edf5efe 100644 --- a/src/app/modules/user/login/login.component.ts +++ b/src/app/modules/user/login/login.component.ts @@ -11,18 +11,18 @@ import {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus'; styleUrls: ['./login.component.less'], }) export class LoginComponent implements OnInit { - public form: FormGroup; - public errorMessage: string; + public form: FormGroup = new FormGroup({ + user: new FormControl(null, [Validators.required, Validators.email]), + pass: new FormControl(null, [Validators.required]), + }); + public errorMessage = ''; public faSignIn = faSignInAlt; public faNewUser = faUserPlus; public constructor(private userService: UserService, private router: Router) {} public ngOnInit(): void { - this.form = new FormGroup({ - user: new FormControl(null, [Validators.required, Validators.email]), - pass: new FormControl(null, [Validators.required]), - }); + this.form.reset; } public async onLogin(): Promise { diff --git a/src/app/modules/user/new/new.component.ts b/src/app/modules/user/new/new.component.ts index bd1145c..eee2cf5 100644 --- a/src/app/modules/user/new/new.component.ts +++ b/src/app/modules/user/new/new.component.ts @@ -9,17 +9,17 @@ import {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus'; styleUrls: ['./new.component.less'], }) export class NewComponent implements OnInit { - public form: FormGroup; + public form: FormGroup = this.fb.group({ + email: new FormControl(null, [Validators.required, Validators.email]), + name: new FormControl(null, [Validators.required]), + password: new FormControl(null, [Validators.required, Validators.minLength(6)]), + }); public faNewUser = faUserPlus; public constructor(private fb: FormBuilder, private userService: UserService) {} public ngOnInit(): void { - this.form = this.fb.group({ - email: new FormControl(null, [Validators.required, Validators.email]), - name: new FormControl(null, [Validators.required]), - password: new FormControl(null, [Validators.required, Validators.minLength(6)]), - }); + this.form.reset(); } public async onCreate(): Promise { diff --git a/src/app/modules/user/password/password.component.ts b/src/app/modules/user/password/password.component.ts index 13a141a..d59a241 100644 --- a/src/app/modules/user/password/password.component.ts +++ b/src/app/modules/user/password/password.component.ts @@ -1,5 +1,5 @@ import {Component, OnInit} from '@angular/core'; -import {AbstractControl, FormControl, FormGroup, Validators} from '@angular/forms'; +import {FormControl, FormGroup, Validators} from '@angular/forms'; import {Router} from '@angular/router'; import {UserService} from '../../../services/user/user.service'; import {faWindowRestore} from '@fortawesome/free-solid-svg-icons/faWindowRestore'; @@ -10,18 +10,17 @@ import {faWindowRestore} from '@fortawesome/free-solid-svg-icons/faWindowRestore styleUrls: ['./password.component.less'], }) export class PasswordComponent implements OnInit { - public form: FormGroup; - public errorMessage: string; + public form: FormGroup = new FormGroup({ + user: new FormControl(null, [Validators.required, Validators.email]), + }); + + public errorMessage = ''; public faNewPassword = faWindowRestore; public constructor(public userService: UserService, private router: Router) {} public ngOnInit(): void { - const required = (c: AbstractControl) => Validators.required(c); - const email = Validators.email; - this.form = new FormGroup({ - user: new FormControl(null, [required, email]), - }); + this.form.reset(); } public async onResetPassword(): Promise { diff --git a/src/app/modules/user/user.module.ts b/src/app/modules/user/user.module.ts index e572c88..7f60cfe 100644 --- a/src/app/modules/user/user.module.ts +++ b/src/app/modules/user/user.module.ts @@ -24,7 +24,18 @@ import {LogoModule} from '../../widget-modules/components/logo/logo.module'; import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; @NgModule({ - declarations: [LoginComponent, AuthMessagePipe, InfoComponent, LogoutComponent, RolePipe, PasswordComponent, PasswordSendComponent, UsersComponent, UserComponent, NewComponent], + declarations: [ + LoginComponent, + AuthMessagePipe, + InfoComponent, + LogoutComponent, + RolePipe, + PasswordComponent, + PasswordSendComponent, + UsersComponent, + UserComponent, + NewComponent, + ], imports: [ CommonModule, UserRoutingModule, diff --git a/src/app/services/config.service.ts b/src/app/services/config.service.ts index 7c8e31d..78cbb92 100644 --- a/src/app/services/config.service.ts +++ b/src/app/services/config.service.ts @@ -10,11 +10,11 @@ import {first} from 'rxjs/operators'; export class ConfigService { public constructor(private db: DbService) {} - public get get$(): Observable { + public get get$(): Observable { return this.db.doc$('global/config'); } - public async get(): Promise { + public async get(): Promise { return await this.db.doc$('global/config').pipe(first()).toPromise(); } } diff --git a/src/app/services/config.ts b/src/app/services/config.ts index 96f9c2b..b863c43 100644 --- a/src/app/services/config.ts +++ b/src/app/services/config.ts @@ -1,3 +1,4 @@ export interface Config { + id: string; ccliLicenseId: string; } diff --git a/src/app/services/db.service.ts b/src/app/services/db.service.ts index a684c37..c8012e2 100644 --- a/src/app/services/db.service.ts +++ b/src/app/services/db.service.ts @@ -2,6 +2,7 @@ import {Injectable} from '@angular/core'; import {AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument} from '@angular/fire/firestore'; import {Observable} from 'rxjs'; import {QueryFn} from '@angular/fire/firestore/interfaces'; +import {map} from 'rxjs/operators'; type CollectionPredicate = string | AngularFirestoreCollection; type DocumentPredicate = string | AngularFirestoreDocument; @@ -20,8 +21,10 @@ export class DbService { return typeof ref === 'string' ? this.afs.doc(ref) : ref; } - public doc$(ref: DocumentPredicate): Observable { - return this.doc(ref).valueChanges({idField: 'id'}); + public doc$(ref: DocumentPredicate): Observable<(T & {id: string}) | null> { + return this.doc(ref) + .valueChanges({idField: 'id'}) + .pipe(map(_ => (_ ? _ : null))); } public col$(ref: CollectionPredicate, queryFn?: QueryFn): Observable { diff --git a/src/app/services/filter.helper.ts b/src/app/services/filter.helper.ts index 0dfcda0..b059e86 100644 --- a/src/app/services/filter.helper.ts +++ b/src/app/services/filter.helper.ts @@ -5,8 +5,8 @@ export function filterSong(song: Song, filterValue: string): boolean { return true; } - const textMatch = song.text && normalize(song.text).indexOf(normalize(filterValue)) !== -1; - const titleMatch = song.title && normalize(song.title).indexOf(normalize(filterValue)) !== -1; + const textMatch = !!song.text && normalize(song.text).indexOf(normalize(filterValue)) !== -1; + const titleMatch = !!song.title && normalize(song.title).indexOf(normalize(filterValue)) !== -1; return textMatch || titleMatch; } diff --git a/src/app/services/global-settings.service.ts b/src/app/services/global-settings.service.ts index 04f5189..f5ac13d 100644 --- a/src/app/services/global-settings.service.ts +++ b/src/app/services/global-settings.service.ts @@ -9,7 +9,7 @@ import {Observable} from 'rxjs'; export class GlobalSettingsService { public constructor(private db: DbService) {} - public get get$(): Observable { + public get get$(): Observable { return this.db.doc$('global/static'); } diff --git a/src/app/services/scroll.service.ts b/src/app/services/scroll.service.ts index 56dc702..afed269 100644 --- a/src/app/services/scroll.service.ts +++ b/src/app/services/scroll.service.ts @@ -5,7 +5,7 @@ import {BehaviorSubject} from 'rxjs'; providedIn: 'root', }) export class ScrollService { - private scrollPosition: number; + private scrollPosition = 0; private scrollSlots: {[key: string]: number} = {}; private iRestoreScrollPosition$ = new BehaviorSubject(0); public restoreScrollPosition$ = this.iRestoreScrollPosition$.asObservable(); diff --git a/src/app/services/user/owner.directive.ts b/src/app/services/user/owner.directive.ts index 4372c30..1cd71a1 100644 --- a/src/app/services/user/owner.directive.ts +++ b/src/app/services/user/owner.directive.ts @@ -5,11 +5,15 @@ import {UserService} from './user.service'; selector: '[appOwner]', }) export class OwnerDirective implements OnInit { - private currentUserId: string; + private currentUserId: string | null = null; + private iAppOwner: string | null = null; - private iAppOwner: string; - - public constructor(private element: ElementRef, private templateRef: TemplateRef, private viewContainer: ViewContainerRef, private userService: UserService) {} + public constructor( + private element: ElementRef, + private templateRef: TemplateRef, + private viewContainer: ViewContainerRef, + private userService: UserService + ) {} @Input() public set appOwner(value: string) { diff --git a/src/app/services/user/role.directive.ts b/src/app/services/user/role.directive.ts index 07d2ec4..1bae3ba 100644 --- a/src/app/services/user/role.directive.ts +++ b/src/app/services/user/role.directive.ts @@ -8,10 +8,15 @@ import {User} from './user'; }) export class RoleDirective implements OnInit { @Input() public appRole: roles[] = []; - private currentUser: User; - private loggedIn: boolean; + private currentUser: User | null = null; + private loggedIn = false; - public constructor(private element: ElementRef, private templateRef: TemplateRef, private viewContainer: ViewContainerRef, private userService: UserService) {} + public constructor( + private element: ElementRef, + private templateRef: TemplateRef, + private viewContainer: ViewContainerRef, + private userService: UserService + ) {} public ngOnInit(): void { this.userService.user$.subscribe(user => { diff --git a/src/app/services/user/user-name/user-name.component.ts b/src/app/services/user/user-name/user-name.component.ts index e49d31a..5a0a343 100644 --- a/src/app/services/user/user-name/user-name.component.ts +++ b/src/app/services/user/user-name/user-name.component.ts @@ -9,12 +9,12 @@ import {Observable} from 'rxjs'; styleUrls: ['./user-name.component.less'], }) export class UserNameComponent { - public name$: Observable; + public name$: Observable | null = null; public constructor(private userService: UserService) {} @Input() public set userId(id: string) { - this.name$ = this.userService.getUserbyId$(id).pipe(map(_ => _.name)); + this.name$ = this.userService.getUserbyId$(id).pipe(map(_ => _?.name ?? null)); } } diff --git a/src/app/services/user/user.service.ts b/src/app/services/user/user.service.ts index 067e38b..04ef7df 100644 --- a/src/app/services/user/user.service.ts +++ b/src/app/services/user/user.service.ts @@ -1,7 +1,7 @@ import {Injectable} from '@angular/core'; import {AngularFireAuth} from '@angular/fire/auth'; import {BehaviorSubject, Observable} from 'rxjs'; -import {filter, first, switchMap, tap} from 'rxjs/operators'; +import {filter, first, map, switchMap, tap} from 'rxjs/operators'; import {User} from './user'; import {DbService} from '../db.service'; import {environment} from '../../../environments/environment'; @@ -11,41 +11,43 @@ import {Router} from '@angular/router'; providedIn: 'root', }) export class UserService { - private iUserId$ = new BehaviorSubject(null); - private iUser$ = new BehaviorSubject(null); + private iUserId$ = new BehaviorSubject(null); + private iUser$ = new BehaviorSubject(null); public constructor(private afAuth: AngularFireAuth, private db: DbService, private router: Router) { this.afAuth.authState .pipe( - filter(_ => !!_), - tap(auth => this.iUserId$.next(auth.uid)), - switchMap(auth => this.readUser$(auth.uid)) + filter(auth => !!auth), + map(auth => auth?.uid ?? ''), + tap(uid => this.iUserId$.next(uid)), + switchMap(uid => this.readUser$(uid)) ) .subscribe(_ => this.iUser$.next(_)); } - public get userId$(): Observable { + public get userId$(): Observable { return this.iUserId$.asObservable(); } - public get user$(): Observable { + public get user$(): Observable { return this.iUser$.pipe(filter(_ => !!_)); } - public async currentUser(): Promise { + public async currentUser(): Promise { return this.user$.pipe(first()).toPromise(); } - public getUserbyId(userId: string): Promise { + public getUserbyId(userId: string): Promise { return this.getUserbyId$(userId).pipe(first()).toPromise(); } - public getUserbyId$(userId: string): Observable { + public getUserbyId$(userId: string): Observable { return this.db.doc$('users/' + userId); } - public async login(user: string, password: string): Promise { + public async login(user: string, password: string): Promise { const aUser = await this.afAuth.signInWithEmailAndPassword(user, password); + if (!aUser.user) return null; const dUser = await this.readUser(aUser.user.uid); this.iUser$.next(dUser); this.iUserId$.next(aUser.user.uid); @@ -73,6 +75,7 @@ export class UserService { public async createNewUser(user: string, name: string, password: string): Promise { const aUser = await this.afAuth.createUserWithEmailAndPassword(user, password); + if (!aUser.user) return; const userId = aUser.user.uid; await this.db.doc('users/' + userId).set({name, chordMode: 'onlyFirst'}); const dUser = await this.readUser(aUser.user.uid); @@ -80,6 +83,6 @@ export class UserService { await this.router.navigateByUrl('/brand/new-user'); } - private readUser$ = (uid: string) => this.db.doc$('users/' + uid); - private readUser = async (uid: string): Promise => await this.readUser$(uid).pipe(first()).toPromise(); + private readUser$ = (uid: string) => this.db.doc$('users/' + uid); + private readUser = async (uid: string): Promise => await this.readUser$(uid).pipe(first()).toPromise(); } diff --git a/src/app/widget-modules/components/add-song/add-song.component.ts b/src/app/widget-modules/components/add-song/add-song.component.ts index 1a8a8f4..b4ef5c1 100644 --- a/src/app/widget-modules/components/add-song/add-song.component.ts +++ b/src/app/widget-modules/components/add-song/add-song.component.ts @@ -12,15 +12,16 @@ import {ShowSongService} from '../../../modules/shows/services/show-song.service styleUrls: ['./add-song.component.less'], }) export class AddSongComponent { - @Input() public songs: Song[]; - @Input() public showSongs: ShowSong[]; - @Input() public showId: string; + @Input() public songs: Song[] | null = null; + @Input() public showSongs: ShowSong[] | null = null; + @Input() public showId: string | null = null; @Input() public addedLive = false; public filteredSongsControl = new FormControl(); public constructor(private showSongService: ShowSongService) {} public filteredSongs(): Song[] { + if (!this.songs) return []; const songs = this.songs .filter(_ => !!_) .filter(_ => !!_.title) @@ -41,6 +42,7 @@ export class AddSongComponent { } public async onAddSongSelectionChanged(event: MatSelectChange): Promise { + if (!this.showSongs || !this.showId) return; const order = this.showSongs.reduce((oa, u) => Math.max(oa, u.order), 0) + 1; await this.showSongService.new$(this.showId, event.value, order, this.addedLive); event.source.value = null; diff --git a/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts b/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts index 277c127..8d51e54 100644 --- a/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts +++ b/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts @@ -1,5 +1,5 @@ import {Component} from '@angular/core'; -import {ActivatedRoute, Router} from '@angular/router'; +import {ActivatedRoute, Params, Router} from '@angular/router'; @Component({ selector: 'app-filter', @@ -7,13 +7,12 @@ import {ActivatedRoute, Router} from '@angular/router'; styleUrls: ['./filter.component.less'], }) export class FilterComponent { - public value: string; + public value = ''; public constructor(private router: Router, activatedRoute: ActivatedRoute) { - activatedRoute.queryParams.subscribe((_: {q: string}) => { - if (_.q) { - this.value = _.q; - } + activatedRoute.queryParams.subscribe((params: Params) => { + const typedParams = params as {q: string}; + if (typedParams.q) this.value = typedParams.q; }); } diff --git a/src/app/widget-modules/components/application-frame/navigation/link/link.component.ts b/src/app/widget-modules/components/application-frame/navigation/link/link.component.ts index 835aac0..dfbe8e2 100644 --- a/src/app/widget-modules/components/application-frame/navigation/link/link.component.ts +++ b/src/app/widget-modules/components/application-frame/navigation/link/link.component.ts @@ -1,5 +1,6 @@ import {Component, Input} from '@angular/core'; import {IconProp} from '@fortawesome/fontawesome-svg-core'; +import {faCross} from '@fortawesome/free-solid-svg-icons/faCross'; @Component({ selector: 'app-link', @@ -7,7 +8,7 @@ import {IconProp} from '@fortawesome/fontawesome-svg-core'; styleUrls: ['./link.component.less'], }) export class LinkComponent { - @Input() public text: string; - @Input() public link: string; - @Input() public icon: IconProp; + @Input() public text: string | null = null; + @Input() public link: string | null = null; + @Input() public icon: IconProp = faCross; } diff --git a/src/app/widget-modules/components/application-frame/navigation/navigation.component.html b/src/app/widget-modules/components/application-frame/navigation/navigation.component.html index 7666a26..cdcaa12 100644 --- a/src/app/widget-modules/components/application-frame/navigation/navigation.component.html +++ b/src/app/widget-modules/components/application-frame/navigation/navigation.component.html @@ -1,4 +1,4 @@ -