light_maymays/pult/frontend/src/Sliders.tsx

120 lines
2.9 KiB
TypeScript

import MuiSlider from "@mui/material/Slider";
import React from "react";
import { useState, useEffect, useRef } from "react";
type StateItem = {
value: number,
status: "open" | "owned" | "locked",
};
type State = Array<StateItem>;
type ClientAction = {
action_type: "grab" | "release",
slider: number,
} | {
action_type: "move",
slider: number,
new_value: number,
}
const Sliders: React.FC = () => {
const ws = useRef<WebSocket>();
const reconnectInterval = useRef<number>();
const [state, setState] = useState<State>();
const connect = () => {
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
if (ws.current !== undefined && ws.current.readyState !== 3) return;
const wsURL = new URL("ws", window.location.href);
wsURL.protocol = wsURL.protocol.replace("http", "ws");
ws.current = new WebSocket(wsURL.href);
ws.current.onmessage = (ev) => {
setState(JSON.parse(ev.data));
}
}
useEffect(() => {
connect();
reconnectInterval.current = window.setInterval(connect, 1000);
return () => {
if (reconnectInterval.current !== undefined) window.clearInterval(reconnectInterval.current);
if (ws.current !== undefined) ws.current.close();
};
}, []);
const cb = (action: ClientAction) => {
if (ws.current !== undefined && ws.current.readyState !== 3) {
ws.current.send(JSON.stringify({
action: action,
}));
}
}
return <>
{state?.map((item, index) => <Slider key={index} item={item} index={index} cb={cb} />)}
</>
}
const styleOverride = {
"& .MuiSlider-track": { transition: "none" },
"& .MuiSlider-thumb": { transition: "none" },
};
const Slider: React.FC<{
item: StateItem,
index: number,
cb: (action: ClientAction) => void,
}> = ({ item, index, cb }) => {
const disabled = item.status === "locked";
const [value, setValue] = useState(item.value);
useEffect(() => {
if (item.status !== "owned") setValue(item.value);
}, [item]);
const onChange = (n: number) => {
setValue(n);
cb({
action_type: "move",
slider: index,
new_value: n,
});
};
const onGrab = () => {
cb({
action_type: "grab",
slider: index,
});
};
const onRelease = () => {
cb({
action_type: "release",
slider: index,
});
};
return <MuiSlider
min={0}
max={255}
sx={disabled ? styleOverride : {}}
disabled={disabled}
value={value}
onChange={(_, n) => { onChange(n as number) }}
onMouseDown={onGrab}
onTouchStart={onGrab}
onMouseUp={onRelease}
onTouchEnd={onRelease}
/>
};
export default Sliders;