@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
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import cn from 'classnames'
|
|
3
|
-
|
|
4
|
-
type DockButtonProps = {
|
|
5
|
-
className?: string
|
|
6
|
-
// Main action (always required)
|
|
7
|
-
mainIcon: string
|
|
8
|
-
mainLabel?: string
|
|
9
|
-
mainTitle?: string
|
|
10
|
-
onMainClick: (e: React.MouseEvent<HTMLButtonElement>) => void
|
|
11
|
-
// Optional secondary action
|
|
12
|
-
showSecondary?: boolean
|
|
13
|
-
secondaryIcon?: string
|
|
14
|
-
secondaryTitle?: string
|
|
15
|
-
onSecondaryClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Renders a customizable dock button component with optional main and secondary actions.
|
|
20
|
-
*
|
|
21
|
-
* @param className - Additional CSS class names to apply to the container.
|
|
22
|
-
* @param mainIcon - The CSS class for the main icon to display.
|
|
23
|
-
* @param mainLabel - Optional label text for the main button.
|
|
24
|
-
* @param mainTitle - The title attribute for the main button (for accessibility/tooltips).
|
|
25
|
-
* @param onMainClick - Click handler for the main button action.
|
|
26
|
-
* @param showSecondary - Flag to indicate if the secondary button should be displayed.
|
|
27
|
-
* @param secondaryIcon - Optional CSS class for the secondary icon to display.
|
|
28
|
-
* @param secondaryTitle - The title attribute for the secondary button (for accessibility/tooltips).
|
|
29
|
-
* @param onSecondaryClick - Optional click handler for the secondary button action.
|
|
30
|
-
*
|
|
31
|
-
* @returns A dock button component with main and optional secondary actions.
|
|
32
|
-
*/
|
|
33
|
-
const DockButton = ({
|
|
34
|
-
className,
|
|
35
|
-
mainIcon,
|
|
36
|
-
mainLabel,
|
|
37
|
-
mainTitle,
|
|
38
|
-
onMainClick,
|
|
39
|
-
secondaryIcon,
|
|
40
|
-
secondaryTitle,
|
|
41
|
-
onSecondaryClick,
|
|
42
|
-
showSecondary = false
|
|
43
|
-
}: DockButtonProps) => {
|
|
44
|
-
const hasLabel = Boolean(mainLabel)
|
|
45
|
-
const hasSecondary = showSecondary && secondaryIcon && onSecondaryClick
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<div
|
|
49
|
-
className={cn(
|
|
50
|
-
'flex items-center bg-neutral-85 h-40 rounded-full',
|
|
51
|
-
{
|
|
52
|
-
'justify-center min-w-40': !hasLabel && !hasSecondary,
|
|
53
|
-
'justify-between gap-12 px-4': hasSecondary,
|
|
54
|
-
'px-12': hasLabel && !hasSecondary
|
|
55
|
-
},
|
|
56
|
-
className
|
|
57
|
-
)}
|
|
58
|
-
>
|
|
59
|
-
{/* Main action */}
|
|
60
|
-
<button
|
|
61
|
-
className={cn('dock-button-main flex items-center justify-center', {
|
|
62
|
-
'p-8 h-full': hasSecondary,
|
|
63
|
-
'w-full h-40': !hasSecondary
|
|
64
|
-
})}
|
|
65
|
-
onClick={onMainClick}
|
|
66
|
-
title={mainTitle}
|
|
67
|
-
type="button"
|
|
68
|
-
>
|
|
69
|
-
<span className="flex items-center justify-center w-24 h-24">
|
|
70
|
-
<i className={mainIcon} aria-hidden="true" />
|
|
71
|
-
</span>
|
|
72
|
-
{hasLabel && <span className="ml-4">{mainLabel}</span>}
|
|
73
|
-
</button>
|
|
74
|
-
|
|
75
|
-
{/* Secondary action (optional) */}
|
|
76
|
-
{hasSecondary && (
|
|
77
|
-
<button
|
|
78
|
-
className="dock-button-secondary flex items-center justify-center w-32 h-full hover:bg-neutral-95 rounded-full"
|
|
79
|
-
onClick={onSecondaryClick}
|
|
80
|
-
title={secondaryTitle}
|
|
81
|
-
type="button"
|
|
82
|
-
>
|
|
83
|
-
<i className={secondaryIcon} aria-hidden="true" />
|
|
84
|
-
</button>
|
|
85
|
-
)}
|
|
86
|
-
</div>
|
|
87
|
-
)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export default DockButton
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { MenuItem, MenuItemOptions } from 'primereact/menuitem'
|
|
2
|
-
|
|
3
|
-
type Props = {
|
|
4
|
-
item: MenuItem
|
|
5
|
-
options: MenuItemOptions
|
|
6
|
-
activeItem: string | undefined
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const MenuItemTemplate = ({ item, options, activeItem }: Props) => {
|
|
10
|
-
const isActive = item.id === activeItem
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<div className="p-menuitem-content" data-pc-section="content">
|
|
14
|
-
<a
|
|
15
|
-
aria-label={item.label}
|
|
16
|
-
className={options.className}
|
|
17
|
-
data-test={item?.data?.dataTestId}
|
|
18
|
-
data-pc-section="action"
|
|
19
|
-
href="#"
|
|
20
|
-
onClick={options.onClick}
|
|
21
|
-
>
|
|
22
|
-
{isActive && (
|
|
23
|
-
<span className={options.iconClassName} data-pc-section="icon">
|
|
24
|
-
<i className="fa-regular fa-check" aria-hidden="true" />
|
|
25
|
-
</span>
|
|
26
|
-
)}
|
|
27
|
-
<span className={options.labelClassName} data-pc-section="label">
|
|
28
|
-
{item.label}
|
|
29
|
-
</span>
|
|
30
|
-
</a>
|
|
31
|
-
</div>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export default MenuItemTemplate
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './ButtonsDock'
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import cn from 'classnames'
|
|
2
|
-
|
|
3
|
-
type MobileIconButtonProps = {
|
|
4
|
-
className?: string
|
|
5
|
-
icon: string
|
|
6
|
-
onClick: () => void
|
|
7
|
-
title?: string
|
|
8
|
-
id?: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const MobileIconButton = ({ className, icon, onClick, title, id }: MobileIconButtonProps) => {
|
|
12
|
-
return (
|
|
13
|
-
<button
|
|
14
|
-
aria-label={title}
|
|
15
|
-
className={cn(
|
|
16
|
-
'flex items-center justify-start gap-4 w-full h-40 px-8 rounded-l border border-neutral-80 hover:bg-neutral-85',
|
|
17
|
-
className
|
|
18
|
-
)}
|
|
19
|
-
id={id}
|
|
20
|
-
onClick={onClick}
|
|
21
|
-
>
|
|
22
|
-
<span className="flex items-center justify-center w-24 h-24">
|
|
23
|
-
<i className={icon} aria-hidden="true" />
|
|
24
|
-
</span>
|
|
25
|
-
<span className="flex-1 text-left">{title}</span>
|
|
26
|
-
</button>
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export default MobileIconButton
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './MobileIconButton'
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { Dialog } from 'primereact/dialog'
|
|
2
|
-
import { Sidebar } from 'primereact/sidebar'
|
|
3
|
-
import { Breakpoint } from '../../hooks/useDeviceSize'
|
|
4
|
-
|
|
5
|
-
type OverlayProps = {
|
|
6
|
-
breakpoint: Breakpoint
|
|
7
|
-
children: React.ReactNode
|
|
8
|
-
className?: string
|
|
9
|
-
header?: string | React.ReactNode
|
|
10
|
-
onHide: () => void
|
|
11
|
-
width?: string | number
|
|
12
|
-
visible: boolean
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const Overlay = ({
|
|
16
|
-
breakpoint,
|
|
17
|
-
children,
|
|
18
|
-
onHide,
|
|
19
|
-
className,
|
|
20
|
-
visible = false,
|
|
21
|
-
width = '50vw',
|
|
22
|
-
header = ''
|
|
23
|
-
}: OverlayProps) => {
|
|
24
|
-
const isTablet = breakpoint < Breakpoint.Desktop
|
|
25
|
-
|
|
26
|
-
if (isTablet) {
|
|
27
|
-
return (
|
|
28
|
-
<Sidebar
|
|
29
|
-
blockScroll
|
|
30
|
-
className={className}
|
|
31
|
-
closeIcon={<i className="fa-regular fa-xmark fa-lg" aria-hidden="true" />}
|
|
32
|
-
header={header}
|
|
33
|
-
onHide={onHide}
|
|
34
|
-
position="bottom"
|
|
35
|
-
pt={{
|
|
36
|
-
header: { className: 'border-b border-neutral-80 font-bold text-l' },
|
|
37
|
-
root: { className: '[&_.p-sidebar-content]:px-0 h-fit' },
|
|
38
|
-
content: { className: 'p-0' }
|
|
39
|
-
}}
|
|
40
|
-
visible={visible}
|
|
41
|
-
>
|
|
42
|
-
{children}
|
|
43
|
-
</Sidebar>
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<Dialog
|
|
49
|
-
blockScroll
|
|
50
|
-
className={className}
|
|
51
|
-
closable
|
|
52
|
-
closeIcon={<i className="fa-regular fa-xmark fa-lg" aria-hidden="true" />}
|
|
53
|
-
contentStyle={{ padding: '0' }}
|
|
54
|
-
dismissableMask
|
|
55
|
-
draggable
|
|
56
|
-
focusOnShow={false}
|
|
57
|
-
header={header}
|
|
58
|
-
headerStyle={{
|
|
59
|
-
borderBottom: '1px solid var(--neutral-80)',
|
|
60
|
-
padding: '16px'
|
|
61
|
-
}}
|
|
62
|
-
onHide={onHide}
|
|
63
|
-
pt={{
|
|
64
|
-
headerTitle: { className: '!font-bold !text-l' }
|
|
65
|
-
}}
|
|
66
|
-
style={{ width }}
|
|
67
|
-
visible={visible}
|
|
68
|
-
>
|
|
69
|
-
{children}
|
|
70
|
-
</Dialog>
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export default Overlay
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './Overlay'
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import type { Participant, VideoClient } from '@zoom/videosdk'
|
|
2
|
-
import cn from 'classnames'
|
|
3
|
-
import { ReactSetter } from '../types'
|
|
4
|
-
|
|
5
|
-
type Props = {
|
|
6
|
-
participants: Participant[]
|
|
7
|
-
setIsCamOn: ReactSetter<boolean>
|
|
8
|
-
setIsMicOn: ReactSetter<boolean>
|
|
9
|
-
zmClient: typeof VideoClient
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const ParticipantsList = ({ zmClient, setIsCamOn, setIsMicOn, participants }: Props) => {
|
|
13
|
-
return (
|
|
14
|
-
<ul className="flex flex-col gap-8 w-full self-start">
|
|
15
|
-
{participants.map((user) => {
|
|
16
|
-
const isCurrentUser = user.userId === zmClient?.getCurrentUserInfo()?.userId
|
|
17
|
-
return (
|
|
18
|
-
<li key={user.userId} className="flex items-center justify-between gap-16 p-4 rounded shadow-medium w-full">
|
|
19
|
-
<span className="truncate flex gap-4">
|
|
20
|
-
<span className="truncate">{user.displayName}</span>
|
|
21
|
-
<span className="truncate">id: {user.userId}</span>
|
|
22
|
-
<span className="text-sm">{isCurrentUser ? '(You)' : ''}</span>
|
|
23
|
-
</span>
|
|
24
|
-
<div className="flex items-center gap-8">
|
|
25
|
-
<button
|
|
26
|
-
onClick={() => setIsCamOn((prev) => !prev)}
|
|
27
|
-
title={user.bVideoOn ? 'Stop Video' : 'Start Video'}
|
|
28
|
-
disabled={!isCurrentUser}
|
|
29
|
-
className={cn('w-24', { 'opacity-32 cursor-not-allowed': !isCurrentUser })}
|
|
30
|
-
>
|
|
31
|
-
<i className={`fa-regular ${user.bVideoOn ? 'fa-video' : 'fa-video-slash'}`} aria-hidden="true" />
|
|
32
|
-
</button>
|
|
33
|
-
<button
|
|
34
|
-
onClick={() => setIsMicOn((prev) => !prev)}
|
|
35
|
-
title={user.muted || !user.audio ? 'Unmute' : 'Mute'}
|
|
36
|
-
disabled={!isCurrentUser}
|
|
37
|
-
className={cn('w-24', { 'opacity-32 cursor-not-allowed': !isCurrentUser })}
|
|
38
|
-
>
|
|
39
|
-
<i
|
|
40
|
-
className={`fa-regular ${user.muted || !user.audio ? 'fa-microphone-slash' : 'fa-microphone'}`}
|
|
41
|
-
aria-hidden="true"
|
|
42
|
-
/>
|
|
43
|
-
</button>
|
|
44
|
-
</div>
|
|
45
|
-
</li>
|
|
46
|
-
)
|
|
47
|
-
})}
|
|
48
|
-
</ul>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export default ParticipantsList
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { useSettingsOverlayContext } from './context'
|
|
2
|
-
import AudioSettings from './Tabs/AudioSettings'
|
|
3
|
-
import BackgroundSettings from './Tabs/BackgroundSettings'
|
|
4
|
-
import VideoSettings from './Tabs/VideoSettings'
|
|
5
|
-
|
|
6
|
-
const settingsRegistry = {
|
|
7
|
-
Audio: AudioSettings,
|
|
8
|
-
Video: VideoSettings,
|
|
9
|
-
Background: BackgroundSettings
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const SettingsContent = () => {
|
|
13
|
-
const { selectedSettingsTab } = useSettingsOverlayContext()
|
|
14
|
-
|
|
15
|
-
const ActiveComponent = settingsRegistry[selectedSettingsTab]
|
|
16
|
-
return ActiveComponent ? <ActiveComponent /> : null
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default SettingsContent
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import cn from 'classnames'
|
|
2
|
-
import MobileIconButton from '../MobileIconButton'
|
|
3
|
-
import { SettingsTab, useSettingsOverlayContext } from './context'
|
|
4
|
-
|
|
5
|
-
const tabs: { id: SettingsTab; icon: string; title: string }[] = [
|
|
6
|
-
{ id: SettingsTab.Audio, icon: 'fa-regular fa-microphone', title: 'Audio' },
|
|
7
|
-
{ id: SettingsTab.Video, icon: 'fa-regular fa-video', title: 'Video' },
|
|
8
|
-
{ id: SettingsTab.Background, icon: 'fa-regular fa-image', title: 'Fondos' }
|
|
9
|
-
]
|
|
10
|
-
|
|
11
|
-
const SettingsMenu = () => {
|
|
12
|
-
const { selectedSettingsTab, setSelectedSettingsTab } = useSettingsOverlayContext()
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<div className="flex flex-col sm:flex-row md:flex-col w-full p-16 md:p-0 gap-16 md:gap-0 md:w-fit md:min-w-[200px] md:flex-shrink-0 overflow-visible">
|
|
16
|
-
{tabs.map(({ id, icon, title }) => (
|
|
17
|
-
<MobileIconButton
|
|
18
|
-
key={id}
|
|
19
|
-
className={cn('font-bold md:rounded-none md:border-0', { 'bg-neutral-95': selectedSettingsTab === id })}
|
|
20
|
-
icon={icon}
|
|
21
|
-
id={id}
|
|
22
|
-
onClick={() => setSelectedSettingsTab(id)}
|
|
23
|
-
title={title}
|
|
24
|
-
/>
|
|
25
|
-
))}
|
|
26
|
-
</div>
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export default SettingsMenu
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { useEffect, useMemo, useState } from 'react'
|
|
2
|
-
import { Breakpoint } from '../../hooks/useDeviceSize'
|
|
3
|
-
import Overlay from '../Overlay'
|
|
4
|
-
import { SETTINGS_OVERLAY_WIDTH } from '../../constants'
|
|
5
|
-
import SettingsMenu from './SettingsMenu'
|
|
6
|
-
import SettingsContent from './SettingsContent'
|
|
7
|
-
import { settingsOverlayContext, SettingsOverlayContextProps, SettingsTab } from './context'
|
|
8
|
-
|
|
9
|
-
type Props = {
|
|
10
|
-
breakpoint: Breakpoint
|
|
11
|
-
visible: boolean
|
|
12
|
-
onHide: () => void
|
|
13
|
-
defaultTab?: SettingsTab
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const SettingsOverlay = ({ visible, onHide, breakpoint, defaultTab = SettingsTab.Audio }: Props) => {
|
|
17
|
-
const [selectedSettingsTab, setSelectedSettingsTab] = useState<SettingsTab>(defaultTab)
|
|
18
|
-
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
if (visible) setSelectedSettingsTab(defaultTab)
|
|
21
|
-
}, [defaultTab, visible])
|
|
22
|
-
|
|
23
|
-
const contextVal = useMemo<SettingsOverlayContextProps>(
|
|
24
|
-
() => ({
|
|
25
|
-
selectedSettingsTab,
|
|
26
|
-
setSelectedSettingsTab
|
|
27
|
-
}),
|
|
28
|
-
[selectedSettingsTab]
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<Overlay
|
|
33
|
-
visible={visible}
|
|
34
|
-
onHide={onHide}
|
|
35
|
-
header="Settings"
|
|
36
|
-
breakpoint={breakpoint}
|
|
37
|
-
width={SETTINGS_OVERLAY_WIDTH}
|
|
38
|
-
className="min-w-[30vw] min-h-[30vh] max-h-[60vh] h-full [&_.p-dialog-content]:overflow-hidden"
|
|
39
|
-
>
|
|
40
|
-
<settingsOverlayContext.Provider value={contextVal}>
|
|
41
|
-
<div className="flex flex-col md:flex-row divide-y md:divide-y-0 md:divide-x divide-neutral-80 h-full">
|
|
42
|
-
<SettingsMenu />
|
|
43
|
-
<div className="flex-1 overflow-y-auto p-12">
|
|
44
|
-
<SettingsContent />
|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
47
|
-
</settingsOverlayContext.Provider>
|
|
48
|
-
</Overlay>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export default SettingsOverlay
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback, useRef, useEffect } from 'react'
|
|
2
|
-
import { ProgressBar } from 'primereact/progressbar'
|
|
3
|
-
import cn from 'classnames'
|
|
4
|
-
import { Dropdown } from 'primereact/dropdown'
|
|
5
|
-
import { useZoomVideoContext } from '../../../context'
|
|
6
|
-
import DropdownValueTemplate from './DropdownValueTemplate'
|
|
7
|
-
import DropdownItemTemplate from './DropdownItemTemplate'
|
|
8
|
-
|
|
9
|
-
const UPDATE_INTERVAL_MS = 300
|
|
10
|
-
|
|
11
|
-
const AudioSettings = () => {
|
|
12
|
-
const {
|
|
13
|
-
activeAudioOutput,
|
|
14
|
-
activeMicrophone,
|
|
15
|
-
audioOutputList,
|
|
16
|
-
localAudio,
|
|
17
|
-
micList,
|
|
18
|
-
switchActiveAudioOutput,
|
|
19
|
-
switchActiveMicrophone
|
|
20
|
-
} = useZoomVideoContext()
|
|
21
|
-
|
|
22
|
-
const [micLevel, setMicLevel] = useState<number>(0)
|
|
23
|
-
const [speakerLevel, setSpeakerLevel] = useState<number>(0)
|
|
24
|
-
const [micState, setMicState] = useState<'idle' | 'recording' | 'playing'>('idle')
|
|
25
|
-
|
|
26
|
-
const micListOptions = micList.map((mic) => ({
|
|
27
|
-
label: mic.label || `Micrófono ${mic.deviceId}`,
|
|
28
|
-
value: mic.deviceId
|
|
29
|
-
}))
|
|
30
|
-
|
|
31
|
-
const speakerOptions = audioOutputList.map((spk) => ({
|
|
32
|
-
label: spk.label || `Altavoz ${spk.deviceId}`,
|
|
33
|
-
value: spk.deviceId
|
|
34
|
-
}))
|
|
35
|
-
|
|
36
|
-
const micTesterRef = useRef<any>(null)
|
|
37
|
-
const lastUpdateMicRef = useRef<number>(0)
|
|
38
|
-
const handleMicTest = useCallback(async () => {
|
|
39
|
-
if (!localAudio) return
|
|
40
|
-
|
|
41
|
-
if (micTesterRef.current) {
|
|
42
|
-
micTesterRef.current.stop?.()
|
|
43
|
-
micTesterRef.current = null
|
|
44
|
-
setMicState('idle')
|
|
45
|
-
setMicLevel(0)
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const tester = localAudio.testMicrophone({
|
|
50
|
-
microphoneId: activeMicrophone,
|
|
51
|
-
speakerId: activeAudioOutput,
|
|
52
|
-
recordAndPlay: true,
|
|
53
|
-
onAnalyseFrequency: (v: number) => {
|
|
54
|
-
const now = Date.now()
|
|
55
|
-
// only update every UPDATE_INTERVAL_MS ms
|
|
56
|
-
if (now - lastUpdateMicRef.current > UPDATE_INTERVAL_MS) {
|
|
57
|
-
lastUpdateMicRef.current = now
|
|
58
|
-
setMicLevel(v)
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
onStartRecording: () => setMicState('recording'),
|
|
62
|
-
onStartPlayRecording: () => setMicState('playing'),
|
|
63
|
-
onStopPlayRecording: () => {
|
|
64
|
-
setMicState('idle')
|
|
65
|
-
micTesterRef.current = null
|
|
66
|
-
setMicLevel(0)
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
micTesterRef.current = tester
|
|
71
|
-
setMicState('recording')
|
|
72
|
-
}, [localAudio, activeMicrophone, activeAudioOutput])
|
|
73
|
-
|
|
74
|
-
const speakerTesterRef = useRef<any>(null)
|
|
75
|
-
const lastUpdateSpeakerRef = useRef<number>(0)
|
|
76
|
-
const handleSpeakerTest = useCallback(async () => {
|
|
77
|
-
if (!localAudio) return
|
|
78
|
-
if (speakerTesterRef.current) {
|
|
79
|
-
speakerTesterRef.current.destroy?.()
|
|
80
|
-
speakerTesterRef.current = null
|
|
81
|
-
setSpeakerLevel(0)
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
const tester = localAudio.testSpeaker({
|
|
85
|
-
speakerId: activeAudioOutput,
|
|
86
|
-
onAnalyseFrequency: (v: number) => {
|
|
87
|
-
const now = Date.now()
|
|
88
|
-
// only update every UPDATE_INTERVAL_MS ms
|
|
89
|
-
if (now - lastUpdateSpeakerRef.current > UPDATE_INTERVAL_MS) {
|
|
90
|
-
lastUpdateSpeakerRef.current = now
|
|
91
|
-
setSpeakerLevel(v)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
})
|
|
95
|
-
speakerTesterRef.current = tester
|
|
96
|
-
}, [localAudio, activeAudioOutput])
|
|
97
|
-
|
|
98
|
-
useEffect(() => {
|
|
99
|
-
return () => {
|
|
100
|
-
micTesterRef.current?.stop?.()
|
|
101
|
-
speakerTesterRef.current?.destroy?.()
|
|
102
|
-
}
|
|
103
|
-
}, [])
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<div className="flex flex-col">
|
|
107
|
-
{/* Microphone Section */}
|
|
108
|
-
<p className="text-s font-bold">Micrófono</p>
|
|
109
|
-
<p className="text-s mb-8 text-neutral-40">Selecciona un método de entrada de audio</p>
|
|
110
|
-
<Dropdown
|
|
111
|
-
className="mb-16 w-full"
|
|
112
|
-
itemTemplate={(option) => <DropdownItemTemplate option={option} isActive={option.value === activeMicrophone} />}
|
|
113
|
-
placeholder="Selecciona un micrófono"
|
|
114
|
-
options={micListOptions}
|
|
115
|
-
onChange={async (e) => await switchActiveMicrophone(e.value)}
|
|
116
|
-
value={activeMicrophone}
|
|
117
|
-
valueTemplate={(option) => <DropdownValueTemplate option={option} icon="fa-regular fa-microphone" />}
|
|
118
|
-
/>
|
|
119
|
-
|
|
120
|
-
{/* Microphone Test Header */}
|
|
121
|
-
<div className="mt-4 mb-8">
|
|
122
|
-
<p className="text-s font-bold">Prueba de micrófono</p>
|
|
123
|
-
<div className="mt-4 self-start">
|
|
124
|
-
<p className="text-s text-neutral-40 text-start">
|
|
125
|
-
{micState === 'idle' && 'Graba una muestra de tu voz y escúchela para verificar el funcionamiento.'}
|
|
126
|
-
{micState === 'recording' && 'Grabando la voz... espera unos segundos.'}
|
|
127
|
-
{micState === 'playing' && 'Reproduciendo grabación...'}
|
|
128
|
-
</p>
|
|
129
|
-
</div>
|
|
130
|
-
</div>
|
|
131
|
-
|
|
132
|
-
<button
|
|
133
|
-
type="button"
|
|
134
|
-
onClick={handleMicTest}
|
|
135
|
-
className={cn(
|
|
136
|
-
'w-fit rounded-l p-8 font-semibold transition-all duration-200 border hover:border-neutral-70 text-white',
|
|
137
|
-
{
|
|
138
|
-
'bg-neutral-60': micState === 'recording' || micState === 'playing',
|
|
139
|
-
'bg-neutral-50': micState === 'idle'
|
|
140
|
-
}
|
|
141
|
-
)}
|
|
142
|
-
>
|
|
143
|
-
{micState === 'recording' && 'Detener grabación'}
|
|
144
|
-
{micState === 'playing' && 'Detener reproducción'}
|
|
145
|
-
{micState === 'idle' && 'Probar micrófono'}
|
|
146
|
-
</button>
|
|
147
|
-
|
|
148
|
-
{/* Status message */}
|
|
149
|
-
<ProgressBar
|
|
150
|
-
value={micState === 'idle' ? 0 : micLevel}
|
|
151
|
-
showValue={false}
|
|
152
|
-
className="w-full mt-4 h-8 rounded-m border border-neutral-80"
|
|
153
|
-
/>
|
|
154
|
-
|
|
155
|
-
{/* Speaker Section */}
|
|
156
|
-
<div className="mt-32">
|
|
157
|
-
<p className="text-s font-bold">Altavoz</p>
|
|
158
|
-
<p className="text-s mb-8 text-neutral-40">Selecciona un método de salida de audio</p>
|
|
159
|
-
<Dropdown
|
|
160
|
-
className="mb-16 w-full"
|
|
161
|
-
itemTemplate={(option) => (
|
|
162
|
-
<DropdownItemTemplate option={option} isActive={option.value === activeAudioOutput} />
|
|
163
|
-
)}
|
|
164
|
-
options={speakerOptions}
|
|
165
|
-
placeholder="Selecciona un altavoz"
|
|
166
|
-
onChange={async (e) => await switchActiveAudioOutput(e.value)}
|
|
167
|
-
value={activeAudioOutput}
|
|
168
|
-
valueTemplate={(option) => <DropdownValueTemplate option={option} icon="fa-regular fa-volume" />}
|
|
169
|
-
/>
|
|
170
|
-
|
|
171
|
-
<button
|
|
172
|
-
type="button"
|
|
173
|
-
onClick={handleSpeakerTest}
|
|
174
|
-
className={cn(
|
|
175
|
-
'w-fit rounded-l p-8 font-semibold transition-all duration-200 border hover:border-neutral-70 text-white',
|
|
176
|
-
speakerTesterRef.current ? 'bg-neutral-60' : 'bg-neutral-50'
|
|
177
|
-
)}
|
|
178
|
-
>
|
|
179
|
-
{speakerTesterRef.current ? 'Detener prueba de altavoz' : 'Probar altavoz'}
|
|
180
|
-
</button>
|
|
181
|
-
<ProgressBar
|
|
182
|
-
value={speakerLevel}
|
|
183
|
-
showValue={false}
|
|
184
|
-
className="w-full mt-8 h-8 rounded-m border border-neutral-80"
|
|
185
|
-
/>
|
|
186
|
-
</div>
|
|
187
|
-
</div>
|
|
188
|
-
)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export default AudioSettings
|
package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/BackgroundSettings.tsx
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import cn from 'classnames'
|
|
2
|
-
import { useZoomVideoContext } from '../../../context'
|
|
3
|
-
|
|
4
|
-
const BackgroundSettings = () => {
|
|
5
|
-
const { isBlurred, setIsBlurred } = useZoomVideoContext()
|
|
6
|
-
|
|
7
|
-
const options = [
|
|
8
|
-
{
|
|
9
|
-
label: 'Ninguno',
|
|
10
|
-
icon: 'fa-ban',
|
|
11
|
-
isActive: !isBlurred
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
label: 'Desenfocar',
|
|
15
|
-
icon: 'fa-droplet',
|
|
16
|
-
isActive: isBlurred
|
|
17
|
-
}
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div className="flex flex-col">
|
|
22
|
-
<p className="text-s font-bold mb-8">Fondo</p>
|
|
23
|
-
<div className="flex gap-12">
|
|
24
|
-
{options.map(({ label, icon, isActive }) => {
|
|
25
|
-
return (
|
|
26
|
-
<button
|
|
27
|
-
key={label}
|
|
28
|
-
type="button"
|
|
29
|
-
onClick={() => setIsBlurred((prev: boolean) => (isActive ? prev : !prev))}
|
|
30
|
-
className={cn(
|
|
31
|
-
'w-full h-80 flex flex-col items-center justify-center gap-8 border rounded-l bg-white font-bold transition-colors ease-in-out',
|
|
32
|
-
isActive ? '!border-neutral-60 border-2' : 'border-neutral-80 hover:border-neutral-70'
|
|
33
|
-
)}
|
|
34
|
-
>
|
|
35
|
-
<span className="flex items-center justify-center w-24 h-24">
|
|
36
|
-
<i className={cn('fa-regular fa-xl', icon)} aria-hidden="true" />
|
|
37
|
-
</span>
|
|
38
|
-
<span>{label}</span>
|
|
39
|
-
</button>
|
|
40
|
-
)
|
|
41
|
-
})}
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export default BackgroundSettings
|
package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/DropdownItemTemplate.tsx
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
const DropdownItemTemplate = ({
|
|
2
|
-
option,
|
|
3
|
-
isActive
|
|
4
|
-
}: {
|
|
5
|
-
option: { label: string; value: string }
|
|
6
|
-
isActive: boolean
|
|
7
|
-
}) => {
|
|
8
|
-
return (
|
|
9
|
-
<div className="flex items-center gap-4">
|
|
10
|
-
{isActive && (
|
|
11
|
-
<span className="flex items-center justify-center w-24 h-24">
|
|
12
|
-
<i className="fa-regular fa-check" aria-hidden="true" />
|
|
13
|
-
</span>
|
|
14
|
-
)}
|
|
15
|
-
<span className="mr-2">{option.label}</span>
|
|
16
|
-
</div>
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export default DropdownItemTemplate
|
package/src/components/ZoomVideoPlugin/components/SettingsOverlay/Tabs/DropdownValueTemplate.tsx
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
const DropdownValueTemplate = ({ option, icon }: { option: { label: string; value: string }; icon: string }) => {
|
|
2
|
-
return (
|
|
3
|
-
<div className="flex items-center gap-4 font-semibold text-neutral-50">
|
|
4
|
-
<span className="flex items-center justify-center w-24 h-24">
|
|
5
|
-
<i className={icon} aria-hidden="true" />
|
|
6
|
-
</span>
|
|
7
|
-
<span className="truncate">{option.label}</span>
|
|
8
|
-
</div>
|
|
9
|
-
)
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default DropdownValueTemplate
|