156 lines
5.5 KiB
TypeScript
156 lines
5.5 KiB
TypeScript
import {
|
|
Signal,
|
|
computed,
|
|
signal
|
|
} from '@angular/core';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import type { LocalScreenShareState } from '../media/screen-share.manager';
|
|
|
|
export class WebRtcStateController {
|
|
readonly peerId: Signal<string>;
|
|
readonly isConnected: Signal<boolean>;
|
|
readonly hasEverConnected: Signal<boolean>;
|
|
readonly isVoiceConnected: Signal<boolean>;
|
|
readonly connectedPeers: Signal<string[]>;
|
|
readonly isMuted: Signal<boolean>;
|
|
readonly isDeafened: Signal<boolean>;
|
|
readonly isScreenSharing: Signal<boolean>;
|
|
readonly isNoiseReductionEnabled: Signal<boolean>;
|
|
readonly screenStream: Signal<MediaStream | null>;
|
|
readonly isScreenShareRemotePlaybackSuppressed: Signal<boolean>;
|
|
readonly forceDefaultRemotePlaybackOutput: Signal<boolean>;
|
|
readonly hasConnectionError: Signal<boolean>;
|
|
readonly connectionErrorMessage: Signal<string | null>;
|
|
readonly shouldShowConnectionError: Signal<boolean>;
|
|
readonly peerLatencies: Signal<ReadonlyMap<string, number>>;
|
|
|
|
private activeServerId: string | null = null;
|
|
private readonly _localPeerId = signal<string>(uuidv4());
|
|
private readonly _isSignalingConnected = signal(false);
|
|
private readonly _isVoiceConnected = signal(false);
|
|
private readonly _connectedPeers = signal<string[]>([]);
|
|
private readonly _isMuted = signal(false);
|
|
private readonly _isDeafened = signal(false);
|
|
private readonly _isScreenSharing = signal(false);
|
|
private readonly _isNoiseReductionEnabled = signal(false);
|
|
private readonly _screenStreamSignal = signal<MediaStream | null>(null);
|
|
private readonly _isScreenShareRemotePlaybackSuppressed = signal(false);
|
|
private readonly _forceDefaultRemotePlaybackOutput = signal(false);
|
|
private readonly _hasConnectionError = signal(false);
|
|
private readonly _connectionErrorMessage = signal<string | null>(null);
|
|
private readonly _hasEverConnected = signal(false);
|
|
private readonly _peerLatencies = signal<ReadonlyMap<string, number>>(new Map());
|
|
|
|
constructor() {
|
|
this.peerId = computed(() => this._localPeerId());
|
|
this.isConnected = computed(() => this._isSignalingConnected());
|
|
this.hasEverConnected = computed(() => this._hasEverConnected());
|
|
this.isVoiceConnected = computed(() => this._isVoiceConnected());
|
|
this.connectedPeers = computed(() => this._connectedPeers());
|
|
this.isMuted = computed(() => this._isMuted());
|
|
this.isDeafened = computed(() => this._isDeafened());
|
|
this.isScreenSharing = computed(() => this._isScreenSharing());
|
|
this.isNoiseReductionEnabled = computed(() => this._isNoiseReductionEnabled());
|
|
this.screenStream = computed(() => this._screenStreamSignal());
|
|
this.isScreenShareRemotePlaybackSuppressed = computed(() => this._isScreenShareRemotePlaybackSuppressed());
|
|
this.forceDefaultRemotePlaybackOutput = computed(() => this._forceDefaultRemotePlaybackOutput());
|
|
this.hasConnectionError = computed(() => this._hasConnectionError());
|
|
this.connectionErrorMessage = computed(() => this._connectionErrorMessage());
|
|
this.shouldShowConnectionError = computed(() => {
|
|
if (!this._hasConnectionError())
|
|
return false;
|
|
|
|
if (this._isVoiceConnected() && this._connectedPeers().length > 0)
|
|
return false;
|
|
|
|
return true;
|
|
});
|
|
|
|
this.peerLatencies = computed(() => this._peerLatencies());
|
|
}
|
|
|
|
get currentServerId(): string | null {
|
|
return this.activeServerId;
|
|
}
|
|
|
|
getLocalPeerId(): string {
|
|
return this._localPeerId();
|
|
}
|
|
|
|
isSignalingConnected(): boolean {
|
|
return this._isSignalingConnected();
|
|
}
|
|
|
|
isVoiceConnectedActive(): boolean {
|
|
return this._isVoiceConnected();
|
|
}
|
|
|
|
isScreenSharingActive(): boolean {
|
|
return this._isScreenSharing();
|
|
}
|
|
|
|
setCurrentServer(serverId: string): void {
|
|
this.activeServerId = serverId;
|
|
}
|
|
|
|
setVoiceConnected(connected: boolean): void {
|
|
this._isVoiceConnected.set(connected);
|
|
}
|
|
|
|
setMuted(muted: boolean): void {
|
|
this._isMuted.set(muted);
|
|
}
|
|
|
|
setDeafened(deafened: boolean): void {
|
|
this._isDeafened.set(deafened);
|
|
}
|
|
|
|
setNoiseReductionEnabled(enabled: boolean): void {
|
|
this._isNoiseReductionEnabled.set(enabled);
|
|
}
|
|
|
|
setConnectedPeers(peers: string[]): void {
|
|
this._connectedPeers.set(peers);
|
|
}
|
|
|
|
syncPeerLatencies(latencies: ReadonlyMap<string, number>): void {
|
|
this._peerLatencies.set(new Map(latencies));
|
|
}
|
|
|
|
applyLocalScreenShareState(state: LocalScreenShareState): void {
|
|
this._isScreenSharing.set(state.active);
|
|
this._screenStreamSignal.set(state.stream);
|
|
this._isScreenShareRemotePlaybackSuppressed.set(state.suppressRemotePlayback);
|
|
this._forceDefaultRemotePlaybackOutput.set(state.forceDefaultRemotePlaybackOutput);
|
|
}
|
|
|
|
clearPeerViewState(): void {
|
|
this._connectedPeers.set([]);
|
|
this._peerLatencies.set(new Map());
|
|
}
|
|
|
|
clearScreenShareState(): void {
|
|
this._isScreenSharing.set(false);
|
|
this._screenStreamSignal.set(null);
|
|
this._isScreenShareRemotePlaybackSuppressed.set(false);
|
|
this._forceDefaultRemotePlaybackOutput.set(false);
|
|
}
|
|
|
|
resetConnectionState(): void {
|
|
this._isSignalingConnected.set(false);
|
|
this._hasEverConnected.set(false);
|
|
this._hasConnectionError.set(false);
|
|
this._connectionErrorMessage.set(null);
|
|
}
|
|
|
|
updateSignalingConnectionStatus(anyConnected: boolean, markHasEverConnected: boolean, errorMessage?: string): void {
|
|
if (markHasEverConnected) {
|
|
this._hasEverConnected.set(true);
|
|
}
|
|
|
|
this._isSignalingConnected.set(anyConnected);
|
|
this._hasConnectionError.set(!anyConnected);
|
|
this._connectionErrorMessage.set(anyConnected ? null : (errorMessage ?? 'Disconnected from signaling server'));
|
|
}
|
|
}
|