Skip to content
This repository was archived by the owner on Oct 18, 2024. It is now read-only.

Commit

Permalink
Merge pull request #619 from SuperViz/fix/participant-joined-payload
Browse files Browse the repository at this point in the history
fix: wait to update participant data to emit participant joined
  • Loading branch information
Raspincel authored Mar 26, 2024
2 parents f9bf6d2 + c428b8a commit c0e26eb
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 126 deletions.
2 changes: 1 addition & 1 deletion src/core/launcher/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ describe('Launcher', () => {
const callback = jest.fn();
LauncherInstance.subscribe(ParticipantEvent.JOINED, callback);

LauncherInstance['onParticipantJoinedIOC']({
LauncherInstance['onParticipantUpdatedIOC']({
connectionId: 'connection1',
data: MOCK_LOCAL_PARTICIPANT,
id: MOCK_LOCAL_PARTICIPANT.id,
Expand Down
37 changes: 19 additions & 18 deletions src/core/launcher/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,28 +352,25 @@ export class Launcher extends Observable implements DefaultLauncher {
* @param presence - participant presence
* @returns {void}
*/
private onParticipantJoinedIOC = (presence: Socket.PresenceEvent<Participant>): void => {
if (presence.id === this.participant.value.id) {
// Assign a slot to the participant
SlotService.register(this.LauncherRealtimeRoom, this.participant.value);
this.LauncherRealtimeRoom.presence.update<Participant>(this.participant.value);
}
private onParticipantJoinedIOC = async (
presence: Socket.PresenceEvent<Participant>,
): Promise<void> => {
if (presence.id !== this.participant.value.id) return;

// When the participant joins, it is without any data, it's updated later
this.participants.value.set(presence.id, {
id: presence.id,
name: presence.name,
...presence.data,
});
// Assign a slot to the participant
const slot = new SlotService(this.LauncherRealtimeRoom, this.participant.value);
const slotData = await slot.assignSlot();

if (presence.id === this.participant.value.id) {
this.logger.log('launcher service @ onParticipantJoined - local participant joined');
this.publish(ParticipantEvent.LOCAL_JOINED, this.participant.value);
}
this.participant.value = {
...this.participant.value,
slot: slotData,
};

this.LauncherRealtimeRoom.presence.update<Participant>(this.participant.value);

this.logger.log('launcher service @ onParticipantJoined - participant joined', presence.data);
this.logger.log('launcher service @ onParticipantJoined - local participant joined');

this.publish(ParticipantEvent.JOINED, this.participants.value.get(presence.id));
this.publish(ParticipantEvent.LOCAL_JOINED, this.participant.value);
};

/**
Expand Down Expand Up @@ -429,6 +426,10 @@ export class Launcher extends Observable implements DefaultLauncher {
}
}

if (!this.participants.value.has(presence.id)) {
this.publish(ParticipantEvent.JOINED, presence.data);
}

this.participants.value.set(presence.id, presence.data);
const participantList = Array.from(this.participants.value.values());

Expand Down
71 changes: 20 additions & 51 deletions src/services/slot/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,11 @@
import { SlotService } from '.';

describe('slot service', () => {
it('should assign a slot to the participant', async () => {
const room = {
presence: {
on: jest.fn(),
get: jest.fn((callback) => {
callback([]);
}),
update: jest.fn(),
},
} as any;

const participant = {
id: '123',
} as any;

const instance = new SlotService(room, participant);
await instance['assignSlot']();

expect(instance['slotIndex']).toBeDefined();
expect(instance['participant'].slot).toBeDefined();
expect(room.presence.update).toHaveBeenCalledWith({
slot: expect.objectContaining({
index: expect.any(Number),
color: expect.any(String),
textColor: expect.any(String),
colorName: expect.any(String),
timestamp: expect.any(Number),
}),
});
afterEach(() => {
jest.resetAllMocks();
});

it('should assign a slot to the participant', async () => {
test('should assign a slot to the participant', async () => {
const room = {
presence: {
on: jest.fn(),
Expand All @@ -48,22 +21,20 @@ describe('slot service', () => {
} as any;

const instance = new SlotService(room, participant);
await instance['assignSlot']();
const result = await instance.assignSlot();

expect(instance['slotIndex']).toBeDefined();
expect(instance['participant'].slot).toBeDefined();
expect(room.presence.update).toHaveBeenCalledWith({
slot: expect.objectContaining({
index: expect.any(Number),
color: expect.any(String),
textColor: expect.any(String),
colorName: expect.any(String),
timestamp: expect.any(Number),
}),
expect(result).toEqual({
index: expect.any(Number),
color: expect.any(String),
textColor: expect.any(String),
colorName: expect.any(String),
timestamp: expect.any(Number),
});
});

it('if there are no more slots available, it should throw an error', async () => {
test('if there are no more slots available, it should throw an error', async () => {
console.error = jest.fn();

const room = {
Expand All @@ -81,13 +52,13 @@ describe('slot service', () => {
} as any;

const instance = new SlotService(room, participant);
await instance['assignSlot']();
const result = await instance.assignSlot();

expect(instance['slotIndex']).toBeUndefined();
expect(instance['participant'].slot).toBeUndefined();
});

it('if the slot is already in use, it should assign a new slot', async () => {
test('if the slot is already in use, it should assign a new slot', async () => {
const room = {
presence: {
on: jest.fn(),
Expand Down Expand Up @@ -115,18 +86,16 @@ describe('slot service', () => {
} as any;

const instance = new SlotService(room, participant);
await instance['assignSlot']();
const result = await instance.assignSlot();

expect(instance['slotIndex']).toBeDefined();
expect(instance['participant'].slot).toBeDefined();
expect(room.presence.update).toHaveBeenCalledWith({
slot: expect.objectContaining({
index: expect.any(Number),
color: expect.any(String),
textColor: expect.any(String),
colorName: expect.any(String),
timestamp: expect.any(Number),
}),
expect(result).toEqual({
index: expect.any(Number),
color: expect.any(String),
textColor: expect.any(String),
colorName: expect.any(String),
timestamp: expect.any(Number),
});
});
});
100 changes: 44 additions & 56 deletions src/services/slot/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
MeetingColors,
MeetingColorsHex,
} from '../../common/types/meeting-colors.types';
import { Participant } from '../../common/types/participant.types';
import { Participant, Slot } from '../../common/types/participant.types';
import { AblyRealtimeService } from '../realtime';

export class SlotService {
Expand All @@ -19,77 +19,64 @@ export class SlotService {
this.room = room;
this.participant = participant;

this.assignSlot();
this.room.presence.on(Socket.PresenceEvents.UPDATE, this.onPresenceUpdate);
}

public static register(room: Socket.Room, participant: Participant) {
if (!SlotService.instance) {
SlotService.instance = new SlotService(room, participant);
}

return SlotService.instance;
}

/**
* @function assignSlot
* @description Assigns a slot to the participant
* @returns void
*/
private async assignSlot() {
const slot = Math.floor(Math.random() * 16);
public async assignSlot(): Promise<Slot> {
try {
const slot = Math.floor(Math.random() * 16);

new Promise((resolve, reject) => {
this.room.presence.get((presences) => {
if (!presences || !presences.length) resolve(false);
const isUsing = await new Promise((resolve, reject) => {
this.room.presence.get((presences) => {
if (!presences || !presences.length) resolve(false);

if (presences.length >= 16) {
reject(new Error('[SuperViz] - No more slots available'));
return;
}
if (presences.length >= 16) {
reject(new Error('[SuperViz] - No more slots available'));
return;
}

presences.forEach((presence: Socket.PresenceEvent<Participant>) => {
if (presence.id === this.participant.id) return;
presences.forEach((presence: Socket.PresenceEvent<Participant>) => {
if (presence.id === this.participant.id) return;

if (presence.data?.slot?.index === slot) resolve(true);
});
if (presence.data?.slot?.index === slot) resolve(true);
});

resolve(false);
});
})
.then(async (isUsing) => {
if (isUsing) {
this.assignSlot();
return;
}

const slotData = {
index: slot,
color: MeetingColorsHex[slot],
textColor: INDEX_IS_WHITE_TEXT.includes(slot) ? '#fff' : '#000',
colorName: MeetingColors[slot],
timestamp: Date.now(),
};

this.slotIndex = slot;
this.participant = {
...this.participant,
slot: slotData,
};

this.room.presence.update({
slot: slotData,
});
})
.catch((error) => {
this.room.presence.update({
slot: null,
resolve(false);
});
console.error(error);
});

if (isUsing) {
const slotData = await this.assignSlot();
return slotData;
}

const slotData = {
index: slot,
color: MeetingColorsHex[slot],
textColor: INDEX_IS_WHITE_TEXT.includes(slot) ? '#fff' : '#000',
colorName: MeetingColors[slot],
timestamp: Date.now(),
};

this.slotIndex = slot;
this.participant = {
...this.participant,
slot: slotData,
};

return slotData;
} catch (error) {
console.error(error);
return null;
}
}

private onPresenceUpdate = (event: Socket.PresenceEvent<Participant>) => {
private onPresenceUpdate = async (event: Socket.PresenceEvent<Participant>) => {
if (!event.data.slot || !this.participant?.slot) return;

if (event.id === this.participant.id) {
Expand All @@ -102,7 +89,8 @@ export class SlotService {

// if someone else has the same slot as me, and they were assigned first, I should reassign
if (event.data.slot?.index === this.slotIndex && slotOccupied) {
this.assignSlot();
const slotData = await this.assignSlot();
this.room.presence.update({ slot: slotData });
}
};
}

0 comments on commit c0e26eb

Please sign in to comment.