import {Component, inject, OnInit} from '@angular/core'; import {ActivatedRoute, Router, RouterLink} from '@angular/router'; import {SongService} from '../services/song.service'; import {distinctUntilChanged, map, switchMap} from 'rxjs/operators'; import {Song} from '../services/song'; import {combineLatest, Observable} from 'rxjs'; import {FileDataService} from '../services/file-data.service'; import {File} from '../services/file'; import {UserService} from '../../../services/user/user.service'; import {User} from '../../../services/user/user'; import {faEdit, faFileCirclePlus, faTrash} from '@fortawesome/free-solid-svg-icons'; import {ShowService} from '../../shows/services/show.service'; import {Show} from '../../shows/services/show'; import {ShowSongService} from '../../shows/services/show-song.service'; import {AsyncPipe, DatePipe} from '@angular/common'; import {CardComponent} from '../../../widget-modules/components/card/card.component'; import {RoleDirective} from '../../../services/user/role.directive'; import {SongTextComponent} from '../../../widget-modules/components/song-text/song-text.component'; import {MatChipListbox, MatChipOption} from '@angular/material/chips'; import {ButtonRowComponent} from '../../../widget-modules/components/button-row/button-row.component'; import {ButtonComponent} from '../../../widget-modules/components/button/button.component'; import {MatMenu, MatMenuTrigger} from '@angular/material/menu'; import {FileComponent} from './file/file.component'; import {SongTypePipe} from '../../../widget-modules/pipes/song-type-translater/song-type.pipe'; import {LegalOwnerPipe} from '../../../widget-modules/pipes/legal-owner-translator/legal-owner.pipe'; import {StatusPipe} from '../../../widget-modules/pipes/status-translater/status.pipe'; import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/show-type.pipe'; import {MatTooltip} from '@angular/material/tooltip'; import {PageFrameComponent} from '../../../widget-modules/components/sidebar/page-frame.component'; @Component({ selector: 'app-song', templateUrl: './song.component.html', styleUrls: ['./song.component.less'], imports: [ CardComponent, RoleDirective, SongTextComponent, MatChipListbox, MatChipOption, ButtonRowComponent, ButtonComponent, RouterLink, MatMenuTrigger, MatMenu, FileComponent, AsyncPipe, DatePipe, SongTypePipe, LegalOwnerPipe, StatusPipe, ShowTypePipe, MatTooltip, PageFrameComponent, ], }) export class SongComponent implements OnInit { private activatedRoute = inject(ActivatedRoute); private songService = inject(SongService); private fileService = inject(FileDataService); private userService = inject(UserService); private router = inject(Router); private showService = inject(ShowService); private showSongService = inject(ShowSongService); public song$: Observable | null = null; public files$: Observable | null = null; public user$: Observable | null = null; public songCount$: Observable | null = null; public songUsageShows$: Observable | null = null; public songUsageTooltip$: Observable | null = null; public faEdit = faEdit; public faDelete = faTrash; public faFileCirclePlus = faFileCirclePlus; public privateShows$ = this.showService.list$().pipe(map(show => show.filter(_ => !_.published).sort((a, b) => b.date.toMillis() - a.date.toMillis()))); private dateFormatter = new Intl.DateTimeFormat('de-DE', {day: '2-digit', month: '2-digit', year: 'numeric'}); private showTypePipe = new ShowTypePipe(); public constructor() { const userService = this.userService; this.user$ = userService.user$; } public ngOnInit(): void { const song$ = this.activatedRoute.params.pipe( map(param => param as {songId: string}), map(param => param.songId), switchMap(songId => this.songService.read$(songId)), ); this.song$ = song$; this.files$ = this.activatedRoute.params.pipe( map(param => param as {songId: string}), map(param => param.songId), switchMap(songId => this.fileService.read$(songId)), ); this.songCount$ = combineLatest([this.userService.user$, song$]).pipe( map(([user, song]) => { if (!song) { return 0; } return user?.songUsage?.[song.id] ?? 0; }), distinctUntilChanged(), ); this.songUsageShows$ = combineLatest([this.userService.user$, this.showService.list$(), song$]).pipe( map(([user, shows, song]) => { if (!user || !song) { return []; } return shows .filter(show => show.owner === user.id) .filter(show => (show.songIds ?? []).includes(song.id)) .sort((a, b) => b.date.toMillis() - a.date.toMillis()); }), ); this.songUsageTooltip$ = combineLatest([this.songCount$, this.songUsageShows$]).pipe( map(([count, shows]) => { if (count === 0) { return 'Noch in keiner Show verwendet.'; } if (shows.length === 0) { return 'Verwendungen vorhanden, aber Show-Zuordnung noch nicht indexiert.'; } return shows.map(show => `${this.dateFormatter.format(show.date.toDate())} - ${this.showTypePipe.transform(show.showType)}`).join('\n'); }), ); } public getFlags = (flags: string): string[] => { if (!flags) { return []; } return flags.split(';').filter(_ => !!_); }; public async onDelete(songId: string): Promise { await this.songService.delete(songId); await this.router.navigateByUrl('/songs'); } public async addSongToShow(show: Show, song: Song): Promise { const newId = await this.showSongService.new$(show.id, song.id, false); await this.showService.update$(show.id, {order: [...show.order, newId ?? '']}); await this.router.navigateByUrl('/shows/' + show.id); } }