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; readonly isConnected: Signal; readonly hasEverConnected: Signal; readonly isVoiceConnected: Signal; readonly connectedPeers: Signal; readonly isMuted: Signal; readonly isDeafened: Signal; readonly isScreenSharing: Signal; readonly isNoiseReductionEnabled: Signal; readonly screenStream: Signal; readonly isScreenShareRemotePlaybackSuppressed: Signal; readonly forceDefaultRemotePlaybackOutput: Signal; readonly hasConnectionError: Signal; readonly connectionErrorMessage: Signal; readonly shouldShowConnectionError: Signal; readonly peerLatencies: Signal>; private activeServerId: string | null = null; private readonly _localPeerId = signal(uuidv4()); private readonly _isSignalingConnected = signal(false); private readonly _isVoiceConnected = signal(false); private readonly _connectedPeers = signal([]); private readonly _isMuted = signal(false); private readonly _isDeafened = signal(false); private readonly _isScreenSharing = signal(false); private readonly _isNoiseReductionEnabled = signal(false); private readonly _screenStreamSignal = signal(null); private readonly _isScreenShareRemotePlaybackSuppressed = signal(false); private readonly _forceDefaultRemotePlaybackOutput = signal(false); private readonly _hasConnectionError = signal(false); private readonly _connectionErrorMessage = signal(null); private readonly _hasEverConnected = signal(false); private readonly _peerLatencies = signal>(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): 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')); } }