wip
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { MouseEventHandler, useEffect, useRef, useState, MutableRefObject } from 'react'
|
||||
import { MouseEventHandler, useEffect, useRef, useState, MutableRefObject, useCallback } from 'react'
|
||||
import { useAppState } from 'AppState'
|
||||
import { createMachine, createActor, setup } from 'xstate'
|
||||
import { createActorContext } from '@xstate/react'
|
||||
@ -26,11 +26,15 @@ export const EngineStream = () => {
|
||||
const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
|
||||
const { setAppState } = useAppState()
|
||||
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
|
||||
const { overallState } = useNetworkContext()
|
||||
const { auth, settings } = useSettingsAuthContext()
|
||||
const { settings } = useSettingsAuthContext()
|
||||
|
||||
const settingsEngine = {
|
||||
theme: settings.context.app.theme.current,
|
||||
enableSSAO: settings.context.app.enableSSAO.current,
|
||||
highlightEdges: settings.context.modeling.highlightEdges.current,
|
||||
showScaleGrid: settings.context.modeling.showScaleGrid.current,
|
||||
}
|
||||
|
||||
const {
|
||||
state: modelingMachineState,
|
||||
@ -41,17 +45,7 @@ export const EngineStream = () => {
|
||||
const engineStreamActor = useEngineStreamContext.useActorRef()
|
||||
const engineStreamState = engineStreamActor.getSnapshot()
|
||||
|
||||
useEffect(() => {
|
||||
engineStreamActor.send({ type: EngineStreamTransition.SetContextProperty, value: { authToken: auth?.context?.token } } )
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
engineStreamActor.send({ type: EngineStreamTransition.SetContextProperty, value: { videoRef } })
|
||||
}, [videoRef.current])
|
||||
|
||||
useEffect(() => {
|
||||
engineStreamActor.send({ type: EngineStreamTransition.SetContextProperty, value: { canvasRef } })
|
||||
}, [canvasRef.current])
|
||||
const streamIdleMode = settings.context.app.streamIdleMode.current
|
||||
|
||||
useEffect(() => {
|
||||
let timestampNext: number | null = null
|
||||
@ -63,8 +57,22 @@ export const EngineStream = () => {
|
||||
// so those exception people don't see.
|
||||
const REASONABLE_TIME_TO_REFRESH_STREAM_SIZE = 200
|
||||
|
||||
|
||||
const configure = () => {
|
||||
engineStreamActor.send({ type: EngineStreamTransition.StartOrReconfigureEngine, modelingMachineActorSend, settings, setAppState })
|
||||
engineStreamActor.send({
|
||||
type: EngineStreamTransition.StartOrReconfigureEngine,
|
||||
modelingMachineActorSend,
|
||||
settings: settingsEngine,
|
||||
setAppState,
|
||||
|
||||
// It's possible a reconnect happens as we drag the window :')
|
||||
onMediaStream(mediaStream: MediaStream) {
|
||||
engineStreamActor.send({
|
||||
type: EngineStreamTransition.SetMediaStream,
|
||||
mediaStream
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// setTimeout is avoided here because there is no need to create and destroy
|
||||
@ -82,7 +90,9 @@ export const EngineStream = () => {
|
||||
totalDelta = 0
|
||||
timestampLast = null
|
||||
needsResize = false
|
||||
configure()
|
||||
window.requestAnimationFrame(() => {
|
||||
configure()
|
||||
})
|
||||
} else {
|
||||
window.requestAnimationFrame(() => {
|
||||
needsResize = true
|
||||
@ -93,12 +103,116 @@ export const EngineStream = () => {
|
||||
}
|
||||
|
||||
window.addEventListener('resize', onResize)
|
||||
|
||||
const play = () => {
|
||||
engineStreamActor.send({
|
||||
type: EngineStreamTransition.Play,
|
||||
})
|
||||
}
|
||||
engineCommandManager.addEventListener(
|
||||
EngineCommandManagerEvents.SceneReady,
|
||||
play
|
||||
)
|
||||
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', onResize)
|
||||
}
|
||||
}, [settings])
|
||||
engineCommandManager.removeEventListener(
|
||||
EngineCommandManagerEvents.SceneReady,
|
||||
play
|
||||
)
|
||||
|
||||
const isNetworkOkay =
|
||||
}
|
||||
}, [])
|
||||
|
||||
// When the video and canvas element references are set, start the engine.
|
||||
useEffect(() => {
|
||||
if (engineStreamState.context.canvasRef.current && engineStreamState.context.videoRef.current) {
|
||||
engineStreamActor.send({
|
||||
type: EngineStreamTransition.StartOrReconfigureEngine,
|
||||
modelingMachineActorSend,
|
||||
settings: settingsEngine,
|
||||
setAppState,
|
||||
onMediaStream(mediaStream: MediaStream) {
|
||||
engineStreamActor.send({
|
||||
type: EngineStreamTransition.SetMediaStream,
|
||||
mediaStream
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [engineStreamState.context.canvasRef.current, engineStreamState.context.videoRef.current])
|
||||
|
||||
// On settings change, reconfigure the engine.
|
||||
useEffect(() => {
|
||||
engineStreamActor.send({ type: EngineStreamTransition.StartOrReconfigureEngine, modelingMachineActorSend, settings: settingsEngine, setAppState })
|
||||
}, [settings.context])
|
||||
|
||||
const IDLE_TIME_MS = 1000 * 6
|
||||
|
||||
// When streamIdleMode is changed, setup or teardown the timeouts
|
||||
const timeoutId = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
// If timeoutId is falsey, then reset it if steamIdleMode is true
|
||||
if (streamIdleMode && !timeoutId.current) {
|
||||
timeoutId.current = setTimeout(() => {
|
||||
engineStreamActor.send({ type: EngineStreamTransition.Pause })
|
||||
}, IDLE_TIME_MS)
|
||||
} else if (!streamIdleMode) {
|
||||
clearTimeout(timeoutId.current)
|
||||
timeoutId.current = undefined
|
||||
}
|
||||
}, [streamIdleMode])
|
||||
|
||||
useEffect(() => {
|
||||
if (!timeoutId.current) return
|
||||
|
||||
const onAnyInput = () => {
|
||||
// Just in case it happens in the middle of the user turning off
|
||||
// idle mode.
|
||||
if (!streamIdleMode) {
|
||||
clearTimeout(timeoutId.current)
|
||||
return
|
||||
}
|
||||
|
||||
if (engineStreamState.value === EngineStreamState.Paused) {
|
||||
engineStreamActor.send({
|
||||
type: EngineStreamTransition.StartOrReconfigureEngine,
|
||||
modelingMachineActorSend,
|
||||
settings: settingsEngine,
|
||||
setAppState,
|
||||
onMediaStream(mediaStream: MediaStream) {
|
||||
engineStreamActor.send({
|
||||
type: EngineStreamTransition.SetMediaStream,
|
||||
mediaStream
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
clearTimeout(timeoutId.current)
|
||||
timeoutId.current = setTimeout(() => {
|
||||
engineStreamActor.send({ type: EngineStreamTransition.Pause })
|
||||
}, IDLE_TIME_MS)
|
||||
}
|
||||
|
||||
window.document.addEventListener('keydown', onAnyInput)
|
||||
window.document.addEventListener('mousemove', onAnyInput)
|
||||
window.document.addEventListener('mousedown', onAnyInput)
|
||||
window.document.addEventListener('scroll', onAnyInput)
|
||||
window.document.addEventListener('touchstart', onAnyInput)
|
||||
|
||||
return () => {
|
||||
window.document.removeEventListener('keydown', onAnyInput)
|
||||
window.document.removeEventListener('mousemove', onAnyInput)
|
||||
window.document.removeEventListener('mousedown', onAnyInput)
|
||||
window.document.removeEventListener('scroll', onAnyInput)
|
||||
window.document.removeEventListener('touchstart', onAnyInput)
|
||||
}
|
||||
}, [streamIdleMode, engineStreamState.value])
|
||||
|
||||
const isNetworkOkay =
|
||||
overallState === NetworkHealthState.Ok ||
|
||||
overallState === NetworkHealthState.Weak
|
||||
|
||||
@ -106,7 +220,7 @@ export const EngineStream = () => {
|
||||
|
||||
const handleMouseUp: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||
if (!isNetworkOkay) return
|
||||
if (!videoRef.current) return
|
||||
if (!engineStreamState.context.videoRef.current) return
|
||||
|
||||
modelingMachineActorSend({
|
||||
type: 'Set context',
|
||||
@ -120,7 +234,7 @@ export const EngineStream = () => {
|
||||
if (!modelingMachineContext.store?.didDragInStream && btnName(e).left) {
|
||||
sendSelectEventToEngine(
|
||||
e,
|
||||
videoRef.current,
|
||||
engineStreamState.context.videoRef.current,
|
||||
modelingMachineContext.store?.streamDimensions
|
||||
)
|
||||
}
|
||||
@ -136,7 +250,7 @@ export const EngineStream = () => {
|
||||
|
||||
const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||
if (!isNetworkOkay) return
|
||||
if (!videoRef.current) return
|
||||
if (!engineStreamState.context.videoRef.current) return
|
||||
|
||||
if (modelingMachineState.matches('Sketch')) return
|
||||
if (modelingMachineState.matches('Sketch no face')) return
|
||||
@ -144,7 +258,7 @@ export const EngineStream = () => {
|
||||
const { x, y } = getNormalisedCoordinates({
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
el: videoRef.current,
|
||||
el: engineStreamState.context.videoRef.current,
|
||||
...modelingMachineContext.store?.streamDimensions,
|
||||
})
|
||||
|
||||
@ -188,60 +302,20 @@ export const EngineStream = () => {
|
||||
onContextMenuCapture={(e) => e.preventDefault()}
|
||||
>
|
||||
<video
|
||||
ref={videoRef}
|
||||
key={engineStreamActor.id + 'video'}
|
||||
ref={engineStreamState.context.videoRef}
|
||||
controls={false}
|
||||
onMouseMoveCapture={handleMouseMove}
|
||||
className="w-full cursor-pointer h-full"
|
||||
className="cursor-pointer"
|
||||
disablePictureInPicture
|
||||
id="video-stream"
|
||||
/>
|
||||
<canvas ref={canvasRef} className="w-full cursor-pointer h-full" id="freeze-frame">No canvas support</canvas>
|
||||
<canvas
|
||||
key={engineStreamActor.id + 'canvas'}
|
||||
ref={engineStreamState.context.canvasRef} className="cursor-pointer" id="freeze-frame">No canvas support</canvas>
|
||||
<ClientSideScene
|
||||
cameraControls={settings.context.modeling.mouseControls.current}
|
||||
/>
|
||||
{(engineStreamState.value === EngineStreamState.Paused ||
|
||||
engineStreamState.value === EngineStreamState.Resuming) && (
|
||||
<div className="text-center absolute inset-0">
|
||||
<div
|
||||
className="flex flex-col items-center justify-center h-screen"
|
||||
data-testid="paused"
|
||||
>
|
||||
<div className="border-primary border p-2 rounded-sm">
|
||||
<svg
|
||||
width="8"
|
||||
height="12"
|
||||
viewBox="0 0 8 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M2 12V0H0V12H2ZM8 12V0H6V12H8Z"
|
||||
fill="var(--primary)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<p className="text-base mt-2 text-primary bold">
|
||||
{engineStreamState.value === EngineStreamState.Paused && 'Paused'}
|
||||
{engineStreamState.value === EngineStreamState.Resuming && 'Resuming'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{(!isNetworkOkay || isLoading) && (
|
||||
<div className="text-center absolute inset-0">
|
||||
<Loading>
|
||||
{!isNetworkOkay && !isLoading ? (
|
||||
<span data-testid="loading-stream">Stream disconnected...</span>
|
||||
) : (
|
||||
!isLoading && (
|
||||
<span data-testid="loading-stream">Loading stream...</span>
|
||||
)
|
||||
)}
|
||||
</Loading>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user