mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
159 lines
4.0 KiB
TypeScript
159 lines
4.0 KiB
TypeScript
import React from 'react';
|
|
import Sockette from 'sockette';
|
|
import throttle from 'lodash/throttle';
|
|
import { withSnackbar, WithSnackbarProps } from 'notistack';
|
|
|
|
import { addAccessTokenParameter } from '../authentication';
|
|
import { extractEventValue } from '.';
|
|
|
|
export interface WebSocketControllerProps<D> extends WithSnackbarProps {
|
|
handleValueChange: (
|
|
name: keyof D
|
|
) => (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
|
|
setData: (data: D, callback?: () => void) => void;
|
|
saveData: () => void;
|
|
saveDataAndClear(): () => void;
|
|
|
|
connected: boolean;
|
|
data?: D;
|
|
}
|
|
|
|
interface WebSocketControllerState<D> {
|
|
ws: Sockette;
|
|
connected: boolean;
|
|
clientId?: string;
|
|
data?: D;
|
|
}
|
|
|
|
enum WebSocketMessageType {
|
|
ID = 'id',
|
|
PAYLOAD = 'payload'
|
|
}
|
|
|
|
interface WebSocketIdMessage {
|
|
type: typeof WebSocketMessageType.ID;
|
|
id: string;
|
|
}
|
|
|
|
interface WebSocketPayloadMessage<D> {
|
|
type: typeof WebSocketMessageType.PAYLOAD;
|
|
origin_id: string;
|
|
payload: D;
|
|
}
|
|
|
|
export type WebSocketMessage<D> =
|
|
| WebSocketIdMessage
|
|
| WebSocketPayloadMessage<D>;
|
|
|
|
export function webSocketController<D, P extends WebSocketControllerProps<D>>(
|
|
wsUrl: string,
|
|
wsThrottle: number,
|
|
WebSocketController: React.ComponentType<P & WebSocketControllerProps<D>>
|
|
) {
|
|
return withSnackbar(
|
|
class extends React.Component<
|
|
Omit<P, keyof WebSocketControllerProps<D>> & WithSnackbarProps,
|
|
WebSocketControllerState<D>
|
|
> {
|
|
constructor(
|
|
props: Omit<P, keyof WebSocketControllerProps<D>> & WithSnackbarProps
|
|
) {
|
|
super(props);
|
|
this.state = {
|
|
ws: new Sockette(addAccessTokenParameter(wsUrl), {
|
|
onmessage: this.onMessage,
|
|
onopen: this.onOpen,
|
|
onclose: this.onClose
|
|
}),
|
|
connected: false
|
|
};
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
this.state.ws.close();
|
|
}
|
|
|
|
onMessage = (event: MessageEvent) => {
|
|
const rawData = event.data;
|
|
if (typeof rawData === 'string' || rawData instanceof String) {
|
|
this.handleMessage(
|
|
JSON.parse(rawData as string) as WebSocketMessage<D>
|
|
);
|
|
}
|
|
};
|
|
|
|
handleMessage = (message: WebSocketMessage<D>) => {
|
|
const { clientId, data } = this.state;
|
|
|
|
switch (message.type) {
|
|
case WebSocketMessageType.ID:
|
|
this.setState({ clientId: message.id });
|
|
break;
|
|
case WebSocketMessageType.PAYLOAD:
|
|
if (clientId && (!data || clientId !== message.origin_id)) {
|
|
this.setState({ data: message.payload });
|
|
}
|
|
break;
|
|
}
|
|
};
|
|
|
|
onOpen = () => {
|
|
this.setState({ connected: true });
|
|
};
|
|
|
|
onClose = () => {
|
|
this.setState({
|
|
connected: false,
|
|
clientId: undefined,
|
|
data: undefined
|
|
});
|
|
};
|
|
|
|
setData = (data: D, callback?: () => void) => {
|
|
this.setState({ data }, callback);
|
|
};
|
|
|
|
saveData = throttle(() => {
|
|
const { ws, connected, data } = this.state;
|
|
if (connected) {
|
|
ws.json(data);
|
|
}
|
|
}, wsThrottle);
|
|
|
|
saveDataAndClear = throttle(() => {
|
|
const { ws, connected, data } = this.state;
|
|
if (connected) {
|
|
this.setState(
|
|
{
|
|
data: undefined
|
|
},
|
|
() => ws.json(data)
|
|
);
|
|
}
|
|
}, wsThrottle);
|
|
|
|
handleValueChange = (name: keyof D) => (
|
|
event: React.ChangeEvent<HTMLInputElement>
|
|
) => {
|
|
const data = { ...this.state.data!, [name]: extractEventValue(event) };
|
|
this.setState({ data });
|
|
};
|
|
|
|
render() {
|
|
return (
|
|
<WebSocketController
|
|
{...(this.props as P)}
|
|
handleValueChange={this.handleValueChange}
|
|
setData={this.setData}
|
|
saveData={this.saveData}
|
|
saveDataAndClear={this.saveDataAndClear}
|
|
connected={this.state.connected}
|
|
data={this.state.data}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
);
|
|
}
|