Files
Toju/toju-app/src/app/infrastructure/realtime/state/webrtc-state-controller.ts
2026-03-29 23:55:24 +02:00

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'));
}
}