75 lines
2.1 KiB
JavaScript
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 }
|
|
}
|