@fraku/video 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +2 -0
  2. package/package.json +75 -0
  3. package/src/App.tsx +7 -0
  4. package/src/components/ZoomVideoPlugin/ZoomVideoPlugin.stories.tsx +50 -0
  5. package/src/components/ZoomVideoPlugin/ZoomVideoPlugin.tsx +253 -0
  6. package/src/components/ZoomVideoPlugin/components/ButtonsDock/ButtonsDock.tsx +353 -0
  7. package/src/components/ZoomVideoPlugin/components/ButtonsDock/DockButton.tsx +90 -0
  8. package/src/components/ZoomVideoPlugin/components/ButtonsDock/MenuItemTemplate.tsx +35 -0
  9. package/src/components/ZoomVideoPlugin/components/ButtonsDock/index.ts +1 -0
  10. package/src/components/ZoomVideoPlugin/components/MobileIconButton/MobileIconButton.tsx +30 -0
  11. package/src/components/ZoomVideoPlugin/components/MobileIconButton/index.ts +1 -0
  12. package/src/components/ZoomVideoPlugin/components/Overlay/Overlay.tsx +74 -0
  13. package/src/components/ZoomVideoPlugin/components/Overlay/index.ts +1 -0
  14. package/src/components/ZoomVideoPlugin/components/ParticipantsList.tsx +52 -0
  15. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/SettingsContent.tsx +19 -0
  16. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/SettingsMenu.tsx +30 -0
  17. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/SettingsOverlay.tsx +52 -0
  18. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/AudioSettings.tsx +191 -0
  19. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/BackgroundSettings.tsx +47 -0
  20. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/DropdownItemTemplate.tsx +20 -0
  21. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/DropdownValueTemplate.tsx +12 -0
  22. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/VideoSettings.tsx +30 -0
  23. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/context.ts +20 -0
  24. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/index.ts +1 -0
  25. package/src/components/ZoomVideoPlugin/components/Video.tsx +35 -0
  26. package/src/components/ZoomVideoPlugin/constants.ts +4 -0
  27. package/src/components/ZoomVideoPlugin/context.ts +86 -0
  28. package/src/components/ZoomVideoPlugin/hooks/useClientMessages.ts +142 -0
  29. package/src/components/ZoomVideoPlugin/hooks/useDeviceSize.ts +24 -0
  30. package/src/components/ZoomVideoPlugin/hooks/useStartVideoOptions.ts +14 -0
  31. package/src/components/ZoomVideoPlugin/hooks/useZoomVideoPlayer.tsx +142 -0
  32. package/src/components/ZoomVideoPlugin/index.ts +2 -0
  33. package/src/components/ZoomVideoPlugin/lib/platforms.ts +17 -0
  34. package/src/components/ZoomVideoPlugin/pages/AfterSession.tsx +14 -0
  35. package/src/components/ZoomVideoPlugin/pages/MainSession.tsx +53 -0
  36. package/src/components/ZoomVideoPlugin/pages/PanelistsSession.tsx +97 -0
  37. package/src/components/ZoomVideoPlugin/pages/PreSessionConfiguration.tsx +154 -0
  38. package/src/components/ZoomVideoPlugin/types.global.d.ts +15 -0
  39. package/src/components/ZoomVideoPlugin/types.ts +23 -0
  40. package/src/global.d.ts +46 -0
  41. package/src/index.css +4 -0
  42. package/src/index.ts +4 -0
  43. package/src/main.tsx +10 -0
  44. package/src/vite-env.d.ts +12 -0
