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
This commit is contained in:
Sean 2021-06-03 15:12:49 +02:00
parent 7dcdbb4ee1
commit 8b22f5ff0e
6 changed files with 78 additions and 116 deletions

View File

@ -72,15 +72,17 @@
</div>
<div class="exit">
<!-- Exit jitsi, view stream -->
<button
mat-icon-button
color="primary"
matTooltip="{{ 'Continue livestream' | translate }}"
(click)="viewStream()"
*ngIf="!!(liveStreamUrl | async)?.trim()"
>
<mat-icon>live_tv</mat-icon>
</button>
<ng-container *osPerms="permission.coreCanSeeLiveStream">
<button
mat-icon-button
color="primary"
matTooltip="{{ 'Continue livestream' | translate }}"
(click)="viewStream()"
*ngIf="hasLiveStreamUrl | async"
>
<mat-icon>live_tv</mat-icon>
</button>
</ng-container>
</div>
</div>
</div>

View File

@ -38,6 +38,8 @@ export class CallComponent extends BaseViewComponentDirective implements OnInit,
public isJitsiActiveInAnotherTab: Observable<boolean> = this.rtcService.inOtherTab;
public canEnterCall: Observable<boolean> = this.callRestrictionService.canEnterCallObservable;
public isJitsiDialogOpen: Observable<boolean> = this.rtcService.showCallDialogObservable;
public showParticles: Observable<boolean> = this.applauseService.showParticles;
public hasLiveStreamUrl: Observable<boolean> = 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<boolean> {
return this.applauseService.showParticles;
}
public get canSeeLiveStream(): Observable<boolean> {
return this.streamService.canSeeLiveStreamObservable;
}
public get liveStreamUrl(): Observable<string> {
return this.streamService.liveStreamUrlObservable;
}
private autoConnect: boolean;
@Output()

View File

@ -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();
}
}
}

View File

@ -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();
}
}

View File

@ -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>(ConferenceState.none);
public conferenceStateObservable = this.conferenceStateSubject.asObservable();
public showLiveConfObservable: Observable<boolean>;
public showLiveConfObservable: Observable<boolean> = this.configService.get<boolean>(
'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<boolean> {
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<boolean>('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<void> {
@ -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);
}
}
}

View File

@ -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<string>;
public liveStreamUrlObservable: Observable<string> = this.configService.get<string>('general_system_stream_url');
public hasLiveStreamUrlObvervable: Observable<boolean> = 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<boolean>();
public canSeeLiveStreamObservable = this.canSeeLiveStreamSubject.asObservable();
public constructor(private storageMap: StorageMap, operator: OperatorService, configService: ConfigService) {
this.liveStreamUrlObservable = configService.get<string>('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());