add song usage (experimental)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {BehaviorSubject, firstValueFrom, Observable} from 'rxjs';
|
||||
import {DbService} from '../../../services/db.service';
|
||||
import {Show} from './show';
|
||||
import {map} from 'rxjs/operators';
|
||||
@@ -15,6 +15,8 @@ export class ShowDataService {
|
||||
}
|
||||
|
||||
public list$ = new BehaviorSubject<Show[]>([]);
|
||||
public list = () => firstValueFrom(this.dbService.col$<Show>(this.collection));
|
||||
|
||||
public read$ = (showId: string): Observable<Show | null> => this.list$.pipe(map(_ => _.find(s => s.id === showId) || null));
|
||||
|
||||
// public list$ = (): Observable<Show[]> => this.dbService.col$(this.collection);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {DbService} from '../../../services/db.service';
|
||||
import {Observable} from 'rxjs';
|
||||
import {firstValueFrom, Observable} from 'rxjs';
|
||||
import {ShowSong} from './show-song';
|
||||
import {QueryFn} from '@angular/fire/compat/firestore/interfaces';
|
||||
|
||||
@@ -14,6 +14,8 @@ export class ShowSongDataService {
|
||||
public constructor(private dbService: DbService) {}
|
||||
|
||||
public list$ = (showId: string, queryFn?: QueryFn): Observable<ShowSong[]> => this.dbService.col$(`${this.collection}/${showId}/${this.subCollection}`, queryFn);
|
||||
public list = (showId: string): Promise<ShowSong[]> => firstValueFrom(this.list$(showId));
|
||||
|
||||
public read$ = (showId: string, songId: string): Observable<ShowSong | null> => this.dbService.doc$(`${this.collection}/${showId}/${this.subCollection}/${songId}`);
|
||||
public update$ = async (showId: string, songId: string, data: Partial<ShowSong>): Promise<void> =>
|
||||
await this.dbService.doc(`${this.collection}/${showId}/${this.subCollection}/${songId}`).update(data);
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {ShowSongDataService} from './show-song-data.service';
|
||||
import {firstValueFrom, Observable} from 'rxjs';
|
||||
import {firstValueFrom, forkJoin, mergeMap, Observable} from 'rxjs';
|
||||
import {ShowSong} from './show-song';
|
||||
import {SongDataService} from '../../songs/services/song-data.service';
|
||||
import {UserService} from '../../../services/user/user.service';
|
||||
import {ShowService} from './show.service';
|
||||
import {map, switchMap} from 'rxjs/operators';
|
||||
import {ShowDataService} from './show-data.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -14,7 +16,8 @@ export class ShowSongService {
|
||||
private showSongDataService: ShowSongDataService,
|
||||
private songDataService: SongDataService,
|
||||
private userService: UserService,
|
||||
private showService: ShowService
|
||||
private showService: ShowService,
|
||||
private showDataService: ShowDataService
|
||||
) {}
|
||||
|
||||
public async new$(showId: string, songId: string, addedLive = false): Promise<string | null> {
|
||||
@@ -47,5 +50,39 @@ export class ShowSongService {
|
||||
await this.showService.update$(showId, {order});
|
||||
}
|
||||
|
||||
public countSongUsage$(songId: string) {
|
||||
return this.userService.user$.pipe(
|
||||
switchMap(user =>
|
||||
this.showService.list$().pipe(
|
||||
map(shows => {
|
||||
const myShows = shows.filter(_ => _.owner === user?.id);
|
||||
return myShows.map(show => this.list$(show.id));
|
||||
}),
|
||||
mergeMap(songs => forkJoin(songs)),
|
||||
map(songs => songs.reduce((pn, u) => [...pn, ...u], [])),
|
||||
map(songs => songs.reduce((count, song) => (song.songId === songId ? 1 : 0), 0))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public async countSongUsage(songId: string) {
|
||||
// return 0; // todo
|
||||
|
||||
const user = await this.userService.currentUser();
|
||||
const userId = user?.id;
|
||||
if (!userId) return null;
|
||||
const shows = await this.showDataService.list();
|
||||
let count = 0;
|
||||
for (const show of shows.filter(_ => _.owner === userId)) {
|
||||
const songs = await this.showSongDataService.list(show.id);
|
||||
for (const song of songs) {
|
||||
if (song.songId === songId) count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public update$ = async (showId: string, songId: string, data: Partial<ShowSong>): Promise<void> => await this.showSongDataService.update$(showId, songId, data);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<ng-container *ngIf="count>0">Verwendet: {{count}}x</ng-container>
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {SongUsedComponent} from './song-used.component';
|
||||
|
||||
describe('SongUsedComponent', () => {
|
||||
let component: SongUsedComponent;
|
||||
let fixture: ComponentFixture<SongUsedComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [SongUsedComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SongUsedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
18
src/app/modules/songs/song/song-used/song-used.component.ts
Normal file
18
src/app/modules/songs/song/song-used/song-used.component.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core';
|
||||
import {ShowSongService} from '../../../shows/services/show-song.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-song-used',
|
||||
templateUrl: './song-used.component.html',
|
||||
styleUrls: ['./song-used.component.less'],
|
||||
})
|
||||
export class SongUsedComponent implements OnInit {
|
||||
@Input() public songId: string | null = null;
|
||||
public count = 0;
|
||||
|
||||
public constructor(private service: ShowSongService, private cref: ChangeDetectorRef) {}
|
||||
|
||||
public async ngOnInit() {
|
||||
this.count = (await this.service.countSongUsage(this.songId ?? '')) ?? 0;
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,9 @@
|
||||
<div *ngIf="song.label">Verlag: {{ song.label }}</div>
|
||||
<div *ngIf="song.origin">Quelle: {{ song.origin }}</div>
|
||||
<div *ngIf="song.origin">Quelle: {{ song.origin }}</div>
|
||||
<div>
|
||||
<app-song-used [songId]="song.id"></app-song-used>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -65,6 +68,7 @@
|
||||
>Bearbeiten
|
||||
</app-button>
|
||||
<ng-container *appRole="['leader']">
|
||||
|
||||
<app-button [icon]="faFileCirclePlus" [matMenuTriggerFor]="menu">
|
||||
Zu Veranstaltung hinzufügen
|
||||
</app-button>
|
||||
|
||||
@@ -15,9 +15,10 @@ import {ButtonModule} from '../../../widget-modules/components/button/button.mod
|
||||
import {FileComponent} from './file/file.component';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {ShowTypeTranslaterModule} from '../../../widget-modules/pipes/show-type-translater/show-type-translater.module';
|
||||
import {SongUsedComponent} from './song-used/song-used.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [SongComponent, FileComponent],
|
||||
declarations: [SongComponent, FileComponent, SongUsedComponent],
|
||||
exports: [SongComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@@ -21,7 +21,13 @@
|
||||
></app-link>
|
||||
<app-link [icon]="faUser" link="/user" text="Benutzer"></app-link>
|
||||
</div>
|
||||
<div *appRole="['user', 'presenter', 'leader']" class="actions">
|
||||
<app-filter></app-filter>
|
||||
<div class="actions">
|
||||
<app-filter *appRole="['user', 'presenter', 'leader']"></app-filter>
|
||||
<app-link
|
||||
*ngIf="userService.userId$|async"
|
||||
[icon]="faLogout"
|
||||
link="/user/logout"
|
||||
></app-link>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
@@ -32,7 +32,7 @@ nav {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
padding-right: 20px;
|
||||
// padding-right: 20px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {faChalkboard, faMusic, faPersonBooth, faUserCog} from '@fortawesome/free-solid-svg-icons';
|
||||
import {faChalkboard, faMusic, faPersonBooth, faRightFromBracket, faUserCog} from '@fortawesome/free-solid-svg-icons';
|
||||
import {fromEvent, Observable} from 'rxjs';
|
||||
import {distinctUntilChanged, map, shareReplay, startWith} from 'rxjs/operators';
|
||||
import {UserService} from '../../../../services/user/user.service';
|
||||
import {Router} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navigation',
|
||||
@@ -13,6 +15,9 @@ export class NavigationComponent {
|
||||
public faShows = faPersonBooth;
|
||||
public faUser = faUserCog;
|
||||
public faPresentation = faChalkboard;
|
||||
public faLogout = faRightFromBracket;
|
||||
|
||||
public constructor(public userService: UserService, private router: Router) {}
|
||||
|
||||
public readonly windowScroll$: Observable<number> = fromEvent(window, 'scroll').pipe(
|
||||
map(() => window.scrollY),
|
||||
@@ -22,4 +27,9 @@ export class NavigationComponent {
|
||||
);
|
||||
|
||||
public isNavigationHidden = (scroll: number | null): boolean => (scroll ?? 0) > 60;
|
||||
|
||||
public async logout(): Promise<void> {
|
||||
await this.userService.logout();
|
||||
await this.router.navigateByUrl('/');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
.card {
|
||||
margin: 20px;
|
||||
border-radius: 8px;
|
||||
background: #fffc;
|
||||
background: #fff;
|
||||
backdrop-filter: blur(12px);
|
||||
overflow: hidden;
|
||||
width: 800px;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<link href="favicon-16x16.png" rel="icon" sizes="16x16" type="image/png">
|
||||
<link color="#4286f4" href="safari-pinned-tab.svg" rel="mask-icon">
|
||||
<meta content="#4286f4" name="msapplication-TileColor">
|
||||
<meta content="#4286f4" name="theme-color">
|
||||
<meta content="#222222" name="theme-color">
|
||||
|
||||
<link href="manifest.webmanifest" rel="manifest">
|
||||
<meta content="#4286f4" name="theme-color">
|
||||
|
||||
Reference in New Issue
Block a user