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

Commit

Permalink
Merge pull request #673 from SuperViz/lab
Browse files Browse the repository at this point in the history
Migrating Video and Presences components to new IO
  • Loading branch information
carlossantos74 authored May 20, 2024
2 parents 5ab02e0 + 89e3d69 commit b75b3c7
Show file tree
Hide file tree
Showing 65 changed files with 4,182 additions and 2,561 deletions.
12 changes: 12 additions & 0 deletions __mocks__/config.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,16 @@ export const MOCK_CONFIG: Configuration = {
limits: LIMITS_MOCK,
waterMark: WATERMARK_MOCK,
colors: MOCK_COLORS,
features: {
realtime: true,
presence: true,
videoConference: true,
comments: true,
whoIsOnline: true,
presence3dMatterport: true,
presence3dAutodesk: true,
presence3dThreejs: true,
formElements: true,
transcriptLangs: ['en-US'],
},
};
10 changes: 9 additions & 1 deletion __mocks__/io.mock.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { jest } from '@jest/globals';
import { Room } from '@superviz/socket-client';

export const MOCK_IO = {
PresenceEvents: {
Expand All @@ -7,6 +8,13 @@ export const MOCK_IO = {
ERROR: 'presence.error',
UPDATE: 'presence.update',
},
RoomEvents: {
JOIN_ROOM: 'room.join',
JOINED_ROOM: 'room.joined',
LEAVE_ROOM: 'room.leave',
UPDATE: 'room.update',
ERROR: 'room.error',
},
Realtime: class {
connection;

Expand All @@ -30,7 +38,7 @@ export const MOCK_IO = {
get: jest.fn(),
update: jest.fn(),
},
};
} as unknown as Room;
}

destroy() {}
Expand Down
13 changes: 10 additions & 3 deletions __mocks__/participants.mock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Avatar, Group, Participant } from '../src';
import { MeetingColorsHex } from '../src/common/types/meeting-colors.types';
import { MeetingColors, MeetingColorsHex } from '../src/common/types/meeting-colors.types';
import { ParticipantByGroupApi } from '../src/common/types/participant.types';
import { AblyParticipant } from '../src/services/realtime/ably/types';

Expand All @@ -16,6 +16,13 @@ export const MOCK_LOCAL_PARTICIPANT: Participant = {
imageUrl: 'unit-test-avatar-thumbnail.png',
model3DUrl: 'unit-test-avatar-model.glb',
},
slot: {
color: MeetingColorsHex[0],
index: 0,
colorName: MeetingColors[0],
textColor: '#000',
timestamp: 0,
},
};

