Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Video Component #113

Draft
wants to merge 5 commits into
base: lab
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions apps/playground/src/lib/sdk/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export {
default as Room,
VideoConference,
VideoMeeting,
WhoIsOnline,
FormElements,
Comments,
Expand Down
77 changes: 77 additions & 0 deletions apps/playground/src/pages/video-meeting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {
LauncherFacade,
ParticipantType,
Room,
VideoMeeting,
} from "../lib/sdk";
import { v4 as generateId } from "uuid";

import { useCallback, useEffect, useRef } from "react";
import { getConfig } from "../config";
import { useSearchParams } from "react-router-dom";

const SUPERVIZ_KEY = getConfig<string>("keys.superviz");
const SUPERVIZ_ROOM_PREFIX = getConfig<string>("roomPrefix");

const componentName = "video-conference";

export function VideoMeetingPage() {
const room = useRef<LauncherFacade>();
const loaded = useRef<boolean>(false);
const video = useRef<VideoMeeting>();
const [searchParams] = useSearchParams();

const initializeSuperViz = useCallback(async () => {
const uuid = generateId();
const type =
(searchParams.get("userType") as ParticipantType) || ParticipantType.HOST;

room.current = await Room(SUPERVIZ_KEY, {
roomId: `${SUPERVIZ_ROOM_PREFIX}-${componentName}`,
participant: {
name: "Participant " + type,
id: uuid,
},
group: {
name: SUPERVIZ_ROOM_PREFIX,
id: SUPERVIZ_ROOM_PREFIX,
},
environment: "dev",
debug: true,
});

video.current = new VideoMeeting({
participantType: 'guest',
permissions: {
allowGuests: true,
toggleCamera: false,
toggleChat: false,
toggleMic: false,
toggleParticipantList: false,
toggleRecording: false,
toggleScreenShare: false,
}
});

room.current.addComponent(video.current);
}, []);

useEffect(() => {
if (loaded.current) return;
loaded.current = true;
initializeSuperViz();

return () => {
room.current?.removeComponent(video.current);
room.current?.destroy();
};
}, []);

const initAgain = () => {
video.current = new VideoMeeting();

room.current!.addComponent(video.current);
};

return <button onClick={initAgain}>Init again</button>;
}
5 changes: 5 additions & 0 deletions apps/playground/src/router/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { YjsMonacoWio } from "../pages/yjs-monaco-wio.tsx";
import { YjsQuillWio } from "../pages/yjs-quill-wio.tsx";
import { YjsQuillReact } from "../pages/yjs-quill-react.tsx";
import { ReactFlowWithReactSDK } from "../pages/react-flow-with-react-sdk.tsx";
import { VideoMeetingPage } from "../pages/video-meeting.tsx";

export const routeList: RouteObject[] = [
{
Expand All @@ -32,6 +33,10 @@ export const routeList: RouteObject[] = [
path: "video",
element: <Video />,
},
{
path: "video-meeting",
element: <VideoMeetingPage />,
},
{
path: "matterport",
element: <Matterport />,
Expand Down
11 changes: 7 additions & 4 deletions packages/sdk/src/common/types/cdn.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { PresenceEvents } from '@superviz/socket-client';

import type {
CanvasPin,
HTMLPin,
Expand All @@ -7,7 +9,9 @@ import type {
VideoConference,
WhoIsOnline,
FormElements,
VideoMeeting,
} from '../../components';
import { FieldEvents } from '../../components/form-elements/types';
import type {
RealtimeComponentEvent,
RealtimeComponentState,
Expand All @@ -18,6 +22,7 @@ import type {
LayoutMode,
LayoutPosition,
} from '../../services/video-conference-manager/types';
import { PinMode } from '../../web-components/comments/components/types';

import type {
DeviceEvent,
Expand All @@ -34,10 +39,7 @@ import type {
} from './events.types';
import { ParticipantType } from './participant.types';
import { SuperVizSdkOptions } from './sdk-options.types';
import { StoreType } from '../types/stores.types';
import { PresenceEvents } from '@superviz/socket-client';
import { FieldEvents } from '../../components/form-elements/types';
import { PinMode } from '../../web-components/comments/components/types';
import { StoreType } from './stores.types';

export interface SuperVizCdn {
init: (apiKey: string, options: SuperVizSdkOptions) => Promise<LauncherFacade>;
Expand All @@ -57,6 +59,7 @@ export interface SuperVizCdn {
LayoutPosition: typeof LayoutPosition;
CamerasPosition: typeof CamerasPosition;
VideoConference: typeof VideoConference;
VideoMeeting: typeof VideoMeeting;
MousePointers: typeof MousePointers;
Realtime: typeof Realtime;
Comments: typeof Comments;
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/components/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Comments } from './comments';
import { CanvasPin } from './comments/canvas-pin-adapter';
import { MousePointers } from './presence-mouse';
import { Realtime } from './realtime';
import { VideoConference } from './video';
import { VideoConference } from './video-conference';

import * as Components from '.';

Expand Down
3 changes: 2 additions & 1 deletion packages/sdk/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export { Comments } from './comments';
export { CanvasPin } from './comments/canvas-pin-adapter';
export { HTMLPin } from './comments/html-pin-adapter';
export { VideoConference } from './video';
export { VideoConference } from './video-conference';
export { MousePointers } from './presence-mouse';
export { Realtime } from './realtime';
export { WhoIsOnline } from './who-is-online';
export { FormElements } from './form-elements';
export { VideoMeeting } from './video-meeting';
158 changes: 158 additions & 0 deletions packages/sdk/src/components/video-conference/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { ColorsVariables } from '../../common/types/colors.types';
import { ParticipantType } from '../../common/types/participant.types';
import { BrowserService } from '../../services/browser';
import config from '../../services/config';
import VideoConferecenManager from '../../services/video-conference-manager';
import { CamerasPosition, LayoutMode, LayoutPosition } from '../../services/video-conference-manager/types';

import { VideoConferenceOptions } from './types';

import { VideoConference } from '.';

// Mock dependencies
jest.mock('../../services/config');
jest.mock('../../services/video-conference-manager');

describe('VideoConference Class', () => {
const defaultParams: VideoConferenceOptions = {
showAudienceList: true,
camsOff: false,
screenshareOff: false,
chatOff: false,
enableRecording: true,
defaultAvatars: true,
offset: {
top: 0,
bottom: 0,
left: 0,
right: 0,
},
enableFollow: true,
enableGoTo: true,
enableGather: true,
defaultToolbar: true,
devices: { audioInput: true, videoInput: true, audioOutput: true },
language: 'en',
locales: [{ language: 'en', messages: {} }],
avatars: [],
skipMeetingSettings: false,
allowGuests: true,
userType: ParticipantType.GUEST,
participantType: ParticipantType.GUEST,
styles: 'default-style',
collaborationMode: {
enabled: true,
position: CamerasPosition.LEFT,
modalPosition: LayoutPosition.CENTER,
initialView: LayoutMode.GRID,
},
callbacks: {
onToggleMicrophone: jest.fn(),
onToggleCam: jest.fn(),
},
};

beforeEach(() => {
jest.clearAllMocks();
config.get = jest.fn().mockImplementation((key) => {
if (key === 'colors') return { primary: '#000000' } as ColorsVariables;
if (key === 'waterMark') return true;
return undefined;
});
});

test('should initialize with provided params', () => {
const videoConference = new VideoConference(defaultParams);

expect(videoConference['allowGuests']).toBe(true);
expect(videoConference['userType']).toBe(ParticipantType.GUEST);
});

test('should initialize with default values when no params are provided', () => {
const videoConference = new VideoConference({});

expect(videoConference['allowGuests']).toBe(false);
expect(videoConference['userType']).toBe(ParticipantType.GUEST);
});

test('should set videoConfig correctly in startVideo', () => {
const videoConference = new VideoConference(defaultParams);
videoConference['startVideo']();

expect(videoConference['videoConfig']).toEqual({
language: 'en',
canUseRecording: true,
canShowAudienceList: true,
canUseChat: true,
canUseCams: true,
canUseScreenshare: true,
canUseDefaultAvatars: true,
canUseGather: true,
canUseFollow: true,
canUseGoTo: true,
canUseDefaultToolbar: true,
camerasPosition: CamerasPosition.LEFT,
devices: { audioInput: true, videoInput: true, audioOutput: true },
skipMeetingSettings: false,
browserService: new BrowserService(),
offset: {
top: 0,
bottom: 0,
left: 0,
right: 0,
},
locales: defaultParams.locales,
avatars: [],
customColors: { primary: '#000000' },
waterMark: true,
styles: 'default-style',
collaborationMode: true,
layoutPosition: LayoutPosition.CENTER,
layoutMode: LayoutMode.GRID,
callbacks: defaultParams.callbacks,
});
});

test('should instantiate VideoConferecenManager in startVideo', () => {
const videoConference = new VideoConference(defaultParams);
videoConference['startVideo']();

expect(VideoConferecenManager).toHaveBeenCalledWith(videoConference['videoConfig']);
expect(videoConference['videoManager']).toBeInstanceOf(VideoConferecenManager);
});

test('should handle undefined collaborationMode gracefully', () => {
const paramsWithoutCollabMode: VideoConferenceOptions = {
...defaultParams,
collaborationMode: undefined,
};
const videoConference = new VideoConference(paramsWithoutCollabMode);
videoConference['startVideo']();

expect(videoConference['videoConfig'].collaborationMode).toBe(true); // Default to true
expect(videoConference['videoConfig'].layoutPosition).toBe(LayoutPosition.CENTER);
expect(videoConference['videoConfig'].layoutMode).toBe(LayoutMode.LIST);
});

test('should handle undefined permissions gracefully', () => {
const paramsWithoutPermissions: VideoConferenceOptions = {
...defaultParams,
camsOff: undefined,
screenshareOff: undefined,
chatOff: undefined,
};
const videoConference = new VideoConference(paramsWithoutPermissions);
videoConference['startVideo']();

expect(videoConference['videoConfig'].canUseChat).toBe(true);
expect(videoConference['videoConfig'].canUseCams).toBe(true);
expect(videoConference['videoConfig'].canUseScreenshare).toBe(true);
});

test('should pass callbacks correctly in videoConfig', () => {
const videoConference = new VideoConference(defaultParams);
videoConference['startVideo']();

expect(videoConference['videoConfig'].callbacks).toEqual(defaultParams.callbacks);
});
});
68 changes: 68 additions & 0 deletions packages/sdk/src/components/video-conference/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { ColorsVariables } from '../../common/types/colors.types';
import { ParticipantType } from '../../common/types/participant.types';
import config from '../../services/config';
import VideoConferecenManager from '../../services/video-conference-manager';
import { CamerasPosition, LayoutMode, LayoutPosition } from '../../services/video-conference-manager/types';
import { VideoComponent } from '../video';

import { VideoConferenceOptions } from './types';

export class VideoConference extends VideoComponent {
private params: VideoConferenceOptions;
protected allowGuests: boolean;
protected userType: ParticipantType | `${ParticipantType}`;

constructor(params: VideoConferenceOptions) {
super();

this.params = params;

this.userType = params?.participantType ?? params?.userType ?? ParticipantType.GUEST;
this.allowGuests = params.allowGuests ?? false;
}

/**
* @function startVideo
* @description start video manager
* @returns {void}
*/
protected startVideo = (): void => {
const defaultAvatars =
this.userType !== ParticipantType.AUDIENCE && this.params?.defaultAvatars === true;

this.videoConfig = {
language: this.params?.language,
canUseRecording: !!this.params?.enableRecording,
canShowAudienceList: this.params?.showAudienceList ?? true,
canUseChat: !this.params?.chatOff,
canUseCams: !this.params?.camsOff,
canUseScreenshare: !this.params?.screenshareOff,
canUseDefaultAvatars: defaultAvatars && !this.localParticipant?.avatar?.model3DUrl,
canUseGather: !!this.params?.enableGather,
canUseFollow: !!this.params?.enableFollow,
canUseGoTo: !!this.params?.enableGoTo,
canUseDefaultToolbar: this.params?.defaultToolbar ?? true,
camerasPosition: this.params?.collaborationMode?.position as CamerasPosition,
devices: this.params?.devices,
skipMeetingSettings: this.params?.skipMeetingSettings,
browserService: this.browserService,
offset: this.params?.offset,
locales: this.params?.locales ?? [],
avatars: this.params?.avatars ?? [],
customColors: config.get<ColorsVariables>('colors'),
waterMark: config.get<boolean>('waterMark'),
styles: this.params?.styles,
collaborationMode: this.params?.collaborationMode?.enabled ?? true,
layoutPosition:
this.params?.collaborationMode?.enabled === false
? LayoutPosition.CENTER
: (this.params?.collaborationMode?.modalPosition as LayoutPosition) ??
LayoutPosition.CENTER,
layoutMode: (this.params?.collaborationMode?.initialView as LayoutMode) ?? LayoutMode.LIST,
callbacks: this.params?.callbacks,
};

this.logger.log('video conference @ start video', this.videoConfig);
this.videoManager = new VideoConferecenManager(this.videoConfig);
};
}
Loading
Loading