2024-03-02 08:20:50 +11:00
|
|
|
import { MouseEventHandler, useEffect, useRef, useState } from 'react'
|
2023-06-22 16:43:33 +10:00
|
|
|
import { useStore } from '../useStore'
|
2024-03-02 08:20:50 +11:00
|
|
|
import { getNormalisedCoordinates } from '../lib/utils'
|
2023-08-10 16:22:45 -04:00
|
|
|
import Loading from './Loading'
|
2024-03-11 20:26:13 -04:00
|
|
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
2023-10-11 13:36:54 +11:00
|
|
|
import { useModelingContext } from 'hooks/useModelingContext'
|
2024-03-22 16:55:30 +11:00
|
|
|
import { useKclContext } from 'lang/KclProvider'
|
2024-02-14 08:03:20 +11:00
|
|
|
import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
|
2024-02-26 21:02:33 +11:00
|
|
|
import { NetworkHealthState, useNetworkStatus } from './NetworkHealthIndicator'
|
2024-03-22 10:23:04 +11:00
|
|
|
import { butName } from 'lib/cameraControls'
|
|
|
|
import { sendSelectEventToEngine } from 'lib/selections'
|
2023-03-06 20:13:34 +11:00
|
|
|
|
2024-02-11 12:59:00 +11:00
|
|
|
export const Stream = ({ className = '' }: { className?: string }) => {
|
2023-08-10 16:22:45 -04:00
|
|
|
const [isLoading, setIsLoading] = useState(true)
|
2023-09-06 01:32:53 -04:00
|
|
|
const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
|
2023-03-06 20:13:34 +11:00
|
|
|
const videoRef = useRef<HTMLVideoElement>(null)
|
2023-08-06 21:29:26 -04:00
|
|
|
const {
|
|
|
|
mediaStream,
|
2023-09-08 10:13:35 -04:00
|
|
|
setButtonDownInStream,
|
2023-08-09 20:49:10 +10:00
|
|
|
didDragInStream,
|
|
|
|
setDidDragInStream,
|
|
|
|
streamDimensions,
|
2023-08-06 21:29:26 -04:00
|
|
|
} = useStore((s) => ({
|
2023-06-22 16:43:33 +10:00
|
|
|
mediaStream: s.mediaStream,
|
2023-09-08 10:13:35 -04:00
|
|
|
setButtonDownInStream: s.setButtonDownInStream,
|
2023-08-09 20:49:10 +10:00
|
|
|
didDragInStream: s.didDragInStream,
|
|
|
|
setDidDragInStream: s.setDidDragInStream,
|
|
|
|
streamDimensions: s.streamDimensions,
|
2023-06-22 16:43:33 +10:00
|
|
|
}))
|
2024-03-11 20:26:13 -04:00
|
|
|
const { settings } = useSettingsAuthContext()
|
2024-02-11 12:59:00 +11:00
|
|
|
const { state } = useModelingContext()
|
2023-10-11 13:36:54 +11:00
|
|
|
const { isExecuting } = useKclContext()
|
2024-02-26 21:02:33 +11:00
|
|
|
const { overallState } = useNetworkStatus()
|
|
|
|
const isNetworkOkay = overallState === NetworkHealthState.Ok
|
2023-03-06 20:13:34 +11:00
|
|
|
|
|
|
|
useEffect(() => {
|
2023-03-07 15:45:59 +11:00
|
|
|
if (
|
|
|
|
typeof window === 'undefined' ||
|
|
|
|
typeof RTCPeerConnection === 'undefined'
|
|
|
|
)
|
|
|
|
return
|
2023-06-22 16:43:33 +10:00
|
|
|
if (!videoRef.current) return
|
|
|
|
if (!mediaStream) return
|
|
|
|
videoRef.current.srcObject = mediaStream
|
2023-09-25 19:49:53 -07:00
|
|
|
}, [mediaStream])
|
2023-03-06 20:13:34 +11:00
|
|
|
|
2024-02-11 12:59:00 +11:00
|
|
|
const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {
|
2023-06-22 16:43:33 +10:00
|
|
|
if (!videoRef.current) return
|
2024-02-11 12:59:00 +11:00
|
|
|
if (state.matches('Sketch')) return
|
|
|
|
if (state.matches('Sketch no face')) return
|
2023-08-09 20:49:10 +10:00
|
|
|
const { x, y } = getNormalisedCoordinates({
|
2023-09-08 10:13:35 -04:00
|
|
|
clientX: e.clientX,
|
|
|
|
clientY: e.clientY,
|
2023-08-09 20:49:10 +10:00
|
|
|
el: videoRef.current,
|
|
|
|
...streamDimensions,
|
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
|
2023-09-08 10:13:35 -04:00
|
|
|
setButtonDownInStream(e.button)
|
2023-09-06 01:32:53 -04:00
|
|
|
setClickCoords({ x, y })
|
2023-06-22 16:43:33 +10:00
|
|
|
}
|
2023-08-06 21:29:26 -04:00
|
|
|
|
2024-03-22 10:23:04 +11:00
|
|
|
const handleMouseUp: MouseEventHandler<HTMLDivElement> = (e) => {
|
2023-06-22 16:43:33 +10:00
|
|
|
if (!videoRef.current) return
|
2023-09-13 08:36:47 +10:00
|
|
|
setButtonDownInStream(undefined)
|
2024-02-11 12:59:00 +11:00
|
|
|
if (state.matches('Sketch')) return
|
|
|
|
if (state.matches('Sketch no face')) return
|
2023-08-06 21:29:26 -04:00
|
|
|
|
2024-03-22 10:23:04 +11:00
|
|
|
if (!didDragInStream && butName(e).left) {
|
|
|
|
sendSelectEventToEngine(e, videoRef.current, streamDimensions)
|
2023-10-11 13:36:54 +11:00
|
|
|
}
|
2023-09-14 09:34:37 -04:00
|
|
|
|
2023-08-09 20:49:10 +10:00
|
|
|
setDidDragInStream(false)
|
2023-09-06 01:32:53 -04:00
|
|
|
setClickCoords(undefined)
|
|
|
|
}
|
|
|
|
|
|
|
|
const handleMouseMove: MouseEventHandler<HTMLVideoElement> = (e) => {
|
2024-02-11 12:59:00 +11:00
|
|
|
if (state.matches('Sketch')) return
|
|
|
|
if (state.matches('Sketch no face')) return
|
2023-09-06 01:32:53 -04:00
|
|
|
if (!clickCoords) return
|
|
|
|
|
|
|
|
const delta =
|
|
|
|
((clickCoords.x - e.clientX) ** 2 + (clickCoords.y - e.clientY) ** 2) **
|
|
|
|
0.5
|
|
|
|
|
|
|
|
if (delta > 5 && !didDragInStream) {
|
|
|
|
setDidDragInStream(true)
|
|
|
|
}
|
2023-06-22 16:43:33 +10:00
|
|
|
}
|
2023-03-06 20:13:34 +11:00
|
|
|
|
|
|
|
return (
|
2024-02-11 12:59:00 +11:00
|
|
|
<div
|
|
|
|
id="stream"
|
|
|
|
className={className}
|
|
|
|
onMouseUp={handleMouseUp}
|
|
|
|
onMouseDown={handleMouseDown}
|
|
|
|
onContextMenu={(e) => e.preventDefault()}
|
|
|
|
onContextMenuCapture={(e) => e.preventDefault()}
|
|
|
|
>
|
2023-06-22 16:43:33 +10:00
|
|
|
<video
|
|
|
|
ref={videoRef}
|
|
|
|
muted
|
|
|
|
autoPlay
|
|
|
|
controls={false}
|
2023-08-10 16:22:45 -04:00
|
|
|
onPlay={() => setIsLoading(false)}
|
2023-09-06 01:32:53 -04:00
|
|
|
onMouseMoveCapture={handleMouseMove}
|
2023-10-11 13:36:54 +11:00
|
|
|
className={`w-full cursor-pointer h-full ${isExecuting && 'blur-md'}`}
|
2023-10-03 15:11:44 -07:00
|
|
|
disablePictureInPicture
|
2023-09-08 17:50:37 +10:00
|
|
|
style={{ transitionDuration: '200ms', transitionProperty: 'filter' }}
|
2024-03-22 10:23:04 +11:00
|
|
|
id="video-stream"
|
2023-06-22 16:43:33 +10:00
|
|
|
/>
|
2024-03-05 20:37:48 -05:00
|
|
|
<ClientSideScene cameraControls={settings.context?.cameraControls} />
|
2024-02-26 21:02:33 +11:00
|
|
|
{!isNetworkOkay && !isLoading && (
|
|
|
|
<div className="text-center absolute inset-0">
|
|
|
|
<Loading>
|
|
|
|
<span data-testid="loading-stream">Stream disconnected</span>
|
|
|
|
</Loading>
|
|
|
|
</div>
|
|
|
|
)}
|
2023-08-10 16:22:45 -04:00
|
|
|
{isLoading && (
|
|
|
|
<div className="text-center absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
2023-11-24 08:59:24 +11:00
|
|
|
<Loading>
|
|
|
|
<span data-testid="loading-stream">Loading stream...</span>
|
|
|
|
</Loading>
|
2023-08-10 16:22:45 -04:00
|
|
|
</div>
|
|
|
|
)}
|
2023-03-06 20:13:34 +11:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|