mirror of https://github.com/OpenVidu/openvidu.git
ov-components: enhance participant disconnection handling with reasons and refactor disconnect logic
parent
11137a2a8f
commit
b92630ecd8
|
@ -46,7 +46,7 @@ import {
|
||||||
RoomEvent,
|
RoomEvent,
|
||||||
Track
|
Track
|
||||||
} from 'livekit-client';
|
} from 'livekit-client';
|
||||||
import { ParticipantLeftEvent, ParticipantModel } from '../../models/participant.model';
|
import { ParticipantLeftEvent, ParticipantLeftReason, ParticipantModel } from '../../models/participant.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
@ -79,7 +79,8 @@ export class SessionComponent implements OnInit, OnDestroy {
|
||||||
@Output() onRoomReconnected: EventEmitter<void> = new EventEmitter<void>();
|
@Output() onRoomReconnected: EventEmitter<void> = new EventEmitter<void>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides event notifications that fire when Room is disconnected for the local participant.
|
* Provides event notifications that fire when participant is disconnected from Room.
|
||||||
|
* @deprecated Use onParticipantLeft instead
|
||||||
*/
|
*/
|
||||||
@Output() onRoomDisconnected: EventEmitter<void> = new EventEmitter<void>();
|
@Output() onRoomDisconnected: EventEmitter<void> = new EventEmitter<void>();
|
||||||
|
|
||||||
|
@ -129,7 +130,7 @@ export class SessionComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@HostListener('window:beforeunload')
|
@HostListener('window:beforeunload')
|
||||||
beforeunloadHandler() {
|
beforeunloadHandler() {
|
||||||
this.disconnectRoom();
|
this.disconnectRoom(ParticipantLeftReason.BROWSER_UNLOAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('window:resize')
|
@HostListener('window:resize')
|
||||||
|
@ -185,6 +186,7 @@ export class SessionComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
this.shouldDisconnectRoomWhenComponentIsDestroyed = true;
|
||||||
this.room = this.openviduService.getRoom();
|
this.room = this.openviduService.getRoom();
|
||||||
|
|
||||||
// this.subscribeToCaptionLanguage();
|
// this.subscribeToCaptionLanguage();
|
||||||
|
@ -208,13 +210,6 @@ export class SessionComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await this.participantService.connect();
|
await this.participantService.connect();
|
||||||
this.openviduService.setDisconnectCallback(() => {
|
|
||||||
const event: ParticipantLeftEvent = {
|
|
||||||
roomName: this.openviduService.getRoomName(),
|
|
||||||
participantName: this.participantService.getLocalParticipant()?.identity || ''
|
|
||||||
};
|
|
||||||
this.onParticipantLeft.emit(event);
|
|
||||||
});
|
|
||||||
// Send room created after participant connect for avoiding to send incomplete room payload
|
// Send room created after participant connect for avoiding to send incomplete room payload
|
||||||
this.onRoomCreated.emit(this.room);
|
this.onRoomCreated.emit(this.room);
|
||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
|
@ -233,7 +228,7 @@ export class SessionComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
async ngOnDestroy() {
|
async ngOnDestroy() {
|
||||||
if (this.shouldDisconnectRoomWhenComponentIsDestroyed) {
|
if (this.shouldDisconnectRoomWhenComponentIsDestroyed) {
|
||||||
await this.disconnectRoom();
|
await this.disconnectRoom(ParticipantLeftReason.LEAVE);
|
||||||
}
|
}
|
||||||
if (this.room) this.room.removeAllListeners();
|
if (this.room) this.room.removeAllListeners();
|
||||||
this.participantService.clear();
|
this.participantService.clear();
|
||||||
|
@ -243,10 +238,16 @@ export class SessionComponent implements OnInit, OnDestroy {
|
||||||
// if (this.captionLanguageSubscription) this.captionLanguageSubscription.unsubscribe();
|
// if (this.captionLanguageSubscription) this.captionLanguageSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
async disconnectRoom() {
|
async disconnectRoom(reason: ParticipantLeftReason) {
|
||||||
// Mark session as disconnected for avoiding to do it again in ngOnDestroy
|
// Mark session as disconnected for avoiding to do it again in ngOnDestroy
|
||||||
this.shouldDisconnectRoomWhenComponentIsDestroyed = false;
|
this.shouldDisconnectRoomWhenComponentIsDestroyed = false;
|
||||||
await this.openviduService.disconnectRoom();
|
await this.openviduService.disconnectRoom(() => {
|
||||||
|
this.onParticipantLeft.emit({
|
||||||
|
roomName: this.openviduService.getRoomName(),
|
||||||
|
participantName: this.participantService.getLocalParticipant()?.identity || '',
|
||||||
|
reason
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private subscribeToTogglingMenu() {
|
private subscribeToTogglingMenu() {
|
||||||
|
@ -483,14 +484,46 @@ export class SessionComponent implements OnInit, OnDestroy {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.room.on(RoomEvent.Disconnected, async (reason: DisconnectReason | undefined) => {
|
this.room.on(RoomEvent.Disconnected, async (reason: DisconnectReason | undefined) => {
|
||||||
if (reason === DisconnectReason.SERVER_SHUTDOWN) {
|
const participantLeftEvent: ParticipantLeftEvent = {
|
||||||
this.log.e('Room Disconnected', reason);
|
roomName: this.openviduService.getRoomName(),
|
||||||
this.actionService.openConnectionDialog(
|
participantName: this.participantService.getLocalParticipant()?.identity || '',
|
||||||
this.translateService.translate('ERRORS.CONNECTION'),
|
reason: ParticipantLeftReason.NETWORK_DISCONNECT
|
||||||
this.translateService.translate('ERRORS.RECONNECT')
|
};
|
||||||
);
|
const messageErrorKey = 'ERRORS.DISCONNECT';
|
||||||
this.onRoomDisconnected.emit();
|
let descriptionErrorKey = 'ERRORS.NETWORK_DISCONNECT';
|
||||||
|
|
||||||
|
switch (reason) {
|
||||||
|
case DisconnectReason.CLIENT_INITIATED:
|
||||||
|
// Skip disconnect reason if the user has left the room
|
||||||
|
return;
|
||||||
|
case DisconnectReason.DUPLICATE_IDENTITY:
|
||||||
|
participantLeftEvent.reason = ParticipantLeftReason.DUPLICATE_IDENTITY;
|
||||||
|
descriptionErrorKey = 'ERRORS.DUPLICATE_IDENTITY';
|
||||||
|
break;
|
||||||
|
case DisconnectReason.SERVER_SHUTDOWN:
|
||||||
|
descriptionErrorKey = 'ERRORS.SERVER_SHUTDOWN';
|
||||||
|
participantLeftEvent.reason = ParticipantLeftReason.SERVER_SHUTDOWN;
|
||||||
|
break;
|
||||||
|
case DisconnectReason.PARTICIPANT_REMOVED:
|
||||||
|
participantLeftEvent.reason = ParticipantLeftReason.PARTICIPANT_REMOVED;
|
||||||
|
descriptionErrorKey = 'ERRORS.PARTICIPANT_REMOVED';
|
||||||
|
break;
|
||||||
|
case DisconnectReason.ROOM_DELETED:
|
||||||
|
participantLeftEvent.reason = ParticipantLeftReason.ROOM_DELETED;
|
||||||
|
descriptionErrorKey = 'ERRORS.ROOM_DELETED';
|
||||||
|
break;
|
||||||
|
case DisconnectReason.SIGNAL_CLOSE:
|
||||||
|
participantLeftEvent.reason = ParticipantLeftReason.SIGNAL_CLOSE;
|
||||||
|
descriptionErrorKey = 'ERRORS.SIGNAL_CLOSE';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
this.log.e('Room Disconnected', participantLeftEvent.reason);
|
||||||
|
this.onParticipantLeft.emit(participantLeftEvent);
|
||||||
|
this.onRoomDisconnected.emit();
|
||||||
|
this.actionService.openDialog(
|
||||||
|
this.translateService.translate(messageErrorKey),
|
||||||
|
this.translateService.translate(descriptionErrorKey)
|
||||||
|
);
|
||||||
// await this.disconnectRoom();
|
// await this.disconnectRoom();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ import { RecordingService } from '../../services/recording/recording.service';
|
||||||
import { StorageService } from '../../services/storage/storage.service';
|
import { StorageService } from '../../services/storage/storage.service';
|
||||||
import { TranslateService } from '../../services/translate/translate.service';
|
import { TranslateService } from '../../services/translate/translate.service';
|
||||||
import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service';
|
import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service';
|
||||||
import { ParticipantLeftEvent, ParticipantModel } from '../../models/participant.model';
|
import { ParticipantLeftEvent, ParticipantLeftReason, ParticipantModel } from '../../models/participant.model';
|
||||||
import { Room, RoomEvent } from 'livekit-client';
|
import { Room, RoomEvent } from 'livekit-client';
|
||||||
import { ToolbarAdditionalButtonsPosition } from '../../models/toolbar.model';
|
import { ToolbarAdditionalButtonsPosition } from '../../models/toolbar.model';
|
||||||
|
|
||||||
|
@ -512,16 +512,18 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The participant leaves the room voluntarily.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
async disconnect() {
|
async disconnect() {
|
||||||
const event: ParticipantLeftEvent = {
|
|
||||||
roomName: this.openviduService.getRoomName(),
|
|
||||||
participantName: this.participantService.getLocalParticipant()?.identity || ''
|
|
||||||
};
|
|
||||||
try {
|
try {
|
||||||
await this.openviduService.disconnectRoom();
|
await this.openviduService.disconnectRoom(() =>
|
||||||
this.onParticipantLeft.emit(event);
|
this.onParticipantLeft.emit({
|
||||||
|
roomName: this.openviduService.getRoomName(),
|
||||||
|
participantName: this.participantService.getLocalParticipant()?.identity || '',
|
||||||
|
reason: ParticipantLeftReason.LEAVE
|
||||||
|
})
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.log.e('There was an error disconnecting:', error.code, error.message);
|
this.log.e('There was an error disconnecting:', error.code, error.message);
|
||||||
this.actionService.openDialog(this.translateService.translate('ERRORS.DISCONNECT'), error?.error || error?.message || error);
|
this.actionService.openDialog(this.translateService.translate('ERRORS.DISCONNECT'), error?.error || error?.message || error);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { DeviceType } from './device.model';
|
import { DeviceType } from './device.model';
|
||||||
import {
|
import {
|
||||||
AudioCaptureOptions,
|
AudioCaptureOptions,
|
||||||
DataPacket_Kind,
|
|
||||||
DataPublishOptions,
|
DataPublishOptions,
|
||||||
LocalParticipant,
|
LocalParticipant,
|
||||||
LocalTrack,
|
LocalTrack,
|
||||||
|
@ -19,8 +18,26 @@ import {
|
||||||
export interface ParticipantLeftEvent {
|
export interface ParticipantLeftEvent {
|
||||||
roomName: string;
|
roomName: string;
|
||||||
participantName: string;
|
participantName: string;
|
||||||
|
reason: ParticipantLeftReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ParticipantLeftReason {
|
||||||
|
// User-initiated disconnections
|
||||||
|
LEAVE = 'LEAVE', // The participant left the room voluntarily
|
||||||
|
BROWSER_UNLOAD = 'browser_unload', // The participant was disconnected due to a browser unload event
|
||||||
|
|
||||||
|
// Network-related disconnections
|
||||||
|
NETWORK_DISCONNECT = 'network_disconnect', // The participant was disconnected due to a network error
|
||||||
|
SIGNAL_CLOSE = 'websocket_closed', // The participant was disconnected due to a websocket error
|
||||||
|
|
||||||
|
// Server-initiated disconnections
|
||||||
|
SERVER_SHUTDOWN = 'server_shutdown', // The server was shut down
|
||||||
|
PARTICIPANT_REMOVED = 'participant_removed', // The participant was removed from the room
|
||||||
|
ROOM_DELETED = 'room_deleted', // The room was deleted
|
||||||
|
|
||||||
|
// Permission/policy-based disconnections
|
||||||
|
DUPLICATE_IDENTITY = 'duplicate_identity' // The participant was disconnected due to a duplicate identity
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Interface that defines the properties of the participant track publication.
|
* Interface that defines the properties of the participant track publication.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -38,7 +38,6 @@ export class OpenViduService {
|
||||||
private localTracks: LocalTrack[] = [];
|
private localTracks: LocalTrack[] = [];
|
||||||
private livekitToken = '';
|
private livekitToken = '';
|
||||||
private livekitUrl = '';
|
private livekitUrl = '';
|
||||||
private disconnectCallback: () => void;
|
|
||||||
private log: ILogger;
|
private log: ILogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,32 +101,21 @@ export class OpenViduService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects local participant from the room
|
* Disconnects from the current room.
|
||||||
|
*
|
||||||
|
* This method will check if there's an active connection to a room before attempting to disconnect.
|
||||||
|
* If the room is connected, it will perform the disconnection and call the optional callback function.
|
||||||
|
*
|
||||||
|
* @param callback - Optional function to be executed after a successful disconnection
|
||||||
|
* @returns A Promise that resolves once the disconnection is complete
|
||||||
*/
|
*/
|
||||||
async disconnectRoom(): Promise<void> {
|
async disconnectRoom(callback?: () => void): Promise<void> {
|
||||||
if (this.isRoomConnected()) {
|
if (this.isRoomConnected()) {
|
||||||
this.log.d('Disconnecting room');
|
this.log.d('Disconnecting from room');
|
||||||
await this.room.disconnect();
|
await this.room.disconnect();
|
||||||
if (this.disconnectCallback) {
|
if (callback) callback();
|
||||||
this.disconnectCallback();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a callback function that triggers when a participant is disconnected
|
|
||||||
* from the session using the OpenViduService.disconnectRoom() method.
|
|
||||||
*
|
|
||||||
* This is particularly useful in cases where the disconnection occurs externally,
|
|
||||||
* outside of this component, ensuring that the parent component is notified
|
|
||||||
* even when the service is used directly.
|
|
||||||
*
|
|
||||||
* @param callback - The function to be executed upon disconnection.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
setDisconnectCallback(callback: () => void): void {
|
|
||||||
this.disconnectCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns Room instance
|
* @returns Room instance
|
||||||
|
|
Loading…
Reference in New Issue