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

Commit

Permalink
fix: video bug behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
Raspincel committed Aug 26, 2024
1 parent f44870d commit b664a9e
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 17 deletions.
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
10 changes: 6 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,13 @@ export class VideoConference extends BaseComponent {
joinedMeeting: true,
});

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

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

localParticipant.publish({
coreBridge.updateLocalParticipant({
...localParticipant.value,
name: newParticipantName,
});

participants.publish({
coreBridge.updateParticipantsList({
...participants.value,
[participant.id]: {
...participants.value[participant.id],
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,
};
}

0 comments on commit b664a9e

Please sign in to comment.