studiox-streamer/frontend/src/state.js

75 lines
2.1 KiB
JavaScript

import React from 'react'
import { reducer, action, CONNECTION_STATE, INITIAL_STATE } from '../../common/state.mjs'
function getConfig () {
return {
endpoint: process.env.NODE_ENV === 'development' ? 'http://localhost:3030' : ''
}
}
function useConfig () {
return getConfig()
}
export function useCommand (defaultCommand, defaultArgs = {}) {
const config = useConfig()
const url = config.endpoint + '/command'
const headers = { 'content-type': 'application/json' }
const [pending, setPending] = React.useState(false)
const [error, setError] = React.useState(null)
const [success, setSuccess] = React.useState(null)
return { pending, error, success, dispatch }
async function dispatch (command = defaultCommand, args = defaultArgs) {
setPending(true)
try {
const res = await fetch(url, {
headers,
method: 'post',
body: JSON.stringify({ command, args })
})
const json = await res.json()
if (json.error) throw new Error('Remote error: ' + json.details)
setSuccess(json)
} catch (err) {
setError(error)
} finally {
setPending(false)
}
}
}
export function useRemoteState (opts = {}) {
const { retryInterval = 1000 } = opts
const config = useConfig()
const [retry, setRetry] = React.useState(0)
const url = config.endpoint + '/sse'
const [state, dispatch] = React.useReducer(reducer, INITIAL_STATE)
const es = React.useRef()
function reconnect () {
if (es.current) es.current.close()
es.current = null
setRetry(retry => retry + 1)
}
// setup event source
React.useEffect(() => {
if (es.current) return
es.current = new EventSource(url)
}, [retry])
// process event source
React.useEffect(() => {
if (!es.current) return
const eventSource = es.current
eventSource.onmessage = (event) => {
const action = JSON.parse(event.data)
dispatch(action)
}
eventSource.onerror = () => {
dispatch(action(CONNECTION_STATE, { error: `Failed to connect to backend` }))
if (retryInterval) setTimeout(reconnect, retryInterval)
}
}, [es.current])
return { state }
}