@fraku/video 0.0.1 → 0.0.2

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 (43) hide show
  1. package/package.json +2 -3
  2. package/src/App.tsx +0 -7
  3. package/src/components/ZoomVideoPlugin/ZoomVideoPlugin.stories.tsx +0 -50
  4. package/src/components/ZoomVideoPlugin/ZoomVideoPlugin.tsx +0 -253
  5. package/src/components/ZoomVideoPlugin/components/ButtonsDock/ButtonsDock.tsx +0 -353
  6. package/src/components/ZoomVideoPlugin/components/ButtonsDock/DockButton.tsx +0 -90
  7. package/src/components/ZoomVideoPlugin/components/ButtonsDock/MenuItemTemplate.tsx +0 -35
  8. package/src/components/ZoomVideoPlugin/components/ButtonsDock/index.ts +0 -1
  9. package/src/components/ZoomVideoPlugin/components/MobileIconButton/MobileIconButton.tsx +0 -30
  10. package/src/components/ZoomVideoPlugin/components/MobileIconButton/index.ts +0 -1
  11. package/src/components/ZoomVideoPlugin/components/Overlay/Overlay.tsx +0 -74
  12. package/src/components/ZoomVideoPlugin/components/Overlay/index.ts +0 -1
  13. package/src/components/ZoomVideoPlugin/components/ParticipantsList.tsx +0 -52
  14. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/SettingsContent.tsx +0 -19
  15. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/SettingsMenu.tsx +0 -30
  16. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/SettingsOverlay.tsx +0 -52
  17. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/AudioSettings.tsx +0 -191
  18. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/BackgroundSettings.tsx +0 -47
  19. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/DropdownItemTemplate.tsx +0 -20
  20. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/DropdownValueTemplate.tsx +0 -12
  21. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/VideoSettings.tsx +0 -30
  22. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/context.ts +0 -20
  23. package/src/components/ZoomVideoPlugin/components/SettingsOverlay/index.ts +0 -1
  24. package/src/components/ZoomVideoPlugin/components/Video.tsx +0 -35
  25. package/src/components/ZoomVideoPlugin/constants.ts +0 -4
  26. package/src/components/ZoomVideoPlugin/context.ts +0 -86
  27. package/src/components/ZoomVideoPlugin/hooks/useClientMessages.ts +0 -142
  28. package/src/components/ZoomVideoPlugin/hooks/useDeviceSize.ts +0 -24
  29. package/src/components/ZoomVideoPlugin/hooks/useStartVideoOptions.ts +0 -14
  30. package/src/components/ZoomVideoPlugin/hooks/useZoomVideoPlayer.tsx +0 -142
  31. package/src/components/ZoomVideoPlugin/index.ts +0 -2
  32. package/src/components/ZoomVideoPlugin/lib/platforms.ts +0 -17
  33. package/src/components/ZoomVideoPlugin/pages/AfterSession.tsx +0 -14
  34. package/src/components/ZoomVideoPlugin/pages/MainSession.tsx +0 -53
  35. package/src/components/ZoomVideoPlugin/pages/PanelistsSession.tsx +0 -97
  36. package/src/components/ZoomVideoPlugin/pages/PreSessionConfiguration.tsx +0 -154
  37. package/src/components/ZoomVideoPlugin/types.global.d.ts +0 -15
  38. package/src/components/ZoomVideoPlugin/types.ts +0 -23
  39. package/src/global.d.ts +0 -46
  40. package/src/index.css +0 -4
  41. package/src/index.ts +0 -4
  42. package/src/main.tsx +0 -10
  43. package/src/vite-env.d.ts +0 -12
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fraku/video",
3
3
  "private": false,
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
@@ -16,8 +16,7 @@
16
16
  "./src": "./src/index.ts"
17
17
  },
18
18
  "files": [
19
- "dist",
20
- "src"
19
+ "dist"
21
20
  ],
