Skip to content

Commit

Permalink
feat: handle the connections erros and publish the room state for the…
Browse files Browse the repository at this point in the history
… users
  • Loading branch information
carlossantos74 committed Jan 3, 2025
1 parent 8039b8c commit b84dba5
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 10 deletions.
13 changes: 12 additions & 1 deletion apps/playground/src/pages/superviz-room.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createRoom, type Room, ParticipantEvent } from '@superviz/room'
import { createRoom, type Room, ParticipantEvent, RoomEvent } from '@superviz/room'
import { v4 as generateId } from "uuid";

import { useCallback, useEffect, useRef, useState } from "react";
Expand Down Expand Up @@ -33,6 +33,7 @@ export function SuperVizRoom() {
});

room.current = newRoom;
subscribeToEvents();
}, []);

useEffect(() => {
Expand All @@ -51,6 +52,12 @@ export function SuperVizRoom() {
})
});

Object.values(RoomEvent).forEach(event => {
room.current?.subscribe(event, (data) => {
console.log('New event from room, eventName:', event, 'data:', data);
})
});

setSubscribed(true);
}

Expand All @@ -61,6 +68,10 @@ export function SuperVizRoom() {
room.current?.unsubscribe(event);
});

Object.values(RoomEvent).forEach(event => {
room.current?.unsubscribe(event)
});

setSubscribed(false);
}

Expand Down
43 changes: 39 additions & 4 deletions packages/room/src/core/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Subject } from 'rxjs';

import { Logger } from '../common/utils/logger';
import { IOC } from '../services/io';
import { IOCState } from '../services/io/types';

import { ParticipantEvent, RoomParams } from './types';

Expand Down Expand Up @@ -138,12 +139,46 @@ describe('Room', () => {
expect(emitSpy).toHaveBeenCalledWith(ParticipantEvent.MY_PARTICIPANT_UPDATED, data.data);
});

it('should handle connection state change', () => {
const state = { connected: true } as any;
const logSpy = jest.spyOn(room['logger'], 'log');
it('should handle the same account error', () => {
const emitSpy = jest.spyOn(room as any, 'emit');
const leaveSpy = jest.spyOn(room, 'leave');

room['onConnectionStateChange'](IOCState.SAME_ACCOUNT_ERROR);

expect(leaveSpy).toHaveBeenCalled();
expect(emitSpy).toHaveBeenCalledWith(
'room.error',
{
code: 'same_account_error',
message: '[SuperViz] Room initialization failed: the user is already connected to the room. Please verify if the user is connected with the same account and try again.',
},
);
});

it('should handle the authentication error', () => {
const emitSpy = jest.spyOn(room as any, 'emit');
const leaveSpy = jest.spyOn(room, 'leave');

room['onConnectionStateChange'](IOCState.AUTH_ERROR);

expect(emitSpy).toHaveBeenCalledWith(
'room.error',
{
code: 'auth_error',
message: "[SuperViz] Room initialization failed: this website's domain is not whitelisted. If you are the developer, please add your domain in https://dashboard.superviz.com/developer",
},
);

expect(leaveSpy).toHaveBeenCalled();
expect(room['room'].disconnect).toHaveBeenCalled();
});

it('should update the room state', () => {
const state = IOCState.CONNECTED;
const emitSpy = jest.spyOn(room as any, 'emit');

room['onConnectionStateChange'](state);

expect(logSpy).toHaveBeenCalledWith('connection state changed', state);
expect(emitSpy).toHaveBeenCalledWith('room.update', { status: state });
});
});
66 changes: 62 additions & 4 deletions packages/room/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Logger } from '../common/utils/logger';
import { IOC } from '../services/io';
import { IOCState } from '../services/io/types';

import { GeneralEvent, ParticipantEvent, RoomEventPayload, RoomParams, Callback, EventOptions } from './types';
import { GeneralEvent, ParticipantEvent, RoomEventPayload, RoomParams, Callback, EventOptions, RoomEvent } from './types';

