From a8f8cd52713462c4621d47de22a3c84718639a18 Mon Sep 17 00:00:00 2001 From: Carlos Date: Fri, 24 Jan 2025 08:09:14 -0300 Subject: [PATCH] feat: video props and props validations --- .../video/src/common/types/brand.types.ts | 9 +++ .../src/common/types/participant.types.ts | 28 +++++++++ .../src/common/types/permissions.types.ts | 8 +++ packages/video/src/components/base/index.ts | 7 +++ .../components/video-conference/index.test.ts | 62 +++++++++++++++++++ .../src/components/video-conference/index.ts | 47 ++++++++++++++ .../src/components/video-conference/types.ts | 12 ++++ packages/video/src/index.test.ts | 7 --- packages/video/src/index.ts | 3 +- 9 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 packages/video/src/common/types/brand.types.ts create mode 100644 packages/video/src/common/types/participant.types.ts create mode 100644 packages/video/src/common/types/permissions.types.ts create mode 100644 packages/video/src/components/base/index.ts create mode 100644 packages/video/src/components/video-conference/index.test.ts create mode 100644 packages/video/src/components/video-conference/index.ts create mode 100644 packages/video/src/components/video-conference/types.ts delete mode 100644 packages/video/src/index.test.ts diff --git a/packages/video/src/common/types/brand.types.ts b/packages/video/src/common/types/brand.types.ts new file mode 100644 index 00000000..1e1a7f7b --- /dev/null +++ b/packages/video/src/common/types/brand.types.ts @@ -0,0 +1,9 @@ +import { z } from 'zod'; + +export const BrandSchema = z.object({ + logoUrl: z.string().url('[SuperViz] - logoUrl must be a valid URL').optional(), +}); + +export type Brand = { + logoUrl?: string; +} diff --git a/packages/video/src/common/types/participant.types.ts b/packages/video/src/common/types/participant.types.ts new file mode 100644 index 00000000..2046063e --- /dev/null +++ b/packages/video/src/common/types/participant.types.ts @@ -0,0 +1,28 @@ +export interface Avatar { + model3DUrl?: string; + imageUrl?: string; +} + +export type Participant = { + id: string + name: string + email: string + avatar: Avatar + type: ParticipantType + slot: Slot + activeComponents: string[] +} + +export type Slot = { + index: number | null; + color: string; + textColor: string; + colorName: string; + timestamp: number; +} + +export enum ParticipantType { + HOST = 'host', + GUEST = 'guest', + AUDIENCE = 'audience', +} diff --git a/packages/video/src/common/types/permissions.types.ts b/packages/video/src/common/types/permissions.types.ts new file mode 100644 index 00000000..6a061f09 --- /dev/null +++ b/packages/video/src/common/types/permissions.types.ts @@ -0,0 +1,8 @@ +export type Permissions = { + toggleMic?: boolean; + toggleCamera?: boolean; + toggleScreenShare?: boolean; + toggleRecording?: boolean; + toggleChat?: boolean; + toggleParticipantList?: boolean; +}; diff --git a/packages/video/src/components/base/index.ts b/packages/video/src/components/base/index.ts new file mode 100644 index 00000000..f6bb29b5 --- /dev/null +++ b/packages/video/src/components/base/index.ts @@ -0,0 +1,7 @@ +export abstract class BaseComponent { + attach() {} + dettach() {} + + protected abstract destroy(): void; + protected abstract start(): void; +} diff --git a/packages/video/src/components/video-conference/index.test.ts b/packages/video/src/components/video-conference/index.test.ts new file mode 100644 index 00000000..3a3f21d7 --- /dev/null +++ b/packages/video/src/components/video-conference/index.test.ts @@ -0,0 +1,62 @@ +import { VideoConferenceProps } from './types'; + +import { VideoConference } from './index'; + +describe('VideoConference', () => { + it('should create VideoConference with default config when no props are provided', () => { + const videoConference = new VideoConference(); + expect(videoConference).toBeDefined(); + }); + + it('should validate and set the brand logo URL correctly', () => { + const props: VideoConferenceProps = { + brand: { logoUrl: 'https://example.com/logo.png' }, + participantType: 'host', + }; + const videoConference = new VideoConference(props); + expect(videoConference).toBeDefined(); + }); + + it('should log an error for invalid brand logo URL', () => { + console.error = jest.fn(); + const props: VideoConferenceProps = { + brand: { logoUrl: 'invalid-url' }, + participantType: 'host', + }; + expect(() => new VideoConference(props)).toThrow('[SuperViz] Invalid brand logo URL: invalid-url'); + expect(console.error).toHaveBeenCalledWith('[SuperViz] Invalid brand logo URL:', 'invalid-url'); + }); + + it('should log an error for invalid participant type', () => { + console.error = jest.fn(); + const props: VideoConferenceProps = { + participantType: 'invalid-type' as any, + }; + expect(() => new VideoConference(props)).toThrow('[SuperViz] Invalid participant type: invalid-type'); + expect(console.error).toHaveBeenCalledWith('[SuperViz] Invalid participant type:', 'invalid-type'); + }); + + it('should set default permissions when none are provided', () => { + const props: VideoConferenceProps = { + participantType: 'guest', + }; + const videoConference = new VideoConference(props); + expect(videoConference).toBeDefined(); + }); + + it('should set provided permissions correctly', () => { + const props: VideoConferenceProps = { + participantType: 'host', + permissions: { + toggleCamera: false, + toggleMic: false, + toggleChat: false, + toggleParticipantList: false, + toggleRecording: false, + toggleScreenShare: false, + }, + }; + const videoConference = new VideoConference(props); + expect(videoConference).toBeDefined(); + }); +}); diff --git a/packages/video/src/components/video-conference/index.ts b/packages/video/src/components/video-conference/index.ts new file mode 100644 index 00000000..61709548 --- /dev/null +++ b/packages/video/src/components/video-conference/index.ts @@ -0,0 +1,47 @@ +import { Logger } from '../../common/utils/logger'; +import { BaseComponent } from '../base'; + +import { VideoConferenceProps } from './types'; + +export class VideoConference extends BaseComponent { + private config: VideoConferenceProps; + private logger: Logger; + + constructor(props?: VideoConferenceProps) { + super(); + this.logger = new Logger('@superviz/video/video-conference'); + this.validateProps(props); + } + + private validateProps(props: VideoConferenceProps) { + if (props?.brand?.logoUrl) { + const urlPattern = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/; + if (!urlPattern.test(props.brand.logoUrl)) { + console.error('[SuperViz] Invalid brand logo URL:', props.brand.logoUrl); + throw new Error(`[SuperViz] Invalid brand logo URL: ${props.brand.logoUrl}`); + } + } + + if (props?.participantType && !['audience', 'host', 'guest'].includes(props?.participantType)) { + console.error('[SuperViz] Invalid participant type:', props.participantType); + throw new Error(`[SuperViz] Invalid participant type: ${props.participantType}`); + } + + this.config = { + styles: props?.styles || '', + brand: props?.brand || { logoUrl: undefined }, + participantType: props?.participantType || 'guest', + permissions: { + toggleCamera: props?.permissions?.toggleCamera || true, + toggleMic: props?.permissions?.toggleMic || true, + toggleChat: props?.permissions?.toggleChat || true, + toggleParticipantList: props?.permissions?.toggleParticipantList || true, + toggleRecording: props?.permissions?.toggleRecording || true, + toggleScreenShare: props?.permissions?.toggleScreenShare || true, + }, + }; + } + + protected start() {} + protected destroy() {} +} diff --git a/packages/video/src/components/video-conference/types.ts b/packages/video/src/components/video-conference/types.ts new file mode 100644 index 00000000..d52f2a44 --- /dev/null +++ b/packages/video/src/components/video-conference/types.ts @@ -0,0 +1,12 @@ +import { z } from 'zod'; + +import { Brand, BrandSchema } from '../../common/types/brand.types'; +import { ParticipantType } from '../../common/types/participant.types'; +import { Permissions } from '../../common/types/permissions.types'; + +export type VideoConferenceProps = { + brand?: Brand; + participantType?: ParticipantType | `${ParticipantType}`; + permissions?: Permissions; + styles?: string; +}; diff --git a/packages/video/src/index.test.ts b/packages/video/src/index.test.ts deleted file mode 100644 index 13a1a909..00000000 --- a/packages/video/src/index.test.ts +++ /dev/null @@ -1,7 +0,0 @@ - - -describe('video', () => { - it('should work', () => { - expect(true).toBe(true); - }); -}); \ No newline at end of file diff --git a/packages/video/src/index.ts b/packages/video/src/index.ts index cb0ff5c3..195b2b80 100644 --- a/packages/video/src/index.ts +++ b/packages/video/src/index.ts @@ -1 +1,2 @@ -export {}; +export { VideoConferenceProps } from './components/video-conference/types'; +export { VideoConference } from './components/video-conference';