Add eslint

This commit is contained in:
2026-03-03 22:56:12 +01:00
parent d641229f9d
commit ad0e28bf84
92 changed files with 2656 additions and 1127 deletions

View File

@@ -11,6 +11,7 @@
* This file wires them together and exposes a public API that is
* identical to the old monolithic service so consumers don't change.
*/
/* eslint-disable @typescript-eslint/member-ordering, @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any */
import { Injectable, signal, computed, inject, OnDestroy } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { v4 as uuidv4 } from 'uuid';
@@ -43,11 +44,11 @@ import {
SIGNALING_TYPE_USER_LEFT,
DEFAULT_DISPLAY_NAME,
P2P_TYPE_VOICE_STATE,
P2P_TYPE_SCREEN_STATE,
P2P_TYPE_SCREEN_STATE
} from './webrtc';
@Injectable({
providedIn: 'root',
providedIn: 'root'
})
export class WebRTCService implements OnDestroy {
private readonly timeSync = inject(TimeSyncService);
@@ -97,8 +98,12 @@ export class WebRTCService implements OnDestroy {
readonly hasConnectionError = computed(() => this._hasConnectionError());
readonly connectionErrorMessage = computed(() => this._connectionErrorMessage());
readonly shouldShowConnectionError = computed(() => {
if (!this._hasConnectionError()) return false;
if (this._isVoiceConnected() && this._connectedPeers().length > 0) return false;
if (!this._hasConnectionError())
return false;
if (this._isVoiceConnected() && this._connectedPeers().length > 0)
return false;
return true;
});
/** Per-peer latency map (ms). Read via `peerLatencies()`. */
@@ -135,7 +140,7 @@ export class WebRTCService implements OnDestroy {
this.logger,
() => this.lastIdentifyCredentials,
() => this.lastJoinedServer,
() => this.memberServerIds,
() => this.memberServerIds
);
this.peerManager = new PeerConnectionManager(this.logger, null!);
@@ -152,7 +157,7 @@ export class WebRTCService implements OnDestroy {
getVoiceStateSnapshot: (): VoiceStateSnapshot => this.getCurrentVoiceState(),
getIdentifyCredentials: (): IdentifyCredentials | null => this.lastIdentifyCredentials,
getLocalPeerId: (): string => this._localPeerId(),
isScreenSharingActive: (): boolean => this._isScreenSharing(),
isScreenSharingActive: (): boolean => this._isScreenSharing()
});
this.mediaManager.setCallbacks({
@@ -162,7 +167,7 @@ export class WebRTCService implements OnDestroy {
broadcastMessage: (event: any): void => this.peerManager.broadcastMessage(event),
getIdentifyOderId: (): string => this.lastIdentifyCredentials?.oderId || this._localPeerId(),
getIdentifyDisplayName: (): string =>
this.lastIdentifyCredentials?.displayName || DEFAULT_DISPLAY_NAME,
this.lastIdentifyCredentials?.displayName || DEFAULT_DISPLAY_NAME
});
this.screenShareManager.setCallbacks({
@@ -170,7 +175,7 @@ export class WebRTCService implements OnDestroy {
this.peerManager.activePeerConnections,
getLocalMediaStream: (): MediaStream | null => this.mediaManager.getLocalStream(),
renegotiate: (peerId: string): Promise<void> => this.peerManager.renegotiate(peerId),
broadcastCurrentStates: (): void => this.peerManager.broadcastCurrentStates(),
broadcastCurrentStates: (): void => this.peerManager.broadcastCurrentStates()
});
this.wireManagerEvents();
@@ -180,7 +185,10 @@ export class WebRTCService implements OnDestroy {
// Signaling → connection status
this.signalingManager.connectionStatus$.subscribe(({ connected, errorMessage }) => {
this._isSignalingConnected.set(connected);
if (connected) this._hasEverConnected.set(true);
if (connected)
this._hasEverConnected.set(true);
this._hasConnectionError.set(!connected);
this._connectionErrorMessage.set(connected ? null : (errorMessage ?? null));
});
@@ -193,7 +201,7 @@ export class WebRTCService implements OnDestroy {
// Peer manager → connected peers signal
this.peerManager.connectedPeersChanged$.subscribe((peers: string[]) =>
this._connectedPeers.set(peers),
this._connectedPeers.set(peers)
);
// Media manager → voice connected signal
@@ -204,6 +212,7 @@ export class WebRTCService implements OnDestroy {
// Peer manager → latency updates
this.peerManager.peerLatencyChanged$.subscribe(({ peerId, latencyMs }) => {
const next = new Map(this.peerManager.peerLatencies);
this._peerLatencies.set(next);
});
}
@@ -215,23 +224,27 @@ export class WebRTCService implements OnDestroy {
switch (message.type) {
case SIGNALING_TYPE_CONNECTED:
this.logger.info('Server connected', { oderId: message.oderId });
if (typeof message.serverTime === 'number') {
this.timeSync.setFromServerTime(message.serverTime);
}
break;
case SIGNALING_TYPE_SERVER_USERS: {
this.logger.info('Server users', {
count: Array.isArray(message.users) ? message.users.length : 0,
serverId: message.serverId,
serverId: message.serverId
});
if (message.users && Array.isArray(message.users)) {
message.users.forEach((user: { oderId: string; displayName: string }) => {
if (!user.oderId) return;
if (!user.oderId)
return;
const existing = this.peerManager.activePeerConnections.get(user.oderId);
const healthy = this.isPeerHealthy(existing);
if (existing && !healthy) {
this.logger.info('Removing stale peer before recreate', { oderId: user.oderId });
this.peerManager.removePeer(user.oderId);
@@ -240,23 +253,25 @@ export class WebRTCService implements OnDestroy {
if (!healthy) {
this.logger.info('Create peer connection to existing user', {
oderId: user.oderId,
serverId: message.serverId,
serverId: message.serverId
});
this.peerManager.createPeerConnection(user.oderId, true);
this.peerManager.createAndSendOffer(user.oderId);
if (message.serverId) {
this.peerServerMap.set(user.oderId, message.serverId);
}
}
});
}
break;
}
case SIGNALING_TYPE_USER_JOINED:
this.logger.info('User joined', {
displayName: message.displayName,
oderId: message.oderId,
oderId: message.oderId
});
break;
@@ -264,35 +279,42 @@ export class WebRTCService implements OnDestroy {
this.logger.info('User left', {
displayName: message.displayName,
oderId: message.oderId,
serverId: message.serverId,
serverId: message.serverId
});
if (message.oderId) {
this.peerManager.removePeer(message.oderId);
this.peerServerMap.delete(message.oderId);
}
break;
case SIGNALING_TYPE_OFFER:
if (message.fromUserId && message.payload?.sdp) {
// Track inbound peer as belonging to our effective server
const offerEffectiveServer = this.voiceServerId || this.activeServerId;
if (offerEffectiveServer && !this.peerServerMap.has(message.fromUserId)) {
this.peerServerMap.set(message.fromUserId, offerEffectiveServer);
}
this.peerManager.handleOffer(message.fromUserId, message.payload.sdp);
}
break;
case SIGNALING_TYPE_ANSWER:
if (message.fromUserId && message.payload?.sdp) {
this.peerManager.handleAnswer(message.fromUserId, message.payload.sdp);
}
break;
case SIGNALING_TYPE_ICE_CANDIDATE:
if (message.fromUserId && message.payload?.candidate) {
this.peerManager.handleIceCandidate(message.fromUserId, message.payload.candidate);
}
break;
}
}
@@ -307,11 +329,13 @@ export class WebRTCService implements OnDestroy {
*/
private closePeersNotInServer(serverId: string): void {
const peersToClose: string[] = [];
this.peerServerMap.forEach((peerServerId, peerId) => {
if (peerServerId !== serverId) {
peersToClose.push(peerId);
}
});
for (const peerId of peersToClose) {
this.logger.info('Closing peer from different server', { peerId, currentServer: serverId });
this.peerManager.removePeer(peerId);
@@ -326,7 +350,7 @@ export class WebRTCService implements OnDestroy {
isDeafened: this._isDeafened(),
isScreenSharing: this._isScreenSharing(),
roomId: this.mediaManager.getCurrentVoiceRoomId(),
serverId: this.mediaManager.getCurrentVoiceServerId(),
serverId: this.mediaManager.getCurrentVoiceServerId()
};
}
@@ -421,7 +445,7 @@ export class WebRTCService implements OnDestroy {
this.logger.info('Viewed server (already joined)', {
serverId,
userId,
voiceConnected: this._isVoiceConnected(),
voiceConnected: this._isVoiceConnected()
});
} else {
this.memberServerIds.add(serverId);
@@ -429,7 +453,7 @@ export class WebRTCService implements OnDestroy {
this.logger.info('Joined new server via switch', {
serverId,
userId,
voiceConnected: this._isVoiceConnected(),
voiceConnected: this._isVoiceConnected()
});
}
}
@@ -447,9 +471,11 @@ export class WebRTCService implements OnDestroy {
this.memberServerIds.delete(serverId);
this.sendRawMessage({ type: SIGNALING_TYPE_LEAVE_SERVER, serverId });
this.logger.info('Left server', { serverId });
if (this.memberServerIds.size === 0) {
this.fullCleanup();
}
return;
}
@@ -534,6 +560,7 @@ export class WebRTCService implements OnDestroy {
*/
async enableVoice(): Promise<MediaStream> {
const stream = await this.mediaManager.enableVoice();
this.syncMediaSignals();
return stream;
}
@@ -630,6 +657,7 @@ export class WebRTCService implements OnDestroy {
if (serverId) {
this.voiceServerId = serverId;
}
this.mediaManager.startVoiceHeartbeat(roomId, serverId);
}
@@ -644,8 +672,9 @@ export class WebRTCService implements OnDestroy {
* @param includeAudio - Whether to capture and mix system audio.
* @returns The screen-capture {@link MediaStream}.
*/
async startScreenShare(includeAudio: boolean = false): Promise<MediaStream> {
async startScreenShare(includeAudio = false): Promise<MediaStream> {
const stream = await this.screenShareManager.startScreenShare(includeAudio);
this._isScreenSharing.set(true);
this._screenStreamSignal.set(stream);
return stream;
@@ -698,9 +727,12 @@ export class WebRTCService implements OnDestroy {
/** Returns true if a peer connection exists and its data channel is open. */
private isPeerHealthy(peer: import('./webrtc').PeerData | undefined): boolean {
if (!peer) return false;
if (!peer)
return false;
const connState = peer.connection?.connectionState;
const dcState = peer.dataChannel?.readyState;
return connState === 'connected' && dcState === 'open';
}