22
21
  "scripts": {
23
22
  "dev": "vite",
package/src/App.tsx DELETED
@@ -1,7 +0,0 @@
1
- import './index.css'
2
-
3
- function App() {
4
- return <h2>Zoom Video Plugin</h2>
5
- }
6
-
7
- export default App
@@ -1,50 +0,0 @@
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'
@@ -1,253 +0,0 @@
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
@@ -1,353 +0,0 @@
1
- import { useRef, useState } from 'react'
2
- import { MenuItem, MenuItemOptions } from 'primereact/menuitem'
3
- import cn from 'classnames'
4
- import { Menu } from 'primereact/menu'
5
- import { Breakpoint } from './../../hooks/useDeviceSize'
6
- import Overlay from '../Overlay'
7
- import { SessionStep } from '../../types'
8
- import DockButton from './DockButton'
9
- import MenuItemTemplate from './MenuItemTemplate'
10
- import MobileIconButton from '../MobileIconButton'
11
- import SettingsOverlay from '../SettingsOverlay'
12
- import { SettingsTab } from '../SettingsOverlay/context'
13
- import { useZoomVideoContext } from '../../context'
14
-
15
- type DockItem = {
16
- component: JSX.Element
17
- mobileComponent?: JSX.Element
18
- group: number
19
- key: string
20
- show?: boolean
21
- }
22
-
23
- type ButtonsDockProps = {
24
- exit: () => void
25
- setIsCamOn: () => void
26
- setIsMicOn: () => void
27
- sessionStep: SessionStep
28
- setActiveMicrophone: (deviceId: string) => void
29
- setActiveCamera: (deviceId: string) => void
30
- setActiveAudioOutput: (deviceId: string) => void
31
- }
32
-
33
- const ButtonsDock = ({
34
- sessionStep,
35
- exit,
36
- setIsMicOn,
37
- setIsCamOn,
38
- setActiveMicrophone,
39
- setActiveCamera,
40
- setActiveAudioOutput
41
- }: ButtonsDockProps) => {
42
- const {
43
- audioOutputList,
44
- breakpoint,
45
- cameraList,
46
- isCamOn,
47
- isMicOn,
48
- micList,
49
- activeAudioOutput,
50
- activeMicrophone,
51
- activeCamera
52
- } = useZoomVideoContext()
53
-
54
- const isTabletOrDesktop = breakpoint >= Breakpoint.Tablet
55
- const [isSettingsOverlayVisible, setIsSettingsOverlayVisible] = useState({ visible: false, tab: SettingsTab.Audio })
56
- const [isMobileActionsOverlayVisible, setIsMobileActionsOverlayVisible] = useState(false)
57
- const isLocalSettingConfiguration = sessionStep === SessionStep.LocalSettingsConfiguration
58
- const audioMenuOptionsRef = useRef<Menu>(null)
59
- const videoMenuOptionsRef = useRef<Menu>(null)
60
- const micMenuOptionsRef = useRef<Menu>(null)
61
-
62
- const items: DockItem[] = [
63
- // Microphone Button
64
- {
65
- component: (
66
- <DockButton
67
- mainIcon={`fa-regular ${isMicOn ? 'fa-microphone' : 'fa-microphone-slash'}`}
68
- mainLabel={isLocalSettingConfiguration && isTabletOrDesktop ? 'Microphone' : undefined}
69
- mainTitle={isMicOn ? 'Mute' : 'Unmute'}
70
- onMainClick={setIsMicOn}
71
- showSecondary={isTabletOrDesktop}
72
- secondaryIcon="fa-regular fa-chevron-up"
73
- secondaryTitle="Microphone Settings"
74
- onSecondaryClick={(e) => micMenuOptionsRef.current?.toggle(e)}
75
- />
76
- ),
77
- group: 1,
78
- key: 'microphone',
79
- show: true
80
- },
81
- // Camera Button
82
- {
83
- component: (
84
- <DockButton
85
- mainIcon={`fa-regular ${isCamOn ? 'fa-video' : 'fa-video-slash'}`}
86
- mainLabel={isLocalSettingConfiguration && isTabletOrDesktop ? 'Camera' : undefined}
87
- mainTitle={isCamOn ? 'Turn off Camera' : 'Turn on Camera'}
88
- onMainClick={setIsCamOn}
89
- showSecondary={isTabletOrDesktop}
90
- secondaryIcon="fa-regular fa-chevron-up"
91
- secondaryTitle="Camera Settings"
92
- onSecondaryClick={(e) => videoMenuOptionsRef.current?.toggle(e)}
93
- />
94
- ),
95
- group: 1,
96
- key: 'camera',
97
- show: true
98
- },
99
- // Speakers Button
100
- {
101
- component: (
102
- <DockButton
103
- mainIcon="fa-regular fa-volume"
104
- mainLabel={isLocalSettingConfiguration && isTabletOrDesktop ? 'Speakers' : undefined}
105
- mainTitle="Speakers"
106
- onMainClick={(e) => audioMenuOptionsRef.current?.toggle(e)}
107
- showSecondary={isTabletOrDesktop}
108
- secondaryIcon="fa-regular fa-chevron-up"
109
- secondaryTitle="Speaker Settings"
110
- onSecondaryClick={(e) => audioMenuOptionsRef.current?.toggle(e)}
111
- />
112
- ),
113
- group: 1,
114
- key: 'speakers',
115
- show: isTabletOrDesktop
116
- },
117
- // Share Screen Button
118
- {
119
- component: <DockButton mainIcon="fa-regular fa-screencast" mainTitle="Share Screen" onMainClick={() => {}} />,
120
- mobileComponent: (
121
- <MobileIconButton
122
- icon="fa-regular fa-screencast"
123
- title="Share Screen"
124
- onClick={() => {
125
- setIsMobileActionsOverlayVisible(false)
126
- }}
127
- />
128
- ),
129
- group: 2,
130
- key: 'share-screen',
131
- show: !isLocalSettingConfiguration
132
- },
133
- // Raise Hand Button
134
- {
135
- component: <DockButton mainIcon="fa-regular fa-hand" mainTitle="Raise/Lower Hand" onMainClick={() => {}} />,
136
- mobileComponent: (
137
- <MobileIconButton
138
- icon="fa-regular fa-hand"
139
- title="Raise/Lower Hand"
140
- onClick={() => {
141
- setIsMobileActionsOverlayVisible(false)
142
- }}
143
- />
144
- ),
145
- group: 2,
146
- key: 'hand-raise',
147
- show: !isLocalSettingConfiguration
148
- },
149
- // Participants List Button
150
- {
151
- key: 'participants-list',
152
- component: <DockButton mainIcon="fa-regular fa-user-group" mainTitle="Participants" onMainClick={() => {}} />,
153
- mobileComponent: (
154
- <MobileIconButton
155
- icon="fa-regular fa-user-group"
156
- title="Participants"
157
- onClick={() => {
158
- setIsMobileActionsOverlayVisible(false)
159
- }}
160
- />
161
- ),
162
- group: 2,
163
- show: !isLocalSettingConfiguration
164
- },
165
- // Chat Button
166
- {
167
- component: <DockButton mainIcon="fa-regular fa-comment-lines" mainTitle="Chat" onMainClick={() => {}} />,
168
- mobileComponent: (
169
- <MobileIconButton
170
- icon="fa-regular fa-comment-lines"
171
- title="Chat"
172
- onClick={() => {
173
- setIsMobileActionsOverlayVisible(false)
174
- }}
175
- />
176
- ),
177
- group: 2,
178
- key: 'chat',
179
- show: !isLocalSettingConfiguration
180
- },
181
- // Settings Button
182
- {
183
- component: (
184
- <DockButton
185
- mainIcon="fa-regular fa-gear"
186
- mainLabel={isLocalSettingConfiguration && isTabletOrDesktop ? 'Settings' : undefined}
187
- mainTitle="Settings"
188
- onMainClick={() => setIsSettingsOverlayVisible({ visible: true, tab: SettingsTab.Audio })}
189
- />
190
- ),
191
- mobileComponent: (
192
- <MobileIconButton
193
- icon="fa-regular fa-gear"
194
- title="Settings"
195
- onClick={() => {
196
- setIsMobileActionsOverlayVisible(false)
197
- setIsSettingsOverlayVisible({ visible: true, tab: SettingsTab.Audio })
198
- }}
199
- />
200
- ),
201
- group: 2,
202
- key: 'settings',
203
- show: true
204
- },
205
- {
206
- component: (
207
- <DockButton
208
- mainIcon="fa-regular fa-phone-hangup !text-white"
209
- mainTitle="Leave Meeting"
210
- onMainClick={exit}
211
- className="!bg-danger-50"
212
- />
213
- ),
214
- group: 3,
215
- key: 'leave',
216
- show: !isLocalSettingConfiguration
217
- }
218
- ]
219
-
220
- const renderGroup = (group: number, justify: 'start' | 'center' | 'end') => {
221
- const filteredItems = items.filter((item) => item.group === group && item.show)
222
- if (filteredItems.length === 0) return null
223
-
224
- return (
225
- <div className={cn('flex items-center gap-16', `justify-${justify}`)}>
226
- {filteredItems.map((item) => (
227
- <span key={item.key}>{item.component}</span>
228
- ))}
229
- </div>
230
- )
231
- }
232
-
233
- const renderElipsisButton = () => {
234
- return (
235
- <div className="relative">
236
- <DockButton
237
- mainIcon="fa-regular fa-ellipsis-v"
238
- mainTitle="More Actions"
239
- onMainClick={() => setIsMobileActionsOverlayVisible(true)}
240
- />
241
- </div>
242
- )
243
- }
244
-
245
- const audioMenuItems: MenuItem[] = audioOutputList
246
- .map((device) => ({
247
- id: device.deviceId,
248
- label: device.label,
249
- command: () => setActiveAudioOutput(device.deviceId),
250
- template: (item: MenuItem, options: MenuItemOptions) => (
251
- <MenuItemTemplate item={item} options={options} activeItem={activeAudioOutput} />
252
- )
253
- }))
254
- .concat([
255
- {
256
- label: 'Audio Settings',
257
- id: 'audio-settings',
258
- command: () => {
259
- setIsSettingsOverlayVisible({ visible: true, tab: SettingsTab.Audio })
260
- },
261
- template: (item: MenuItem, options: MenuItemOptions) => (
262
- <MenuItemTemplate item={item} options={options} activeItem={activeAudioOutput} />
263
- )
264
- }
265
- ])
266
-
267
- const videoMenuItems: MenuItem[] = cameraList
268
- .map((device) => ({
269
- label: device.label,
270
- id: device.deviceId,
271
- template: (item: MenuItem, options: MenuItemOptions) => (
272
- <MenuItemTemplate item={item} options={options} activeItem={activeCamera} />
273
- ),
274
- command: () => setActiveCamera(device.deviceId)
275
- }))
276
- .concat([
277
- {
278
- label: 'Camera Settings',
279
- id: 'camera-settings',
280
- command: () => {
281
- setIsSettingsOverlayVisible({ visible: true, tab: SettingsTab.Video })
282
- },
283
- template: (item: MenuItem, options: MenuItemOptions) => (
284
- <MenuItemTemplate item={item} options={options} activeItem={activeCamera} />
285
- )
286
- }
287
- ])
288
-
289
- const micMenuItems: MenuItem[] = micList
290
- .map((device) => ({
291
- id: device.deviceId,
292
- label: device.label,
293
- command: () => setActiveMicrophone(device.deviceId),
294
- template: (item: MenuItem, options: MenuItemOptions) => (
295
- <MenuItemTemplate item={item} options={options} activeItem={activeMicrophone} />
296
- )
297
- }))
298
- .concat([
299
- {
300
- id: 'microphone-settings',
301
- label: 'Microphone Settings',
302
- command: () => {
303
- setIsSettingsOverlayVisible({ visible: true, tab: SettingsTab.Audio })
304
- },
305
- template: (item: MenuItem, options: MenuItemOptions) => (
306
- <MenuItemTemplate item={item} options={options} activeItem={activeMicrophone} />
307
- )
308
- }
309
- ])
310
-
311
- return (
312
- <>
313
- <div
314
- className={cn(
315
- 'flex items-center justify-between rounded-full px-16 py-12 w-full shadow-medium overflow-hidden',
316
- { 'gap-16 !w-fit': isLocalSettingConfiguration }
317
- )}
318
- >
319
- {renderGroup(1, 'start')}
320
- {isTabletOrDesktop ? renderGroup(2, 'center') : renderElipsisButton()}
321
- {renderGroup(3, 'end')}
322
- </div>
323
-
324
- <SettingsOverlay
325
- breakpoint={breakpoint}
326
- onHide={() => setIsSettingsOverlayVisible({ ...isSettingsOverlayVisible, visible: false })}
327
- visible={isSettingsOverlayVisible.visible}
328
- defaultTab={isSettingsOverlayVisible.tab}
329
- />
330
-
331
- {/* Mobile Actions Overlay */}
332
- <Overlay
333
- visible={isMobileActionsOverlayVisible}
334
- onHide={() => setIsMobileActionsOverlayVisible(false)}
335
- header="Options"
336
- breakpoint={breakpoint}
337
- >
338
- <div className="flex flex-col gap-16 px-16 py-8 font-bold">
339
- {items
340
- .filter((item) => item.key !== 'more-actions' && item.group === 2)
341
- .map((item) => (
342
- <span key={item.key}>{item.mobileComponent ?? item.component}</span>
343
- ))}
344
- </div>
345
- </Overlay>
346
- <Menu model={audioMenuItems} popup ref={audioMenuOptionsRef} id="popup_menu" popupAlignment="left" />
347
- <Menu model={videoMenuItems} popup ref={videoMenuOptionsRef} id="popup_menu" popupAlignment="left" />
348
- <Menu model={micMenuItems} popup ref={micMenuOptionsRef} id="popup_menu" popupAlignment="left" />
349
- </>
350
- )
351
- }
352
-
353
- export default ButtonsDock