diff --git a/client/src/app/site/interaction/components/call/call.component.html b/client/src/app/site/interaction/components/call/call.component.html index 5233fd94c..ee87ed83f 100644 --- a/client/src/app/site/interaction/components/call/call.component.html +++ b/client/src/app/site/interaction/components/call/call.component.html @@ -72,15 +72,17 @@
- + + +
diff --git a/client/src/app/site/interaction/components/call/call.component.ts b/client/src/app/site/interaction/components/call/call.component.ts index 00852e182..4b44badbe 100644 --- a/client/src/app/site/interaction/components/call/call.component.ts +++ b/client/src/app/site/interaction/components/call/call.component.ts @@ -38,6 +38,8 @@ export class CallComponent extends BaseViewComponentDirective implements OnInit, public isJitsiActiveInAnotherTab: Observable = this.rtcService.inOtherTab; public canEnterCall: Observable = this.callRestrictionService.canEnterCallObservable; public isJitsiDialogOpen: Observable = this.rtcService.showCallDialogObservable; + public showParticles: Observable = this.applauseService.showParticles; + public hasLiveStreamUrl: Observable = this.streamService.hasLiveStreamUrlObvervable; public isJitsiActive: boolean; public isJoined: boolean; @@ -64,18 +66,6 @@ export class CallComponent extends BaseViewComponentDirective implements OnInit, return this.isJitsiActive && this.isJoined; } - public get showParticles(): Observable { - return this.applauseService.showParticles; - } - - public get canSeeLiveStream(): Observable { - return this.streamService.canSeeLiveStreamObservable; - } - - public get liveStreamUrl(): Observable { - return this.streamService.liveStreamUrlObservable; - } - private autoConnect: boolean; @Output() diff --git a/client/src/app/site/interaction/components/interaction-container/interaction-container.component.ts b/client/src/app/site/interaction/components/interaction-container/interaction-container.component.ts index 6a8a50e44..67a9c60ba 100644 --- a/client/src/app/site/interaction/components/interaction-container/interaction-container.component.ts +++ b/client/src/app/site/interaction/components/interaction-container/interaction-container.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Title } from '@angular/platform-browser'; @@ -18,7 +18,7 @@ import { StreamService } from '../../services/stream.service'; styleUrls: ['./interaction-container.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class InteractionContainerComponent extends BaseViewComponentDirective { +export class InteractionContainerComponent extends BaseViewComponentDirective implements OnInit { public showBody = false; private streamRunning = false; @@ -63,11 +63,6 @@ export class InteractionContainerComponent extends BaseViewComponentDirective { ) { super(titleService, translate, matSnackBar); this.subscriptions.push( - interactionService.conferenceStateObservable.pipe(distinctUntilChanged()).subscribe(state => { - if (state) { - this.clearTitles(); - } - }), rtcService.showCallDialogObservable.subscribe(show => { if (show) { this.showBody = false; @@ -91,9 +86,20 @@ export class InteractionContainerComponent extends BaseViewComponentDirective { ); } + public ngOnInit(): void { + this.subscriptions.push( + this.interactionService.conferenceStateObservable.pipe(distinctUntilChanged()).subscribe(state => { + if (state) { + this.clearTitles(); + } + }) + ); + } + private clearTitles(): void { this.containerHeadTitle = ''; this.containerHeadSubtitle = ''; + this.cd.markForCheck(); this.cd.detectChanges(); } @@ -104,14 +110,14 @@ export class InteractionContainerComponent extends BaseViewComponentDirective { public updateTitle(title: string): void { if (title !== this.containerHeadTitle) { this.containerHeadTitle = title ?? ''; - this.cd.detectChanges(); + this.cd.markForCheck(); } } public updateSubtitle(title: string): void { if (title !== this.containerHeadSubtitle) { this.containerHeadSubtitle = title ?? ''; - this.cd.detectChanges(); + this.cd.markForCheck(); } } } diff --git a/client/src/app/site/interaction/services/call-restriction.service.ts b/client/src/app/site/interaction/services/call-restriction.service.ts index 7939ee764..d570232a9 100644 --- a/client/src/app/site/interaction/services/call-restriction.service.ts +++ b/client/src/app/site/interaction/services/call-restriction.service.ts @@ -88,7 +88,7 @@ export class CallRestrictionService { ) { this.hasToEnterCallSubject.next(); } - } else if (operatorClosIndex === UserListIndexType.NotOnList && this.restricted) { + } else if (operatorClosIndex === UserListIndexType.NotOnList && this.restricted && !this.canManageSpeaker) { this.hasToLeaveCallSubject.next(); } } diff --git a/client/src/app/site/interaction/services/interaction.service.ts b/client/src/app/site/interaction/services/interaction.service.ts index 98d0abe33..886aacfae 100644 --- a/client/src/app/site/interaction/services/interaction.service.ts +++ b/client/src/app/site/interaction/services/interaction.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; -import { BehaviorSubject, Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; +import { distinctUntilChanged, map } from 'rxjs/operators'; import { ConfigService } from 'app/core/ui-services/config.service'; import { CallRestrictionService } from './call-restriction.service'; @@ -9,9 +9,9 @@ import { RtcService } from './rtc.service'; import { StreamService } from './stream.service'; export enum ConferenceState { - none, - stream, - jitsi + none = 1, + stream = 2, + jitsi = 3 } @Injectable({ @@ -20,17 +20,14 @@ export enum ConferenceState { export class InteractionService { private conferenceStateSubject = new BehaviorSubject(ConferenceState.none); public conferenceStateObservable = this.conferenceStateSubject.asObservable(); - public showLiveConfObservable: Observable; + public showLiveConfObservable: Observable = this.configService.get( + 'general_system_conference_show' + ); private get conferenceState(): ConferenceState { return this.conferenceStateSubject.value; } - private isJitsiEnabled: boolean; private isInCall: boolean; - private isJitsiActive: boolean; - private hasLiveStreamUrl: boolean; - private canSeeLiveStream: boolean; - private showLiveConf: boolean; public get isConfStateStream(): Observable { return this.conferenceStateObservable.pipe(map(state => state === ConferenceState.stream)); @@ -50,36 +47,38 @@ export class InteractionService { private rtcService: RtcService, private callRestrictionService: CallRestrictionService ) { - this.showLiveConfObservable = this.configService.get('general_system_conference_show'); + combineLatest( + this.showLiveConfObservable, + this.streamService.hasLiveStreamUrlObvervable, + this.streamService.canSeeLiveStreamObservable, + this.rtcService.isJitsiEnabledObservable, + this.rtcService.isJoinedObservable, + this.rtcService.isJitsiActiveObservable, + this.callRestrictionService.canEnterCallObservable, + (showConf, hasStreamUrl, canSeeStream, jitsiEnabled, inCall, jitsiActive, canEnterCall) => { + this.isInCall = inCall; - /** - * If you want to somehow simplify this using rxjs merge-map magic or something - * be my guest. - */ - this.streamService.liveStreamUrlObservable.subscribe(url => { - this.hasLiveStreamUrl = !!url?.trim() ?? false; - this.detectDeadState(); - }); - - this.streamService.canSeeLiveStreamObservable.subscribe(canSee => { - this.canSeeLiveStream = canSee; - this.detectDeadState(); - }); - - this.rtcService.isJitsiEnabledObservable.subscribe(enabled => { - this.isJitsiEnabled = enabled; - this.detectDeadState(); - }); - - this.rtcService.isJoinedObservable.subscribe(joined => { - this.isInCall = joined; - this.detectDeadState(); - }); - - this.rtcService.isJitsiActiveObservable.subscribe(isActive => { - this.isJitsiActive = isActive; - this.detectDeadState(); - }); + /** + * most importantly, if there is a call, to not change the state here + */ + if (inCall || jitsiActive) { + return; + } + if (hasStreamUrl && canSeeStream) { + return ConferenceState.stream; + } else if (showConf && jitsiEnabled && canEnterCall && (!hasStreamUrl || !canSeeStream)) { + return ConferenceState.jitsi; + } else { + return ConferenceState.none; + } + } + ) + .pipe(distinctUntilChanged()) + .subscribe(state => { + if (state) { + this.setConferenceState(state); + } + }); this.callRestrictionService.hasToEnterCallObservable.subscribe(() => { if (!this.isInCall) { @@ -91,13 +90,6 @@ export class InteractionService { this.callRestrictionService.hasToLeaveCallObservable.subscribe(() => { this.viewStream(); }); - - this.showLiveConfObservable.subscribe(showConf => { - this.showLiveConf = showConf; - this.detectDeadState(); - }); - - this.detectDeadState(); } public async enterCall(): Promise { @@ -117,37 +109,4 @@ export class InteractionService { this.conferenceStateSubject.next(newState); } } - - /** - * this is the "dead" state; you would see the jitsi state; but are not connected - * or the connection is prohibited. If this occurs and a live stream - * becomes available, switch to the stream state - */ - private detectDeadState(): void { - if ( - this.isInCall === undefined || - this.isJitsiActive === undefined || - this.hasLiveStreamUrl === undefined || - this.conferenceState === undefined || - this.canSeeLiveStream === undefined || - this.isJitsiEnabled === undefined - ) { - return; - } - - /** - * most importantly, if there is a call, to not change the state! - */ - if (this.isInCall || this.isJitsiActive) { - return; - } - - if (this.hasLiveStreamUrl && this.canSeeLiveStream) { - this.viewStream(); - } else if (this.showLiveConf && (!this.hasLiveStreamUrl || !this.canSeeLiveStream) && this.isJitsiEnabled) { - this.enterCall(); - } else { - this.setConferenceState(ConferenceState.none); - } - } } diff --git a/client/src/app/site/interaction/services/stream.service.ts b/client/src/app/site/interaction/services/stream.service.ts index 61acf6d10..a15573db9 100644 --- a/client/src/app/site/interaction/services/stream.service.ts +++ b/client/src/app/site/interaction/services/stream.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { StorageMap } from '@ngx-pwa/local-storage'; import { Observable, Subject } from 'rxjs'; -import { distinctUntilChanged } from 'rxjs/operators'; +import { distinctUntilChanged, map } from 'rxjs/operators'; import { OperatorService, Permission } from 'app/core/core-services/operator.service'; import { ConfigService } from 'app/core/ui-services/config.service'; @@ -13,7 +13,10 @@ const STREAM_RUNNING_STORAGE_KEY = 'streamIsRunning'; providedIn: 'root' }) export class StreamService { - public liveStreamUrlObservable: Observable; + public liveStreamUrlObservable: Observable = this.configService.get('general_system_stream_url'); + public hasLiveStreamUrlObvervable: Observable = this.liveStreamUrlObservable.pipe( + map(url => !!url?.trim() || false) + ); /** * undefined is controlled behavior, meaning, this property was not @@ -28,9 +31,11 @@ export class StreamService { private canSeeLiveStreamSubject = new Subject(); public canSeeLiveStreamObservable = this.canSeeLiveStreamSubject.asObservable(); - public constructor(private storageMap: StorageMap, operator: OperatorService, configService: ConfigService) { - this.liveStreamUrlObservable = configService.get('general_system_stream_url'); - + public constructor( + private storageMap: StorageMap, + operator: OperatorService, + private configService: ConfigService + ) { this.streamLoadedOnceObservable = this.storageMap .watch(STREAM_RUNNING_STORAGE_KEY, { type: 'boolean' }) .pipe(distinctUntilChanged());