export class Room {
private participant: Participant;
Expand Down Expand Up @@ -34,6 +34,12 @@ export class Room {
public leave() {
this.unsubscribeFromRoomEvents();

this.emit(ParticipantEvent.PARTICIPANT_LEFT, this.participant);
this.emit(ParticipantEvent.MY_PARTICIPANT_LEFT, this.participant);

this.room.disconnect();
this.io.destroy();

this.subscriptions.forEach((subscription) => {
subscription.unsubscribe();
});
Expand All @@ -44,9 +50,6 @@ export class Room {

this.subscriptions.clear();
this.observers.clear();

this.room.disconnect();
this.io.destroy();
}

/**
Expand Down Expand Up @@ -270,12 +273,67 @@ export class Room {
this.emit(ParticipantEvent.MY_PARTICIPANT_UPDATED, data.data);
};

/**
* Handles authentication errors during room initialization.
*
* This method logs an error message indicating that the website's domain is not whitelisted.
* It emits an `ERROR` event with the error code 'auth_error' and the error message.
* Finally, it calls the `leave` method to exit the room.
*
* @fires RoomEvent.ERROR - Emitted when an authentication error occurs.
*/
private onAuthError = () => {
const message = "[SuperViz] Room initialization failed: this website's domain is not whitelisted. If you are the developer, please add your domain in https://dashboard.superviz.com/developer";

this.logger.log(message);
console.error(message);

this.emit(RoomEvent.ERROR, { code: 'auth_error', message });
this.leave();
};

/**
* Handles the error when a user tries to connect to a room with the same account that is already
* connected.
* Logs the error message, emits an error event, and leaves the room.
*
* @fires RoomEvent.ERROR - Emitted when a user tries to connect to a room with the same account.
*/
private onSameAccountError = () => {
const message = '[SuperViz] Room initialization failed: the user is already connected to the room. Please verify if the user is connected with the same account and try again.';

this.logger.log(message);
console.error(message);

this.emit(RoomEvent.ERROR, { code: 'same_account_error', message });
this.leave();
};

/**
* @description Handles changes in the connection state.
*
* @param {IOCState} state - The current state of the connection.
*/
private onConnectionStateChange = (state: IOCState): void => {
this.logger.log('connection state changed', state);

const common = () => {
this.emit(RoomEvent.UPDATE, { status: state });
};

const map = {
[IOCState.CONNECTING]: () => common(),
[IOCState.CONNECTION_ERROR]: () => common(),
[IOCState.CONNECTED]: () => common(),
[IOCState.DISCONNECTED]: () => common(),
[IOCState.RECONNECTING]: () => common(),
[IOCState.RECONNECT_ERROR]: () => common(),

// error
[IOCState.AUTH_ERROR]: () => this.onAuthError(),
[IOCState.SAME_ACCOUNT_ERROR]: () => this.onSameAccountError(),
};

map[state]();
};
}
9 changes: 8 additions & 1 deletion packages/room/src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { InitialParticipant, Participant } from '../common/types/participant.types';
import { IOCState } from '../services/io/types';

export interface RoomParams {
participant: InitialParticipant;
Expand All @@ -9,6 +10,10 @@ type RoomError = {
message: string
}

type RoomUpdate = {
status: IOCState,
}

export enum ParticipantEvent {
MY_PARTICIPANT_JOINED = 'my-participant.joined',
MY_PARTICIPANT_LEFT = 'my-participant.left',
Expand All @@ -19,7 +24,8 @@ export enum ParticipantEvent {
}

export enum RoomEvent {
ERROR = 'room.error'
ERROR = 'room.error',
UPDATE = 'room.update',
}

export interface RoomEventPayloads {
Expand All @@ -30,6 +36,7 @@ export interface RoomEventPayloads {
[ParticipantEvent.PARTICIPANT_LEFT]: Participant;
[ParticipantEvent.PARTICIPANT_UPDATED]: Participant;
[RoomEvent.ERROR]: RoomError;
[RoomEvent.UPDATE]: RoomUpdate;
}

export type EventOptions<T extends ParticipantEvent | RoomEvent> = T | `${T}`
Expand Down

0 comments on commit b84dba5

Please sign in to comment.