From 8b22f5ff0e1592a980cf2bdc716e0f35822abd81 Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 3 Jun 2021 15:12:49 +0200 Subject: [PATCH] Hide conference bar without interaction Hide the conference bar if there is no stream and no entering permission. Hides the "see stream" button if use user has no permission to see the stream (call list window) Use rxjs combineLatest for easier "dead state" detection with less change pushing --- .../components/call/call.component.html | 20 +-- .../components/call/call.component.ts | 14 +-- .../interaction-container.component.ts | 24 ++-- .../services/call-restriction.service.ts | 2 +- .../services/interaction.service.ts | 119 ++++++------------ .../interaction/services/stream.service.ts | 15 ++- 6 files changed, 78 insertions(+), 116 deletions(-) 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());