Add eslint
This commit is contained in:
@@ -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';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user