package/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # fraku-video
2
+ Module of fraku, for integrate Video in Voting and Admin
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@fraku/video",
3
+ "private": false,
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./dist/index.css": "./dist/index.css",
16
+ "./src": "./src/index.ts"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "src"
21
+ ],
22
+ "scripts": {
23
+ "dev": "vite",
24
+ "build": "tsc && vite build",
25
+ "build:css": "tailwindcss -c ./tailwind.config.js -i ./src/index.css -o ./dist/index.css --minify",
26
+ "watch:css": "tailwindcss -c ./tailwind.config.js -i ./src/index.css -o ./dist/index.css --watch",
27
+ "lint": "tsc --noEmit && eslint . --ext ts,tsx --report-unused-disable-directives",
28
+ "preview": "vite preview",
29
+ "prepublishOnly": "npm run build",
30
+ "storybook": "storybook dev -p 6006 --no-open",
31
+ "test": "react-app-rewired test"
32
+ },
33
+ "peerDependencies": {
34
+ "react": "^18.2.0",
35
+ "react-dom": "^18.2.0"
36
+ },
37
+ "dependencies": {
38
+ "@zoom/videosdk": "^2.2.12",
39
+ "classnames": "^2.5.1",
40
+ "primeicons": "^7.0.0",
41
+ "primereact": "^10.9.6",
42
+ "react-use": "^17.6.0"
43
+ },
44
+ "devDependencies": {
45
+ "@storybook/react-vite": "^9.1.13",
46
+ "@types/node": "^24.8.1",
47
+ "@types/react": "^18.2.43",
48
+ "@types/react-dom": "^18.2.17",
49
+ "@typescript-eslint/eslint-plugin": "^6.14.0",
50
+ "@typescript-eslint/parser": "^6.14.0",
51
+ "@vitejs/plugin-react": "^4.2.1",
52
+ "autoprefixer": "^10.4.21",
53
+ "eslint": "^8.55.0",
54
+ "eslint-plugin-react-hooks": "^4.6.0",
55
+ "eslint-plugin-react-refresh": "^0.4.5",
56
+ "eslint-plugin-storybook": "^9.1.13",
57
+ "postcss": "^8.5.6",
58
+ "react": "^18.2.0",
59
+ "react-dom": "^18.2.0",
60
+ "storybook": "^9.1.13",
61
+ "tailwindcss": "^3.4.17",
62
+ "typescript": "^5.2.2",
63
+ "vite": "^5.0.8",
64
+ "vite-plugin-dts": "^3.6.4"
65
+ },
66
+ "keywords": [
67
+ "react",
68
+ "typescript",
69
+ "component",
70
+ "library"
71
+ ],
72
+ "author": "",
73
+ "license": "MIT",
74
+ "description": "A React TypeScript component library"
75
+ }
package/src/App.tsx ADDED
@@ -0,0 +1,7 @@
1
+ import './index.css'
2
+
3
+ function App() {
4
+ return <h2>Zoom Video Plugin</h2>
5
+ }
6
+
7
+ export default App
@@ -0,0 +1,50 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import ZoomVideoPlugin from './ZoomVideoPlugin'
3
+ import type { Credentials } from './types'
4
+
5
+ // Load credentials from environment variables
6
+ const credentials: Credentials = {
7
+ sessionName: import.meta.env.VITE_ZOOM_SESSION_NAME || '',
8
+ signature: import.meta.env.VITE_ZOOM_SIGNATURE || '',
9
+ userName: import.meta.env.VITE_ZOOM_USER_NAME || 'Storybook User',
10
+ sessionPasscode: import.meta.env.VITE_ZOOM_SESSION_PASSCODE || ''
11
+ }
12
+
13
+ const meta = {
14
+ title: 'Components/ZoomVideoPlugin',
15
+ component: ZoomVideoPlugin,
16
+ parameters: {
17
+ layout: 'fullscreen'
18
+ },
19
+ tags: ['autodocs'],
20
+ argTypes: {
21
+ credentials: {
22
+ description: 'Zoom Video SDK credentials for the session',
23
+ control: 'object'
24
+ },
25
+ closeParentContainer: {
26
+ description: 'Callback function to close the parent container',
27
+ action: 'closeParentContainer'
28
+ },
29
+ setIsCloseButtonVisible: {
30
+ description: 'Setter for close button visibility',
31
+ action: 'setIsCloseButtonVisible'
32
+ }
33
+ }
34
+ } satisfies Meta<typeof ZoomVideoPlugin>
35
+
36
+ export default meta
37
+ type Story = StoryObj<typeof meta>
38
+
39
+ export const Default: Story = {
40
+ args: {
41
+ credentials,
42
+ closeParentContainer: () => {
43
+ console.log('Close parent container called')
44
+ },
45
+ setIsCloseButtonVisible: (value: boolean | ((prev: boolean) => boolean)) => {
46
+ console.log('Set close button visible:', value)
47
+ }
48
+ }
49
+ }
50
+ Default.storyName = 'ZoomVideoPlugin'
@@ -0,0 +1,253 @@
1
+ import { useCallback, useEffect, useMemo, useState } from 'react'
2
+ import ZoomVideo, { ActiveSpeaker, ConnectionState, MediaDevice, type Participant, type Stream } from '@zoom/videosdk'
3
+ import { useEffectOnce } from 'react-use'
4
+ import PreSessionConfiguration from './pages/PreSessionConfiguration'
5
+ import PanelistsSession from './pages/PanelistsSession'
6
+ import MainSession from './pages/MainSession'
7
+ import AfterSession from './pages/AfterSession'
8
+ import { useClientMessages } from './hooks/useClientMessages'
9
+ import { zmClient, ZoomVideoContext, ZoomVideoContextType } from './context'
10
+ import { SessionStep, type ZoomVideoPluginProps } from './types'
11
+ import { useContainerSize } from './hooks/useDeviceSize'
12
+
13
+ const fetchDevices = async () => {
14
+ try {
15
+ const devices: MediaDeviceInfo[] = await ZoomVideo.getDevices()
16
+
17
+ const micList = devices.filter((d) => d.kind === 'audioinput')
18
+ const speakerList = devices.filter((d) => d.kind === 'audiooutput')
19
+ const cameraList = devices.filter((d) => d.kind === 'videoinput')
20
+
21
+ return { micList, speakerList, cameraList }
22
+ } catch (e) {
23
+ return { micList: [], speakerList: [], cameraList: [] }
24
+ }
25
+ }
26
+
27
+ const ZoomVideoPlugin = ({ credentials, closeParentContainer, setIsCloseButtonVisible }: ZoomVideoPluginProps) => {
28
+ const { ref, breakpoint } = useContainerSize()
29
+ const [isBlurred, setIsBlurred] = useState(false)
30
+ const [mediaStream, setMediaStream] = useState<typeof Stream | null>(null)
31
+ const [sessionStep, setSessionStep] = useState<SessionStep | null>(null)
32
+ const [participants, setParticipants] = useState<Participant[]>([])
33
+ const [activeVideoId, setActiveVideoId] = useState<number | null>(null)
34
+ const [activeSpeakers, setActiveSpeakers] = useState<ActiveSpeaker[]>([])
35
+ const [connectionState, setConnectionState] = useState<ConnectionState | null>(null)
36
+
37
+ const [localAudio, setLocalAudio] = useState(() => ZoomVideo.createLocalAudioTrack())
38
+ const [localVideo, setLocalVideo] = useState(() => ZoomVideo.createLocalVideoTrack())
39
+ const [activeMicrophone, setActiveMicrophone] = useState<string | undefined>()
40
+ const [activeAudioOutput, setActiveAudioOutput] = useState<string | undefined>()
41
+ const [activeCamera, setActiveCamera] = useState<string | undefined>()
42
+ const [micList, setMicList] = useState<MediaDevice[]>([])
43
+ const [audioOutputList, setAudioOutputList] = useState<MediaDevice[]>([])
44
+ const [cameraList, setCameraList] = useState<MediaDevice[]>([])
45
+ const [isCamOn, setIsCamOn] = useState(true)
46
+ const [isMicOn, setIsMicOn] = useState(true)
47
+
48
+ const initSession = useCallback(async () => {
49
+ if (connectionState === ConnectionState.Closed) return
50
+ try {
51
+ await zmClient.init('en-US', 'Global', {
52
+ webEndpoint: window?.webEndpoint ?? 'zoom.us',
53
+ stayAwake: true,
54
+ patchJsMedia: true,
55
+ leaveOnPageUnload: false,
56
+ enforceMultipleVideos: true // Enforces multiple videos if true (up to 3 videos of others and 1 video of self) on platform without SharedArrayBuffer.
57
+ })
58
+
59
+ if (zmClient.getSessionInfo()) {
60
+ setSessionStep(SessionStep.LocalSettingsConfiguration)
61
+ setIsCloseButtonVisible(true)
62
+ }
63
+ } catch (e) {
64
+ console.log('error init: ', e)
65
+ }
66
+ }, [connectionState, setIsCloseButtonVisible])
67
+
68
+ const joinSession = useCallback(async () => {
69
+ if (connectionState === ConnectionState.Closed) return
70
+ try {
71
+ const { sessionName, signature, userName, sessionPasscode } = credentials ?? {}
72
+ if (!sessionName || !signature || !userName || !sessionPasscode) {
73
+ return
74
+ }
75
+
76
+ await zmClient.join(sessionName, signature, userName, sessionPasscode)
77
+ const stream = zmClient.getMediaStream()
78
+ setMediaStream(stream)
79
+ setSessionStep(SessionStep.OnlyPanelistsSession)
80
+ setIsCloseButtonVisible(false)
81
+ } catch (e: any) {
82
+ console.log('error join: ', e)
83
+ }
84
+ }, [connectionState, credentials, setIsCloseButtonVisible])
85
+
86
+ const startMainSession = useCallback(() => {
87
+ setSessionStep(SessionStep.MainSession)
88
+ setIsCloseButtonVisible(false)
89
+ }, [setIsCloseButtonVisible])
90
+
91
+ const stopSession = useCallback(async () => {
92
+ console.log('Stopping session...')
93
+
94
+ if (zmClient.getSessionInfo()?.isInMeeting) {
95
+ try {
96
+ await zmClient?.leave()
97
+ ZoomVideo.destroyClient()
98
+ } catch (e) {
99
+ console.warn('Leave session failed', e)
100
+ }
101
+ }
102
+ await localAudio?.stop().catch(console.warn)
103
+ await localVideo?.stop().catch(console.warn)
104
+
105
+ setMediaStream(null)
106
+
107
+ setSessionStep(SessionStep.AfterSession)
108
+ setIsCloseButtonVisible(true)
109
+ }, [localAudio, localVideo, setIsCloseButtonVisible])
110
+
111
+ const switchActiveMicrophone = useCallback(
112
+ async (deviceId: string) => {
113
+ if (sessionStep === SessionStep.LocalSettingsConfiguration) {
114
+ if (!localAudio) return
115
+ if (!isMicOn) return
116
+ await localAudio.stop().catch(console.warn)
117
+ setLocalAudio(ZoomVideo.createLocalAudioTrack(deviceId))
118
+ await localAudio.start().catch(console.warn)
119
+ setActiveMicrophone(deviceId)
120
+ } else {
121
+ if (!mediaStream) return
122
+ await mediaStream.switchMicrophone(deviceId)
123
+ setActiveMicrophone(deviceId)
124
+ }
125
+ },
126
+ [isMicOn, localAudio, mediaStream, sessionStep]
127
+ )
128
+
129
+ const switchActiveAudioOutput = useCallback(
130
+ async (deviceId: string) => {
131
+ setActiveAudioOutput(deviceId)
132
+ if (!mediaStream) return
133
+ await mediaStream.switchSpeaker(deviceId).catch(console.warn)
134
+ },
135
+ [mediaStream]
136
+ )
137
+
138
+ const doRestart = useCallback(async () => {
139
+ setConnectionState(null)
140
+ setMediaStream(null)
141
+ setParticipants([])
142
+ setActiveVideoId(null)
143
+ await initSession()
144
+ }, [initSession])
145
+
146
+ useClientMessages({
147
+ zmClient,
148
+ mediaStream,
149
+ setActiveMicrophone,
150
+ setActiveAudioOutput,
151
+ setActiveCamera,
152
+ setMicList,
153
+ setAudioOutputList,
154
+ setCameraList,
155
+ setConnectionState,
156
+ setIsCamOn,
157
+ setIsMicOn,
158
+ setParticipants,
159
+ setActiveVideoId,
160
+ setActiveSpeakers,
161
+ setHandRaises: () => {}
162
+ })
163
+
164
+ useEffect(() => {
165
+ if (!credentials?.signature) return
166
+ initSession()
167
+ }, [initSession, credentials?.signature])
168
+
169
+ useEffectOnce(() => {
170
+ fetchDevices().then(({ micList, speakerList, cameraList }) => {
171
+ setMicList(micList)
172
+ setAudioOutputList(speakerList)
173
+ setCameraList(cameraList)
174
+ setActiveMicrophone(micList[0]?.deviceId)
175
+ setActiveAudioOutput(speakerList[0]?.deviceId)
176
+ setActiveCamera(cameraList[0]?.deviceId)
177
+ })
178
+ })
179
+
180
+ const ctxValue = useMemo<ZoomVideoContextType>(() => {
181
+ return {
182
+ activeCamera,
183
+ activeMicrophone,
184
+ activeAudioOutput,
185
+ activeSpeakers,
186
+ activeVideoId,
187
+ audioOutputList,
188
+ breakpoint,
189
+ cameraList,
190
+ closeParentContainer,
191
+ connectionState,
192
+ isBlurred,
193
+ isCamOn,
194
+ isMicOn,
195
+ localAudio,
196
+ localVideo,
197
+ mediaStream,
198
+ micList,
199
+ participants,
200
+ setActiveCamera,
201
+ setCameraList,
202
+ setIsBlurred,
203
+ setIsCamOn,
204
+ setIsMicOn,
205
+ setLocalAudio,
206
+ setLocalVideo,
207
+ setMediaStream,
208
+ setMicList,
209
+ setParticipants,
210
+ switchActiveAudioOutput,
211
+ switchActiveMicrophone,
212
+ stopSession,
213
+ zmClient
214
+ }
215
+ }, [
216
+ activeCamera,
217
+ activeMicrophone,
218
+ activeAudioOutput,
219
+ activeSpeakers,
220
+ activeVideoId,
221
+ audioOutputList,
222
+ breakpoint,
223
+ cameraList,
224
+ closeParentContainer,
225
+ connectionState,
226
+ isBlurred,
227
+ isCamOn,
228
+ isMicOn,
229
+ localAudio,
230
+ localVideo,
231
+ mediaStream,
232
+ micList,
233
+ participants,
234
+ stopSession,
235
+ switchActiveAudioOutput,
236
+ switchActiveMicrophone
237
+ ])
238
+
239
+ return (
240
+ <ZoomVideoContext.Provider value={ctxValue}>
241
+ <div ref={ref} className="w-full h-full @container">
242
+ {sessionStep === SessionStep.LocalSettingsConfiguration && (
243
+ <PreSessionConfiguration joinSession={joinSession} />
244
+ )}
245
+ {sessionStep === SessionStep.OnlyPanelistsSession && <PanelistsSession initMainSession={startMainSession} />}
246
+ {sessionStep === SessionStep.MainSession && <MainSession />}
247
+ {sessionStep === SessionStep.AfterSession && <AfterSession restartSession={doRestart} />}
248
+ </div>
249
+ </ZoomVideoContext.Provider>
250
+ )
251
+ }
252
+
253
+ export default ZoomVideoPlugin