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

fix: video bug behavior #754

Merged
merged 4 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/common/types/participant.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type Slot = {
export interface Participant {
id: string;
name?: string;
type?: ParticipantType;
type?: ParticipantType | `${ParticipantType}`;
slot?: Slot;
avatar?: Avatar;
isHost?: boolean;
Expand Down
6 changes: 5 additions & 1 deletion src/common/types/stores.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useGlobalStore } from '../../services/stores';
import { useCoreStore } from '../../services/stores/core';
import { usePresence3DStore } from '../../services/stores/presence3D';
import { useVideoStore } from '../../services/stores/video';
import { useWhoIsOnlineStore } from '../../services/stores/who-is-online/index';
Expand All @@ -9,6 +10,7 @@ export enum StoreType {
WHO_IS_ONLINE = 'who-is-online-store',
VIDEO = 'video-store',
PRESENCE_3D = 'presence-3d-store',
CORE = 'core-store',
}

type Subject<T extends (...args: any[]) => any, K extends keyof ReturnType<T>> = ReturnType<T>[K];
Expand All @@ -24,13 +26,13 @@ type IncompleteStoreApi<T extends (...args: any[]) => any> = {

type StoreApi<T extends (...args: any[]) => any> = IncompleteStoreApi<T> & {
destroy(): void;
restart(): void;
};

type GlobalStore = StoreType.GLOBAL | `${StoreType.GLOBAL}`;
type WhoIsOnlineStore = StoreType.WHO_IS_ONLINE | 'who-is-online-store';
type VideoStore = StoreType.VIDEO | 'video-store';
type Presence3DStore = StoreType.PRESENCE_3D | 'presence-3d-store';
type CoreStore = StoreType.CORE | 'core-store';

export type Store<T> = T extends GlobalStore
? StoreApi<typeof useGlobalStore>
Expand All @@ -40,5 +42,7 @@ export type Store<T> = T extends GlobalStore
? StoreApi<typeof useVideoStore>
: T extends Presence3DStore
? StoreApi<typeof usePresence3DStore>
: T extends CoreStore
? StoreApi<typeof useCoreStore>
: never;
export type StoresTypes = typeof StoreType;
2 changes: 2 additions & 0 deletions src/common/utils/use-store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PublicSubject } from '../../services/stores/common/types';
import { useCoreStore } from '../../services/stores/core';
import { useGlobalStore } from '../../services/stores/global';
import { usePresence3DStore } from '../../services/stores/presence3D';
import { useVideoStore } from '../../services/stores/video';
Expand All @@ -10,6 +11,7 @@ const stores = {
[StoreType.WHO_IS_ONLINE]: useWhoIsOnlineStore,
[StoreType.VIDEO]: useVideoStore,
[StoreType.PRESENCE_3D]: usePresence3DStore,
[StoreType.CORE]: useCoreStore,
};

/**
Expand Down
2 changes: 1 addition & 1 deletion src/components/video/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ describe('VideoConference', () => {
VideoConferenceInstance['onParticipantLeft'](MOCK_LOCAL_PARTICIPANT);
expect(VideoConferenceInstance['publish']).toHaveBeenCalledWith(
MeetingEvent.MY_PARTICIPANT_LEFT,
MOCK_LOCAL_PARTICIPANT,
{ ...MOCK_LOCAL_PARTICIPANT, type: ParticipantType.HOST },
);
});

Expand Down
13 changes: 9 additions & 4 deletions src/components/video/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { ComponentNames } from '../types';

import { ParticipantToFrame, VideoComponentOptions } from './types';
import { MEETING_COLORS } from '../../common/types/meeting-colors.types';
import { coreBridge } from '../../services/core-bridge';

const KICK_PARTICIPANTS_TIME = 1000 * 60;
let KICK_PARTICIPANTS_TIMEOUT: ReturnType<typeof setTimeout> | null = null;
Expand Down Expand Up @@ -331,6 +332,7 @@ export class VideoConference extends BaseComponent {
this.localParticipant = {
...this.localParticipant,
...participant,
type: this.params.userType,
};
});

Expand Down Expand Up @@ -542,13 +544,14 @@ export class VideoConference extends BaseComponent {
joinedMeeting: true,
});

localParticipant.publish({
coreBridge.updateLocalParticipant({
...localParticipant.value,
avatar: participant.avatar,
name: participant.name,
type: this.params.userType,
});

participants.publish({
coreBridge.updateParticipantsList({
...participants.value,
[participant.id]: {
...participants.value[participant.id],
Expand All @@ -560,12 +563,13 @@ export class VideoConference extends BaseComponent {
return;
}

localParticipant.publish({
coreBridge.updateLocalParticipant({
...localParticipant.value,
name: newParticipantName,
type: this.params.userType,
});

participants.publish({
coreBridge.updateParticipantsList({
...participants.value,
[participant.id]: {
...participants.value[participant.id],
Expand Down Expand Up @@ -827,6 +831,7 @@ export class VideoConference extends BaseComponent {
this.publish(MeetingEvent.MEETING_HOST_AVAILABLE);
return;
}

this.publish(MeetingEvent.MEETING_NO_HOST_AVAILABLE);
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/video/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ export type ParticipantToFrame = {
name: string;
isHost: boolean;
avatar?: Avatar;
type: ParticipantType;
type: ParticipantType | `${ParticipantType}`;
slot: Slot;
};
34 changes: 26 additions & 8 deletions src/core/launcher/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,33 @@ export class Launcher extends Observable implements DefaultLauncher {
super();
this.logger = new Logger('@superviz/sdk/launcher');

const { localParticipant, group, isDomainWhitelisted } = this.useStore(StoreType.GLOBAL);

localParticipant.publish({ ...participant });
const {
localParticipant: globalParticipant,
group,
isDomainWhitelisted,
} = this.useStore(StoreType.GLOBAL);
const { localParticipant, participants } = this.useStore(StoreType.CORE);

globalParticipant.publish({ ...participant });
isDomainWhitelisted.subscribe(this.onAuthentication);
localParticipant.subscribe(this.onLocalParticipantUpdateOnStore);
globalParticipant.subscribe(this.onLocalParticipantUpdateOnStore);

localParticipant.subscribe(this.onLocalParticipantUpdateOnCore);
participants.subscribe(this.onParticipantsListUpdateOnCore);

group.publish(participantGroup);
this.ioc = new IOC(localParticipant.value);
this.ioc = new IOC(globalParticipant.value);
this.room = this.ioc.createRoom('launcher', 'unlimited');

// Assign a slot to the participant
this.slotService = new SlotService(this.room, this.useStore);
localParticipant.publish({
...localParticipant.value,
globalParticipant.publish({
...globalParticipant.value,
slot: this.slotService.slot,
activeComponents: [],
});

this.participant = localParticipant.value;
this.participant = globalParticipant.value;

// internal events without realtime
this.eventBus = new EventBus();
Expand Down Expand Up @@ -272,6 +280,16 @@ export class Launcher extends Observable implements DefaultLauncher {
this.activeComponents = participant.activeComponents || [];
};

private onLocalParticipantUpdateOnCore = (participant: Participant): void => {
if (!this.room) return;
this.room.presence.update(participant);
};

private onParticipantsListUpdateOnCore = (list: Record<string, Participant>): void => {
const { participants } = this.useStore(StoreType.GLOBAL);
participants.publish(list);
};

private onSameAccount = (): void => {
this.publish(ParticipantEvent.SAME_ACCOUNT_ERROR);
this.destroy();
Expand Down
40 changes: 40 additions & 0 deletions src/services/core-bridge/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { coreBridge } from '.';

const localParticipantSpy = jest.fn();
const participantsSpy = jest.fn();
jest.mock('../../common/utils/use-store', () => ({
useStore: () => ({
localParticipant: {
publish: localParticipantSpy,
},
participants: {
publish: participantsSpy,
},
}),
}));

describe('coreBridge', () => {
describe('updateLocalParticipant', () => {
test('should log "updateLocalParticipant" and call localParticipant.publish', () => {
const data = { id: '1' };
const logSpy = jest.spyOn(coreBridge['logger'], 'log');

coreBridge.updateLocalParticipant(data);

expect(logSpy).toHaveBeenCalledWith('updateLocalParticipant', data);
expect(localParticipantSpy).toHaveBeenCalledWith(data);
});
});

describe('updateParticipantsList', () => {
test('should log "updateParticipantsList" and call participants.publish', () => {
const data = { 1: { id: '1' } };
const logSpy = jest.spyOn(coreBridge['logger'], 'log');

coreBridge.updateParticipantsList(data);

expect(logSpy).toHaveBeenCalledWith('updateParticipantsList', data);
expect(participantsSpy).toHaveBeenCalledWith(data);
});
});
});
26 changes: 26 additions & 0 deletions src/services/core-bridge/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Participant } from '../../common/types/participant.types';
import { StoreType } from '../../common/types/stores.types';
import { Logger } from '../../common/utils';
import { useStore } from '../../common/utils/use-store';

class CoreBridge {
private logger: Logger;
constructor() {
this.logger = new Logger('@superviz/sdk/core-bridge');
this.logger.log('CoreBridge initialized');
}

public updateLocalParticipant(data: Participant) {
this.logger.log('updateLocalParticipant', data);
const { localParticipant } = useStore(StoreType.CORE);
localParticipant.publish(data);
}

public updateParticipantsList(data: Record<Participant['id'], Participant>) {
this.logger.log('updateParticipantsList', data);
const { participants } = useStore(StoreType.CORE);
participants.publish(data);
}
}

export const coreBridge = new CoreBridge();
2 changes: 1 addition & 1 deletion src/services/presence-3d-manager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export class Presence3DManager {
private onJoinedPresence = (event: PresenceEvent<Participant>): void => {
if (event.id !== this.localParticipant.id) return;

this.logger.log('participant joined 3D room', event.id);
this.logger.log('participant joined 3D room', event.id, this.localParticipant);
this.onLocalParticipantJoined(this.localParticipant);
};

Expand Down
38 changes: 38 additions & 0 deletions src/services/stores/core/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Participant } from '../../../common/types/participant.types';
import { Singleton } from '../common/types';
import { CreateSingleton } from '../common/utils';
import subject from '../subject';

const instance: Singleton<CoreStore> = CreateSingleton<CoreStore>();

class CoreStore {
public localParticipant = subject<Participant>({} as Participant);
public participants = subject<Record<Participant['id'], Participant>>({});

constructor() {
if (instance.value) {
throw new Error('CoreStore is a singleton. There can only be one instance of it.');
}

instance.value = this;
}

public destroy() {
this.localParticipant.destroy();
this.participants.destroy();
}
}

const store = new CoreStore();
const destroy = store.destroy.bind(store);

const localParticipant = store.localParticipant.expose();
const participants = store.participants.expose();

export function useCoreStore() {
return {
localParticipant,
participants,
destroy,
};
}
Loading