@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.
- package/package.json +2 -3
- package/src/App.tsx +0 -7
- package/src/components/ZoomVideoPlugin/ZoomVideoPlugin.stories.tsx +0 -50
- package/src/components/ZoomVideoPlugin/ZoomVideoPlugin.tsx +0 -253
- package/src/components/ZoomVideoPlugin/components/ButtonsDock/ButtonsDock.tsx +0 -353
- package/src/components/ZoomVideoPlugin/components/ButtonsDock/DockButton.tsx +0 -90
- package/src/components/ZoomVideoPlugin/components/ButtonsDock/MenuItemTemplate.tsx +0 -35
- package/src/components/ZoomVideoPlugin/components/ButtonsDock/index.ts +0 -1
- package/src/components/ZoomVideoPlugin/components/MobileIconButton/MobileIconButton.tsx +0 -30
- package/src/components/ZoomVideoPlugin/components/MobileIconButton/index.ts +0 -1
- package/src/components/ZoomVideoPlugin/components/Overlay/Overlay.tsx +0 -74
- package/src/components/ZoomVideoPlugin/components/Overlay/index.ts +0 -1
- package/src/components/ZoomVideoPlugin/components/ParticipantsList.tsx +0 -52
- package/src/components/ZoomVideoPlugin/components/SettingsOverlay/SettingsContent.tsx +0 -19
- package/src/components/ZoomVideoPlugin/components/SettingsOverlay/SettingsMenu.tsx +0 -30
- package/src/components/ZoomVideoPlugin/components/SettingsOverlay/SettingsOverlay.tsx +0 -52
- package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/AudioSettings.tsx +0 -191
- package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/BackgroundSettings.tsx +0 -47
- package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/DropdownItemTemplate.tsx +0 -20
- package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/DropdownValueTemplate.tsx +0 -12
- package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/VideoSettings.tsx +0 -30
- package/src/components/ZoomVideoPlugin/components/SettingsOverlay/context.ts +0 -20
- package/src/components/ZoomVideoPlugin/components/SettingsOverlay/index.ts +0 -1
- package/src/components/ZoomVideoPlugin/components/Video.tsx +0 -35
- package/src/components/ZoomVideoPlugin/constants.ts +0 -4
- package/src/components/ZoomVideoPlugin/context.ts +0 -86
- package/src/components/ZoomVideoPlugin/hooks/useClientMessages.ts +0 -142
- package/src/components/ZoomVideoPlugin/hooks/useDeviceSize.ts +0 -24
- package/src/components/ZoomVideoPlugin/hooks/useStartVideoOptions.ts +0 -14
- package/src/components/ZoomVideoPlugin/hooks/useZoomVideoPlayer.tsx +0 -142
- package/src/components/ZoomVideoPlugin/index.ts +0 -2
- package/src/components/ZoomVideoPlugin/lib/platforms.ts +0 -17
- package/src/components/ZoomVideoPlugin/pages/AfterSession.tsx +0 -14
- package/src/components/ZoomVideoPlugin/pages/MainSession.tsx +0 -53
- package/src/components/ZoomVideoPlugin/pages/PanelistsSession.tsx +0 -97
- package/src/components/ZoomVideoPlugin/pages/PreSessionConfiguration.tsx +0 -154
- package/src/components/ZoomVideoPlugin/types.global.d.ts +0 -15
- package/src/components/ZoomVideoPlugin/types.ts +0 -23
- package/src/global.d.ts +0 -46
- package/src/index.css +0 -4
- package/src/index.ts +0 -4
- package/src/main.tsx +0 -10
- 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.
|
|
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,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
|