optimize remote #2

This commit is contained in:
2026-03-09 18:38:00 +01:00
parent 6edfb7e297
commit a46eeeee04
5 changed files with 65 additions and 37 deletions

View File

@@ -83,31 +83,40 @@ export class RemoteComponent implements OnDestroy {
globalSettingsService: GlobalSettingsService, globalSettingsService: GlobalSettingsService,
private cRef: ChangeDetectorRef private cRef: ChangeDetectorRef
) { ) {
globalSettingsService.get$ const currentShowId$ = globalSettingsService.get$
.pipe( .pipe(
filter((settings): settings is NonNullable<typeof settings> => !!settings), filter((settings): settings is NonNullable<typeof settings> => !!settings),
map(_ => _.currentShow), map(_ => _.currentShow),
filter((showId): showId is string => !!showId), filter((showId): showId is string => !!showId),
distinctUntilChanged(), distinctUntilChanged(),
switchMap(showId => takeUntil(this.destroy$)
combineLatest([this.showService.read$(showId), this.showSongService.list$(showId)]).pipe( );
map(([show, list]) => {
const presentationSongs = list.map(song => ({ const show$ = currentShowId$.pipe(
switchMap(showId => this.showService.read$(showId)),
takeUntil(this.destroy$)
);
const parsedSongs$ = currentShowId$.pipe(
switchMap(showId => this.showSongService.list$(showId)),
map(list => ({
list,
parsed: list.map(song => ({
id: song.id, id: song.id,
title: song.title, title: song.title,
sections: this.textRenderingService.parse(song.text, null, false), sections: this.textRenderingService.parse(song.text, null, false),
})); })),
return {show, list, presentationSongs}; })),
})
)
),
takeUntil(this.destroy$) takeUntil(this.destroy$)
) );
.subscribe(({show, list, presentationSongs}) => {
this.showSongs = list; combineLatest([show$, parsedSongs$])
.pipe(takeUntil(this.destroy$))
.subscribe(([show, parsedSongs]) => {
this.showSongs = parsedSongs.list;
this.show = show; this.show = show;
const order = show?.order ?? []; const order = show?.order ?? [];
const presentationSongsById = new Map(presentationSongs.map(song => [song.id, song] as const)); const presentationSongsById = new Map(parsedSongs.parsed.map(song => [song.id, song] as const));
this.presentationSongs = order.map(id => presentationSongsById.get(id) ?? null).filter((s): s is PresentationSong => !!s); this.presentationSongs = order.map(id => presentationSongsById.get(id) ?? null).filter((s): s is PresentationSong => !!s);
this.cRef.markForCheck(); this.cRef.markForCheck();
}); });

View File

@@ -60,12 +60,14 @@
</swiper-slide> </swiper-slide>
</swiper-container> </swiper-container>
<ng-container *ngIf="songs$ | async as songs">
<app-add-song <app-add-song
*ngIf="songs && !show.published && !useSwiper" *ngIf="songs && !show.published && !useSwiper"
[showSongs]="showSongs" [showSongs]="showSongs"
[show]="show" [show]="show"
[songs]="songs" [songs]="songs"
></app-add-song> ></app-add-song>
</ng-container>
<app-button-row *ngIf="!useSwiper"> <app-button-row *ngIf="!useSwiper">
<ng-container *appRole="['leader']"> <ng-container *appRole="['leader']">

View File

@@ -1,8 +1,8 @@
import {ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA, HostListener, OnDestroy, OnInit} from '@angular/core'; import {ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA, HostListener, OnDestroy, OnInit} from '@angular/core';
import {filter, map, switchMap, tap} from 'rxjs/operators'; import {filter, map, shareReplay, switchMap, tap} from 'rxjs/operators';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {ShowService} from '../services/show.service'; import {ShowService} from '../services/show.service';
import {Observable, Subscription} from 'rxjs'; import {Observable, of, Subscription} from 'rxjs';
import {Show} from '../services/show'; import {Show} from '../services/show';
import {SongService} from '../../songs/services/song.service'; import {SongService} from '../../songs/services/song.service';
import {Song} from '../../songs/services/song'; import {Song} from '../../songs/services/song';
@@ -83,7 +83,7 @@ import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/s
}) })
export class ShowComponent implements OnInit, OnDestroy { export class ShowComponent implements OnInit, OnDestroy {
public show$: Observable<Show | null> | null = null; public show$: Observable<Show | null> | null = null;
public songs: Song[] | null = null; public songs$: Observable<Song[] | null> | null = null;
public showSongs: ShowSong[] | null = null; public showSongs: ShowSong[] | null = null;
public showId: string | null = null; public showId: string | null = null;
public showText = false; public showText = false;
@@ -128,7 +128,11 @@ export class ShowComponent implements OnInit, OnDestroy {
map(param => param as {showId: string}), map(param => param as {showId: string}),
map(param => param.showId), map(param => param.showId),
tap((_: string) => (this.showId = _)), tap((_: string) => (this.showId = _)),
switchMap((showId: string) => this.showService.read$(showId)) switchMap((showId: string) => this.showService.read$(showId)),
shareReplay({
bufferSize: 1,
refCount: true,
})
); );
this.subs.push( this.subs.push(
this.activatedRoute.params this.activatedRoute.params
@@ -141,13 +145,14 @@ export class ShowComponent implements OnInit, OnDestroy {
.subscribe(_ => { .subscribe(_ => {
this.showSongs = _; this.showSongs = _;
this.cRef.markForCheck(); this.cRef.markForCheck();
}), })
this.songService );
.list$()
.pipe(filter(_ => !!_)) this.songs$ = this.show$.pipe(
.subscribe(_ => { switchMap(show => (show && !show.published ? this.songService.list$() : of(null))),
this.songs = _; shareReplay({
this.cRef.markForCheck(); bufferSize: 1,
refCount: true,
}) })
); );
} }
@@ -217,7 +222,8 @@ export class ShowComponent implements OnInit, OnDestroy {
public orderedShowSongs(show: Show): ShowSong[] { public orderedShowSongs(show: Show): ShowSong[] {
const list = this.showSongs; const list = this.showSongs;
if (!list) return []; if (!list) return [];
return show.order.map(_ => list.filter(f => f.id === _)[0]); const byId = new Map(list.map(item => [item.id, item] as const));
return show.order.map(id => byId.get(id)).filter((song): song is ShowSong => !!song);
} }
public trackBy = (_: number, show: ShowSong) => show?.id; public trackBy = (_: number, show: ShowSong) => show?.id;

View File

@@ -5,6 +5,7 @@ import {SongService} from '../../services/song.service';
import {Song} from '../../services/song'; import {Song} from '../../services/song';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {Subscription} from 'rxjs'; import {Subscription} from 'rxjs';
import {take} from 'rxjs/operators';
import {CardComponent} from '../../../../widget-modules/components/card/card.component'; import {CardComponent} from '../../../../widget-modules/components/card/card.component';
import {MatFormField, MatLabel} from '@angular/material/form-field'; import {MatFormField, MatLabel} from '@angular/material/form-field';
import {MatInput} from '@angular/material/input'; import {MatInput} from '@angular/material/input';
@@ -34,7 +35,7 @@ export class NewComponent implements OnInit, OnDestroy {
this.form.reset(); this.form.reset();
this.subs.push( this.subs.push(
this.songService.list$().subscribe(songs => { this.songService.list$().pipe(take(1)).subscribe(songs => {
const freeSongnumber = this.getFreeSongNumber(songs); const freeSongnumber = this.getFreeSongNumber(songs);
this.form.controls.number.setValue(freeSongnumber); this.form.controls.number.setValue(freeSongnumber);
}) })

View File

@@ -23,6 +23,7 @@ export class UserService {
public users$ = this.db.col$<User>('users').pipe(shareReplay({bufferSize: 1, refCount: true})); public users$ = this.db.col$<User>('users').pipe(shareReplay({bufferSize: 1, refCount: true}));
private iUserId$ = new BehaviorSubject<string | null>(null); private iUserId$ = new BehaviorSubject<string | null>(null);
private iUser$ = new BehaviorSubject<User | null>(null); private iUser$ = new BehaviorSubject<User | null>(null);
private userByIdCache = new Map<string, Observable<User | null>>();
public constructor( public constructor(
private afAuth: AngularFireAuth, private afAuth: AngularFireAuth,
@@ -52,7 +53,16 @@ export class UserService {
public currentUser = async (): Promise<User | null> => firstValueFrom(this.user$); public currentUser = async (): Promise<User | null> => firstValueFrom(this.user$);
public getUserbyId = (userId: string): Promise<User | null> => firstValueFrom(this.getUserbyId$(userId)); public getUserbyId = (userId: string): Promise<User | null> => firstValueFrom(this.getUserbyId$(userId));
public getUserbyId$ = (userId: string): Observable<User | null> => this.users$.pipe(map(_ => _.find(f => f.id === userId) || null)); public getUserbyId$ = (userId: string): Observable<User | null> => {
const cached = this.userByIdCache.get(userId);
if (cached) {
return cached;
}
const user$ = this.db.doc$<User>(`users/${userId}`).pipe(shareReplay({bufferSize: 1, refCount: true}));
this.userByIdCache.set(userId, user$);
return user$;
};
public async login(user: string, password: string): Promise<string | null> { public async login(user: string, password: string): Promise<string | null> {
const aUser = await this.afAuth.signInWithEmailAndPassword(user, password); const aUser = await this.afAuth.signInWithEmailAndPassword(user, password);