mirror of
https://github.com/suitenumerique/meet.git
synced 2026-09-01 05:07:56 +00:00
27ebc2f2e3
Removes Node.js type dependency from browser/React code by substituting the non-portable `NodeJS.Timeout` type with the standard `ReturnType<typeof setTimeout>` equivalent across all affected files. Also replaces `process.env.NODE_ENV` in Icon.tsx with `import.meta.env.MODE` for Vite compatibility.
90 lines
2.5 KiB
TypeScript
90 lines
2.5 KiB
TypeScript
import { useTranslation } from 'react-i18next'
|
|
import { RiHand } from '@remixicon/react'
|
|
import { ToggleButton } from '@/primitives'
|
|
import { css } from '@/styled-system/css'
|
|
import { useRoomContext } from '@livekit/components-react'
|
|
import { useRaisedHand } from '@/features/rooms/livekit/hooks/useRaisedHand'
|
|
import { useEffect, useRef, useState } from 'react'
|
|
import {
|
|
closeLowerHandToasts,
|
|
showLowerHandToast,
|
|
} from '@/features/notifications/utils'
|
|
import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut'
|
|
|
|
const SPEAKING_DETECTION_DELAY = 3000
|
|
|
|
export const HandToggle = () => {
|
|
const { t } = useTranslation('rooms', { keyPrefix: 'controls.hand' })
|
|
|
|
const room = useRoomContext()
|
|
const { isHandRaised, toggleRaisedHand } = useRaisedHand({
|
|
participant: room.localParticipant,
|
|
})
|
|
|
|
const isSpeaking = room.localParticipant.isSpeaking
|
|
const speakingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
const [hasShownToast, setHasShownToast] = useState(false)
|
|
|
|
const resetToastState = () => {
|
|
setHasShownToast(false)
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (isHandRaised) return
|
|
closeLowerHandToasts()
|
|
}, [isHandRaised])
|
|
|
|
const handleToggle = () => {
|
|
toggleRaisedHand()
|
|
resetToastState()
|
|
}
|
|
|
|
useRegisterKeyboardShortcut({
|
|
id: 'raise-hand',
|
|
handler: handleToggle,
|
|
})
|
|
|
|
useEffect(() => {
|
|
const shouldShowToast = isSpeaking && isHandRaised && !hasShownToast
|
|
|
|
if (shouldShowToast && !speakingTimerRef.current) {
|
|
speakingTimerRef.current = setTimeout(() => {
|
|
setHasShownToast(true)
|
|
const onClose = () => {
|
|
if (isHandRaised) toggleRaisedHand()
|
|
resetToastState()
|
|
}
|
|
showLowerHandToast(room.localParticipant, onClose)
|
|
}, SPEAKING_DETECTION_DELAY)
|
|
}
|
|
if ((!isSpeaking || !isHandRaised) && speakingTimerRef.current) {
|
|
clearTimeout(speakingTimerRef.current)
|
|
speakingTimerRef.current = null
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [isSpeaking, isHandRaised, hasShownToast, toggleRaisedHand])
|
|
|
|
const tooltipLabel = isHandRaised ? 'lower' : 'raise'
|
|
|
|
return (
|
|
<div
|
|
className={css({
|
|
position: 'relative',
|
|
display: 'inline-block',
|
|
})}
|
|
>
|
|
<ToggleButton
|
|
square
|
|
variant="primaryDark"
|
|
aria-label={t(tooltipLabel)}
|
|
tooltip={t(tooltipLabel)}
|
|
isSelected={isHandRaised}
|
|
onPress={handleToggle}
|
|
data-attr={`controls-hand-${tooltipLabel}`}
|
|
>
|
|
<RiHand />
|
|
</ToggleButton>
|
|
</div>
|
|
)
|
|
}
|