import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { QueryClient } from '@tanstack/angular-query-experimental'; import { combineLatest, switchMap, timer } from 'rxjs'; import { filter, map, startWith } from 'rxjs/operators'; import { injectCatalogService } from '@/core/services/catalogs.service'; import { injectWebsocketService, WSEvents } from '@/core/services/websocket.service'; import { CountdownComponent } from '@/features/countdown/countdown.component'; import { ActivityTimer } from '@/features/live/live.models'; import { injectLiveService, LiveService } from '@/features/live/live.service'; import { PlenaryComponent } from '@/features/live/plenary/plenary.component'; import { MainLayoutComponent } from '@/shared/layout/main-layout/main-layout.component'; @Component({ selector: 'app-streaming', templateUrl: './streaming.component.html', styleUrls: ['./streaming.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MainLayoutComponent, PlenaryComponent, CountdownComponent], providers: [LiveService], }) export default class StreamingComponent { private queryClient = inject(QueryClient); private liveServ = injectLiveService(); private catalogServ = injectCatalogService(); private websocketServ = injectWebsocketService(); activity$ = this.loadLive(); countdown$ = this.loadCountdown(); activity = toSignal(this.activity$, { initialValue: null }); countdown = toSignal(this.countdown$, { initialValue: null }); private sessionEndSub = this.countdownToEnd(); /** * * Get live, set countdown and register time in session * * 1. call getLive; get all events with getAllLiveEvents * * 2. check if current event exists * * - if exists, set as activity, set interval (per second) to end_unix_time & set interval (per minute) to record time in session * * - if not, check if next or previous event, remove intervals and unset activity * * - if next, set countdown date as start_unix_time * * - if prev, unset countdown date to show no live events copy * * 3. when countdown stops or end_unix_time interval ends, go to step 1 */ ngOnDestroy() { this.sessionEndSub.unsubscribe(); } refetchSchedule() { this.queryClient.invalidateQueries({ queryKey: ['schedule'] }); this.queryClient.invalidateQueries({ queryKey: ['countdown'] }); } private loadLive() { return this.websocketServ.listen(WSEvents.LIVE, ['schedule']).pipe( startWith(0), switchMap(() => this.liveServ.getAllLiveEvents()), map((events) => events.find((event) => event.current) || null) ); } private countdownToEnd() { const obs$ = this.activity$.pipe( switchMap((activity) => timer(0, 1000).pipe(map((time) => [activity, time]))), map((data) => data as ActivityTimer), filter(([activity]) => Boolean(activity)) ); return obs$.subscribe(async ([activity, time]) => { const eventHasEnded = Date.now() >= activity.end_unix_time * 1000; eventHasEnded && this.refetchSchedule(); time % 60 === 0 && (await this.liveServ.saveTimeSession(activity.id)); }); } private loadCountdown() { return combineLatest([ this.websocketServ.listen(WSEvents.COUNTDOWN).pipe(startWith(0)), this.activity$.pipe(filter((activity) => !activity)), ]).pipe(switchMap(() => this.catalogServ.getCountdown())); } // private testCountdown() { // return this.loadCountdown().pipe( // map((countdown) => ({ // ...countdown, // countdown_unixtime: this.liveServ.now + 30, // open: Math.floor(Date.now() / 1000) > this.liveServ.now + 60, // })) // ); // } }