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

Commit

Permalink
feat: only assign slot to user when it's necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
carlossantos74 committed Jul 9, 2024
1 parent dd1eb8c commit 31b4079
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 27 deletions.
5 changes: 3 additions & 2 deletions src/components/presence-mouse/canvas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Logger } from '../../../common/utils';
import { BaseComponent } from '../../base';
import { ComponentNames } from '../../types';
import { Camera, ParticipantMouse, PresenceMouseProps, Transform } from '../types';
import { MEETING_COLORS } from '../../../common/types/meeting-colors.types';

export class PointersCanvas extends BaseComponent {
public name: ComponentNames;
Expand Down Expand Up @@ -334,8 +335,8 @@ export class PointersCanvas extends BaseComponent {
}

if (mouseUser) {
mouseUser.style.color = mouse.slot.textColor;
mouseUser.style.backgroundColor = mouse.slot.color;
mouseUser.style.color = mouse.slot?.textColor ?? '#fff';
mouseUser.style.backgroundColor = mouse.slot?.color ?? MEETING_COLORS.gray;
mouseUser.innerHTML = mouse.name;
}

Expand Down
5 changes: 3 additions & 2 deletions src/components/presence-mouse/html/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
Transform,
VoidElements,
} from '../types';
import { MEETING_COLORS } from '../../../common/types/meeting-colors.types';