export const MOCK_GROUP: Group = {
Expand Down Expand Up @@ -60,5 +67,5 @@ export const MOCK_PARTICIPANT_LIST: ParticipantByGroupApi[] = [
name: 'unit-test-participant1-name',
avatar: 'unit-test-participant1-avatar',
email: 'unit-test-participant1-email',
}
]
},
];
4 changes: 0 additions & 4 deletions __mocks__/realtime.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,11 @@ export const ABLY_REALTIME_MOCK: AblyRealtimeService = {
kickAllParticipantsObserver: MOCK_OBSERVER_HELPER,
kickParticipantObserver: MOCK_OBSERVER_HELPER,
authenticationObserver: MOCK_OBSERVER_HELPER,
commentsObserver: MOCK_OBSERVER_HELPER,
presenceMouseParticipantLeaveObserver: MOCK_OBSERVER_HELPER,
presenceMouseParticipantJoinedObserver: MOCK_OBSERVER_HELPER,
presenceMouseObserver: MOCK_OBSERVER_HELPER,
presenceSlotsInfosObserver: MOCK_OBSERVER_HELPER,
presenceWIOObserver: MOCK_OBSERVER_HELPER,
privateModeWIOObserver: MOCK_OBSERVER_HELPER,
followWIOObserver: MOCK_OBSERVER_HELPER,
gatherWIOObserver: MOCK_OBSERVER_HELPER,
sameAccountObserver: MOCK_OBSERVER_HELPER,
subscribeToParticipantUpdate: jest.fn(),
unsubscribeFromParticipantUpdate: jest.fn(),
Expand Down
42 changes: 42 additions & 0 deletions __mocks__/roomState.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { jest } from '@jest/globals';

import { ParticipantType } from '../src/common/types/participant.types';
import { RealtimeStateTypes } from '../src/common/types/realtime.types';
import { Logger, Observer } from '../src/common/utils';
import { IOC } from '../src/services/io';
import { RoomStateService } from '../src/services/roomState';

import { MOCK_LOCAL_PARTICIPANT } from './participants.mock';

export const ROOM_STATE_MOCK = {
room: new IOC(MOCK_LOCAL_PARTICIPANT).createRoom('mock_room'),
logger: new Logger('@superviz/sdk/roomStateMock'),
myParticipant: { ...MOCK_LOCAL_PARTICIPANT, type: ParticipantType.HOST },
enableSync: true,
isSyncFrozen: false,
left: false,
state: RealtimeStateTypes.CONNECTED,
MESSAGE_SIZE_LIMIT: 60000,
useStore: jest.fn(),
kickParticipantObserver: new Observer(),
join: jest.fn(),
updateMyProperties: jest.fn(),
isMessageTooBig: jest.fn(),
updateRoomProperties: jest.fn(),
setHost: jest.fn(),
setKickParticipant: jest.fn(),
setGridMode: jest.fn(),
setDrawing: jest.fn(),
setTranscript: jest.fn(),
initializeRoomProperties: jest.fn(),
onParticipantLeave: jest.fn(),
fetchRoomProperties: jest.fn(),
onJoinRoom: jest.fn(),
publishStateUpdate: jest.fn(),
onPresenceEnter: jest.fn(),
setFollowParticipant: jest.fn(),
setGather: jest.fn(),
updateLocalRoomState: jest.fn(),
freezeSync: jest.fn(),
destroy: jest.fn(),
} as unknown as RoomStateService;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
"yargs": "^17.7.2"
},
"dependencies": {
"@superviz/socket-client": "1.2.2",
"@superviz/socket-client": "1.4.0",
"ably": "^1.2.45",
"bowser": "^2.11.0",
"bowser-jr": "^1.0.6",
Expand Down
4 changes: 2 additions & 2 deletions src/common/types/participant.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export interface Group {
}

export interface Avatar {
model3DUrl: string;
imageUrl: string;
model3DUrl?: string;
imageUrl?: string;
}

export type ParticipantByCommentsApi = {
Expand Down
28 changes: 19 additions & 9 deletions src/common/types/stores.types.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { useGlobalStore } from '../../services/stores';
import { PublicSubject } from '../../services/stores/common/types';
import { usePresence3DStore } from '../../services/stores/presence3D';
import { useVideoStore } from '../../services/stores/video';
import { useWhoIsOnlineStore } from '../../services/stores/who-is-online/index';

export enum StoreType {
GLOBAL = 'global-store',
COMMENTS = 'comments-store',
WHO_IS_ONLINE = 'who-is-online-store',
VIDEO = 'video-store',
PRESENCE_3D = 'presence-3d-store',
}

type Subject<T extends (...args: any[]) => any, K extends keyof ReturnType<T>> = ReturnType<T>[K]
type Subject<T extends (...args: any[]) => any, K extends keyof ReturnType<T>> = ReturnType<T>[K];

type StoreApiWithoutDestroy<T extends (...args: any[]) => any> = {
[K in keyof ReturnType<T>]: {
Expand All @@ -17,17 +20,24 @@ type StoreApiWithoutDestroy<T extends (...args: any[]) => any> = {
publish(value: Subject<T, K>['value']): void;
value: Subject<T, K>['value'];
};
}
};

type StoreApi<T extends (...args: any[]) => any> = Omit<StoreApiWithoutDestroy<T>, 'destroy'> & { destroy(): void };
type StoreApi<T extends (...args: any[]) => any> = Omit<StoreApiWithoutDestroy<T>, 'destroy'> & {
destroy(): 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';

// When creating new Stores, expand the ternary with the new Store. For example:
// ...T extends StoreType.GLOBAL ? StoreApi<typeof useGlobalStore> : T extends StoreType.WHO_IS_ONLINE ? StoreApi<typeof useWhoIsOnlineStore> : never;
// Yes, it will be a little bit verbose, but it's not like we'll be creating more and more Stores just for. Rarely will someone need to come here
export type Store<T> = T extends StoreType.GLOBAL
export type Store<T> = T extends GlobalStore
? StoreApi<typeof useGlobalStore>
: T extends StoreType.WHO_IS_ONLINE
: T extends WhoIsOnlineStore
? StoreApi<typeof useWhoIsOnlineStore>
: T extends VideoStore
? StoreApi<typeof useVideoStore>
: T extends Presence3DStore
? StoreApi<typeof usePresence3DStore>
: never;
export type StoresTypes = typeof StoreType;
7 changes: 6 additions & 1 deletion src/common/utils/use-store.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { PublicSubject } from '../../services/stores/common/types';
import { useGlobalStore } from '../../services/stores/global';
import { usePresence3DStore } from '../../services/stores/presence3D';
import { useVideoStore } from '../../services/stores/video';
import { useWhoIsOnlineStore } from '../../services/stores/who-is-online';
import { Store, StoreType } from '../types/stores.types';

const stores = {
[StoreType.GLOBAL]: useGlobalStore,
[StoreType.WHO_IS_ONLINE]: useWhoIsOnlineStore,
[StoreType.VIDEO]: useVideoStore,
[StoreType.PRESENCE_3D]: usePresence3DStore,
};

/**
Expand All @@ -30,6 +34,7 @@ function subscribeTo<T>(
if (this.requestUpdate) this.requestUpdate();
});

if (!this.unsubscribeFrom) this.unsubscribeFrom = [];
this.unsubscribeFrom.push(subject.unsubscribe);
}

Expand All @@ -44,7 +49,7 @@ export function useStore<T extends StoreType>(name: T): Store<T> {
const proxy = new Proxy(storeData, {
get(store, valueName: string) {
if (valueName === 'destroy') return store.destroy;

return {
subscribe(callback?) {
bindedSubscribeTo(valueName, store[valueName], callback);
Expand Down
17 changes: 15 additions & 2 deletions src/components/base/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import { EVENT_BUS_MOCK } from '../../../__mocks__/event-bus.mock';
import { MOCK_OBSERVER_HELPER } from '../../../__mocks__/observer-helper.mock';
import { MOCK_GROUP, MOCK_LOCAL_PARTICIPANT } from '../../../__mocks__/participants.mock';
import { ABLY_REALTIME_MOCK } from '../../../__mocks__/realtime.mock';
import { ROOM_STATE_MOCK } from '../../../__mocks__/roomState.mock';
import { StoreType } from '../../common/types/stores.types';
import { Logger } from '../../common/utils';
import { useStore } from '../../common/utils/use-store';
import { Configuration } from '../../services/config/types';
import { EventBus } from '../../services/event-bus';
import { IOC } from '../../services/io';
import { Presence3DManager } from '../../services/presence-3d-manager';
import { AblyRealtimeService } from '../../services/realtime';
import { useGlobalStore } from '../../services/stores';
import { ComponentNames } from '../types';
Expand All @@ -34,7 +37,7 @@ class DummyComponent extends BaseComponent {
}
}

const REALTIME_MOCK = Object.assign({}, ABLY_REALTIME_MOCK, { isJoinedRoom: true });
const REALTIME_MOCK = Object.assign({}, ABLY_REALTIME_MOCK, { hasJoinedRoom: true });

jest.mock('../../common/utils/observer', () => ({
Observer: jest.fn().mockImplementation(() => MOCK_OBSERVER_HELPER),
Expand All @@ -50,9 +53,10 @@ describe('BaseComponent', () => {
console.error = jest.fn();

jest.clearAllMocks();
const { localParticipant, group } = useGlobalStore();
const { localParticipant, group, hasJoinedRoom } = useGlobalStore();
localParticipant.value = MOCK_LOCAL_PARTICIPANT;
group.value = MOCK_GROUP;
hasJoinedRoom.value = true;

DummyComponentInstance = new DummyComponent();
});
Expand All @@ -63,11 +67,15 @@ describe('BaseComponent', () => {

describe('attach', () => {
test('should not call start if realtime is not joined room', () => {
const { hasJoinedRoom } = useStore(StoreType.GLOBAL);
hasJoinedRoom.publish(false);

DummyComponentInstance.attach({
ioc: new IOC(MOCK_LOCAL_PARTICIPANT),
realtime: ABLY_REALTIME_MOCK,
config: MOCK_CONFIG,
eventBus: EVENT_BUS_MOCK,
Presence3DManagerService: Presence3DManager,
useStore,
});

Expand All @@ -88,6 +96,7 @@ describe('BaseComponent', () => {
ablyMock['isDomainWhitelisted'] = false;

DummyComponentInstance.attach({
Presence3DManagerService: Presence3DManager,
ioc: new IOC(MOCK_LOCAL_PARTICIPANT),
realtime: ablyMock as AblyRealtimeService,
config: MOCK_CONFIG,
Expand All @@ -107,6 +116,7 @@ describe('BaseComponent', () => {
expect(DummyComponentInstance.attach).toBeDefined();

DummyComponentInstance.attach({
Presence3DManagerService: Presence3DManager,
ioc: new IOC(MOCK_LOCAL_PARTICIPANT),
realtime: REALTIME_MOCK,
config: MOCK_CONFIG,
Expand All @@ -125,6 +135,7 @@ describe('BaseComponent', () => {
expect(() => {
DummyComponentInstance.attach({
ioc: null as unknown as IOC,
Presence3DManagerService: Presence3DManager,
realtime: null as unknown as AblyRealtimeService,
config: null as unknown as Configuration,
eventBus: null as unknown as EventBus,
Expand All @@ -140,6 +151,7 @@ describe('BaseComponent', () => {
expect(DummyComponentInstance.detach).toBeDefined();

DummyComponentInstance.attach({
Presence3DManagerService: Presence3DManager,
ioc: new IOC(MOCK_LOCAL_PARTICIPANT),
realtime: REALTIME_MOCK,
config: MOCK_CONFIG,
Expand All @@ -161,6 +173,7 @@ describe('BaseComponent', () => {
DummyComponentInstance['destroy'] = jest.fn();

DummyComponentInstance.attach({
Presence3DManagerService: Presence3DManager,
ioc: new IOC(MOCK_LOCAL_PARTICIPANT),
realtime: REALTIME_MOCK,
config: MOCK_CONFIG,
Expand Down
11 changes: 7 additions & 4 deletions src/components/base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import * as Socket from '@superviz/socket-client';

import { ComponentLifeCycleEvent } from '../../common/types/events.types';
import { Group } from '../../common/types/participant.types';
import { StoreType } from '../../common/types/stores.types';
import { Logger, Observable } from '../../common/utils';
import { useStore } from '../../common/utils/use-store';
import config from '../../services/config';
import { EventBus } from '../../services/event-bus';
import { IOC } from '../../services/io';
import { AblyRealtimeService } from '../../services/realtime';
import { useGlobalStore } from '../../services/stores';
import { RoomStateService } from '../../services/roomState';
import { ComponentNames } from '../types';

import { DefaultAttachComponentOptions } from './types';
Expand Down Expand Up @@ -40,11 +41,11 @@ export abstract class BaseComponent extends Observable {
}

const { realtime, config: globalConfig, eventBus, ioc } = params;
const { isDomainWhitelisted, hasJoinedRoom } = this.useStore(StoreType.GLOBAL);

if (!realtime.isDomainWhitelisted) {
if (!isDomainWhitelisted.value) {
const message = `Component ${this.name} can't be used because this website's domain is not whitelisted. Please add your domain in https://dashboard.superviz.com/developer`;
this.logger.log(message);
console.error(message);
return;
}

Expand All @@ -55,7 +56,7 @@ export abstract class BaseComponent extends Observable {
this.ioc = ioc;
this.room = ioc.createRoom(this.name);

if (!this.realtime.isJoinedRoom) {
if (!hasJoinedRoom.value) {
this.logger.log(`${this.name} @ attach - not joined yet`);

setTimeout(() => {
Expand Down Expand Up @@ -84,9 +85,11 @@ export abstract class BaseComponent extends Observable {
}

this.logger.log('detached');

this.publish(ComponentLifeCycleEvent.UNMOUNT);
this.destroy();
this.room.disconnect();
this.room = undefined;

this.unsubscribeFrom.forEach((unsubscribe) => unsubscribe(this));

Expand Down
3 changes: 3 additions & 0 deletions src/components/base/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { Store, StoreType } from '../../common/types/stores.types';
import { Configuration } from '../../services/config/types';
import { EventBus } from '../../services/event-bus';
import { IOC } from '../../services/io';
import { Presence3DManager } from '../../services/presence-3d-manager';
import { AblyRealtimeService } from '../../services/realtime';
import { RoomStateService } from '../../services/roomState';
import { useGlobalStore } from '../../services/stores';

export interface DefaultAttachComponentOptions {
Expand All @@ -11,6 +13,7 @@ export interface DefaultAttachComponentOptions {
config: Configuration;
eventBus: EventBus;
useStore: <T extends StoreType>(name: T) => Store<T>;
Presence3DManagerService: typeof Presence3DManager;
}

export type GlobalStore = {
Expand Down
Loading

0 comments on commit b75b3c7

Please sign in to comment.