export class PointersHTML extends BaseComponent {
public name: ComponentNames;
Expand Down Expand Up @@ -695,8 +696,8 @@ export class PointersHTML extends BaseComponent {
}

if (mouseUser) {
mouseUser.style.color = participant.slot.textColor;
mouseUser.style.backgroundColor = participant.slot.color;
mouseUser.style.color = participant.slot?.textColor ?? MEETING_COLORS.gray;
mouseUser.style.backgroundColor = participant.slot?.color ?? '#fff';
mouseUser.innerHTML = participant.name;
}

Expand Down
20 changes: 14 additions & 6 deletions src/core/launcher/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as Socket from '@superviz/socket-client';
import { isEqual } from 'lodash';

import { ParticipantEvent } from '../../common/types/events.types';
import { Participant } from '../../common/types/participant.types';
import { Participant, ParticipantType } from '../../common/types/participant.types';
import { StoreType } from '../../common/types/stores.types';
import { Observable } from '../../common/utils';
import { Logger } from '../../common/utils/logger';
Expand Down Expand Up @@ -33,6 +33,7 @@ export class Launcher extends Observable implements DefaultLauncher {
private ioc: IOC;
private room: Socket.Room;
private eventBus: EventBus = new EventBus();
private slotService: SlotService;

private useStore = useStore.bind(this) as typeof useStore;

Expand All @@ -47,9 +48,7 @@ export class Launcher extends Observable implements DefaultLauncher {
localParticipant.publish({ ...participant });
participants.subscribe(this.onParticipantListUpdate);
isDomainWhitelisted.subscribe(this.onAuthentication);
localParticipant.subscribe((participant) => {
this.participant = participant;
});
localParticipant.subscribe(this.onParticipantLocalParticipantUpdateOnStore);

group.publish(participantGroup);
this.ioc = new IOC(localParticipant.value);
Expand Down Expand Up @@ -243,6 +242,16 @@ export class Launcher extends Observable implements DefaultLauncher {
);
};

/**
* @function onParticipantLocalParticipantUpdateOnStore
* @description on participant local participant update on store
* @param {Participant} participant - new participant data
* @returns {void}
*/
private onParticipantLocalParticipantUpdateOnStore = (participant: Participant): void => {
this.participant = participant;
};

/**
* @function onParticipantListUpdate
* @description on participant list update
Expand Down Expand Up @@ -357,8 +366,7 @@ export class Launcher extends Observable implements DefaultLauncher {
if (presence.id !== this.participant.id) return;

// Assign a slot to the participant
const slot = new SlotService(this.room);
await slot.assignSlot();
this.slotService = new SlotService(this.room, this.useStore);

this.room.presence.update(this.participant);

Expand Down
79 changes: 71 additions & 8 deletions src/services/slot/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { SlotService } from '.';
import { MOCK_LOCAL_PARTICIPANT } from '../../../__mocks__/participants.mock';
import { MEETING_COLORS, MEETING_COLORS_KEYS } from '../../common/types/meeting-colors.types';
import { Participant } from '../../common/types/participant.types';
import { StoreType } from '../../common/types/stores.types';
import { useStore } from '../../common/utils/use-store';
import { ComponentNames } from '../../components/types';

describe('slot service', () => {
afterEach(() => {
Expand All @@ -16,11 +22,7 @@ describe('slot service', () => {
},
} as any;

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

const instance = new SlotService(room);
const instance = new SlotService(room, useStore);
const result = await instance.assignSlot();

expect(instance['slotIndex']).toBeDefined();
Expand All @@ -33,6 +35,30 @@ describe('slot service', () => {
});
});

test('should remove the slot from the participant', async () => {
const room = {
presence: {
on: jest.fn(),
update: jest.fn(),
},
} as any;

const instance = new SlotService(room, useStore);
instance['slotIndex'] = 0;
instance.setDefaultSlot();

expect(instance['slotIndex']).toBeNull();
expect(room.presence.update).toHaveBeenCalledWith({
slot: {
index: null,
color: expect.any(String),
textColor: expect.any(String),
colorName: expect.any(String),
timestamp: expect.any(Number),
},
});
});

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

Expand All @@ -46,10 +72,10 @@ describe('slot service', () => {
},
} as any;

const instance = new SlotService(room);
const instance = new SlotService(room, useStore);
await instance.assignSlot();

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

test('if the slot is already in use, it should assign a new slot', async () => {
Expand Down Expand Up @@ -79,7 +105,7 @@ describe('slot service', () => {
id: '123',
} as any;

const instance = new SlotService(room);
const instance = new SlotService(room, useStore);
const result = await instance.assignSlot();

expect(instance['slotIndex']).toBeDefined();
Expand All @@ -91,4 +117,41 @@ describe('slot service', () => {
timestamp: expect.any(Number),
});
});

test("should remove the slot from the participant when the participant don't need it anymore", async () => {
const room = {
presence: {
on: jest.fn(),
update: jest.fn(),
},
} as any;

const instance = new SlotService(room, useStore);
instance['slotIndex'] = 0;

const event: Participant = {
...MOCK_LOCAL_PARTICIPANT,
slot: {
index: 0,
color: MEETING_COLORS.turquoise,
colorName: MEETING_COLORS_KEYS[0],
textColor: '#000',
timestamp: 0,
},
activeComponents: [],
};

const { localParticipant } = useStore(StoreType.GLOBAL);
localParticipant.publish(event);

expect(room.presence.update).toHaveBeenCalledWith({
slot: {
index: null,
color: expect.any(String),
textColor: expect.any(String),
colorName: expect.any(String),
timestamp: expect.any(Number),
},
});
});
});
107 changes: 98 additions & 9 deletions src/services/slot/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
import * as Socket from '@superviz/socket-client';

import { NAME_IS_WHITE_TEXT, MEETING_COLORS } from '../../common/types/meeting-colors.types';
import { Participant, Slot } from '../../common/types/participant.types';
import { StoreType } from '../../common/types/stores.types';
import {
NAME_IS_WHITE_TEXT,
MEETING_COLORS,
MEETING_COLORS_KEYS,
} from '../../common/types/meeting-colors.types';
import { Participant, ParticipantType, Slot } from '../../common/types/participant.types';
import { Store, StoreType } from '../../common/types/stores.types';
import { useStore } from '../../common/utils/use-store';
import { ComponentNames } from '../../components/types';

export class SlotService {
private room: Socket.Room;
private slotIndex: number | null = null;

private slotIndex: number;

constructor(room: Socket.Room) {
constructor(
private room: Socket.Room,
private useStore: <T extends StoreType>(name: T) => Store<T>,
) {
this.room = room;

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

const { localParticipant } = this.useStore(StoreType.GLOBAL);
localParticipant.subscribe(this.onParticipantLocalParticipantUpdateOnStore);

/**
* When the participant enters the room, is setted the default slot
*/
this.setDefaultSlot();
}

/**
Expand Down Expand Up @@ -48,6 +62,7 @@ export class SlotService {
});

const isUsing = !slots.includes(slot);

if (isUsing) {
slot = slots.shift();
}
Expand Down Expand Up @@ -86,10 +101,49 @@ export class SlotService {
}
}

private onPresenceUpdate = async (event: Socket.PresenceEvent<Participant>) => {
/**
* @function setDefaultSlot
* @description Removes the slot from the participant
* @returns void
*/
public setDefaultSlot() {
const { localParticipant, participants } = useStore(StoreType.GLOBAL);

if (!event.data.slot || !localParticipant.value?.slot) return;
const slot: Slot = {
index: null,
color: MEETING_COLORS.gray,
textColor: '#fff',
colorName: 'gray',
timestamp: Date.now(),
};

this.slotIndex = slot.index;

localParticipant.publish({
...localParticipant.value,
slot: slot,
});

participants.publish({
...participants.value,
[localParticipant.value.id]: {
...participants.value[localParticipant.value.id],
slot,
},
});

this.room.presence.update({ slot });
}

private subscribeToStoreEvents() {
const { localParticipant } = useStore(StoreType.GLOBAL);
localParticipant.subscribe(this.onParticipantLocalParticipantUpdateOnStore);
}

private onPresenceUpdate = async (event: Socket.PresenceEvent<Participant>) => {
const { localParticipant } = this.useStore(StoreType.GLOBAL);

if (!event.data.slot || !localParticipant.value?.slot?.index) return;

if (event.id === localParticipant.value.id) {
localParticipant.publish({
Expand All @@ -114,4 +168,39 @@ export class SlotService {
);
}
};

/**
* @function onParticipantLocalParticipantUpdateOnStore
* @description on participant local participant update on store
* @param {Participant} participant - new participant data
* @returns {void}
*/
private onParticipantLocalParticipantUpdateOnStore = (participant: Participant): void => {
const COMPONENTS_THAT_NEED_SLOT = [
ComponentNames.FORM_ELEMENTS,
ComponentNames.WHO_IS_ONLINE,
ComponentNames.PRESENCE,
ComponentNames.PRESENCE_AUTODESK,
ComponentNames.PRESENCE_MATTERPORT,
ComponentNames.PRESENCE_THREEJS,
];

const componentsNeedSlot = COMPONENTS_THAT_NEED_SLOT.some((component) => {
return participant.activeComponents.includes(component);
});

const videoNeedSlot =
participant.activeComponents.includes(ComponentNames.VIDEO_CONFERENCE) &&
participant.type !== ParticipantType.AUDIENCE;

const needSlot = componentsNeedSlot || videoNeedSlot;

if ((participant.slot?.index === null || !participant.slot) && needSlot) {
this.assignSlot();
}

if (participant.slot?.index !== null && !needSlot) {
this.setDefaultSlot();
}
};
}

0 comments on commit 31b4079

Please sign in to comment.