Skip to content

Commit

Permalink
refactor to react hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
satetsu888 committed Jun 1, 2024
1 parent d63b0d7 commit 60f2d0e
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 110 deletions.
122 changes: 12 additions & 110 deletions src/components/RemoteController.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef, useState } from "react"
import { State } from "../types"
import { useEffect } from "react"
import { useVeadotube } from "../hooks/useVeadotube"

type Props = {
host: string,
Expand All @@ -8,114 +8,19 @@ type Props = {
}

export const RemoteController = ({host, port, setConnectionStarted}: Props) => {
const [states, setStates] = useState<State[]>([])
const [currentStateId, setCurrentStateId] = useState<string | null>(null)
const socketRef = useRef<WebSocket>()
const messageReceived = useRef(false)

const INITIAL_LIST_EVENT_PAYLOAD = 'nodes: {"event": "list"}'
const STATE_EVENTS_PAYLOAD = (input: string) => {
return `nodes: {
"event": "payload",
"type": "stateEvents",
"id": "mini",
"payload": ${input}
}`;
};

const onMessage = useCallback((event: MessageEvent<string>) => {
messageReceived.current = true

const str = event.data.replace(/.*nodes:/, '').replaceAll('\u0000', '')
const json = JSON.parse(str)

if (json.event === 'payload') {
const payload = json.payload

switch (payload.event) {
case 'list':
setStates(payload.states)
for (const state of payload.states) {
socketRef.current?.send(
STATE_EVENTS_PAYLOAD(`{"event": "thumb", "state": "${state.id}"}`)
);
}
break
case 'thumb':
setStates((prev) => {
const newState = prev.map((state) => {
if (state.id === payload.state) {
return { ...state, image: {
width: payload.width,
height: payload.height,
data: payload.png,
} }
}
return state
})
return newState
})
break
const { start, messageReceived, states, currentStateId, setCurrentState } = useVeadotube(
host,
port,
() => {
if (import.meta.env.PROD) {
setConnectionStarted(false);
}

}
console.log('json', json)
},[])
);

useEffect(() => {
try {
const websocket = new WebSocket(
`ws://${host}:${port}?n=remote-veadotube`
);
socketRef.current = websocket;

websocket.addEventListener("open", () => {
console.log("open");
websocket.send(INITIAL_LIST_EVENT_PAYLOAD);
websocket.send(STATE_EVENTS_PAYLOAD('{"event": "list"}'));
});

websocket.addEventListener("close", () => {
console.log("close");
});

websocket.addEventListener("error", (event) => {
console.error("error", event);

if (event instanceof Error) {
if (import.meta.env.PROD) {
setConnectionStarted(false);
alert(event.message);
}
}
});

websocket.addEventListener("message", onMessage);

const timeout = setTimeout(() => {
if (!messageReceived.current) {
websocket.close();
setConnectionStarted(false);
alert("Connection timed out. No response from server.");
}
}, 3000);

return () => {
websocket.close();
websocket.removeEventListener("message", onMessage);
websocket.removeEventListener("open", () => {});
websocket.removeEventListener("close", () => {});
websocket.removeEventListener("error", () => {});
clearTimeout(timeout);
};
} catch (error) {
console.error("error", error);
if (error instanceof Error) {
setConnectionStarted(false);
alert(error.message);
}
}
}, [onMessage, host, port, setConnectionStarted])
start();
}, [start]);

return (
<>
Expand All @@ -127,10 +32,7 @@ export const RemoteController = ({host, port, setConnectionStarted}: Props) => {
style={state.id === currentStateId ? { backgroundColor: 'lightblue' } : {}}
key={state.id}
onClick={() => {
socketRef.current?.send(
STATE_EVENTS_PAYLOAD(`{"event": "set", "state": "${state.id}"}`)
);
setCurrentStateId(state.id)
setCurrentState(state.id)
}}
>
{state.image ? <img src={`data:image/png;base64,${state.image.data}`} alt={state.name} width={state.image.width} height={state.image.height} /> : state.name}
Expand Down
145 changes: 145 additions & 0 deletions src/hooks/useVeadotube.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { State } from "../types";

export const useVeadotube = (
host: string,
port: string,
onCloseCallback: (e: Event) => void
) => {
const [connectionStarted, setConnectionStarted] = useState<boolean>(false);

const [states, setStates] = useState<State[]>([]);
const [currentStateId, setCurrentStateId] = useState<string | null>(null);
const websocketRef = useRef<WebSocket>();
const messageReceived = useRef(false);

const INITIAL_LIST_EVENT_PAYLOAD = 'nodes: {"event": "list"}';
const STATE_EVENTS_PAYLOAD = (input: string) => {
return `nodes: {
"event": "payload",
"type": "stateEvents",
"id": "mini",
"payload": ${input}
}`;
};

const start = () => {
setConnectionStarted(true);
};

const setCurrentState = (stateId: string) => {
websocketRef.current?.send(
STATE_EVENTS_PAYLOAD(`{"event": "set", "state": "${stateId}"}`)
);
setCurrentStateId(stateId);
};

const onMessage = useCallback((event: MessageEvent<string>) => {
messageReceived.current = true;

const str = event.data.replace(/.*nodes:/, "").replaceAll("\u0000", "");
const json = JSON.parse(str);

if (json.event === "payload") {
const payload = json.payload;

switch (payload.event) {
case "list":
setStates(payload.states);
for (const state of payload.states) {
websocketRef.current?.send(
STATE_EVENTS_PAYLOAD(`{"event": "thumb", "state": "${state.id}"}`)
);
}
break;
case "thumb":
setStates((prev) => {
const newState = prev.map((state) => {
if (state.id === payload.state) {
return {
...state,
image: {
width: payload.width,
height: payload.height,
data: payload.png,
},
};
}
return state;
});
return newState;
});
break;
}
}
console.log("json", json);
}, []);

useEffect(() => {
console.log("connectionStarted", connectionStarted)
if (!connectionStarted) {
return;
}

try {
const websocket = new WebSocket(
`ws://${host}:${port}?n=remote-veadotube`
);
websocketRef.current = websocket;

websocket.addEventListener("open", () => {
console.log("open");
websocket.send(INITIAL_LIST_EVENT_PAYLOAD);
websocket.send(STATE_EVENTS_PAYLOAD('{"event": "list"}'));
});

websocket.addEventListener("close", (event) => {
console.log("close");
onCloseCallback(event);
});

websocket.addEventListener("error", (event) => {
console.error("error", event);

if (event instanceof Error) {
setConnectionStarted(false);
alert(event.message);
}
});

websocket.addEventListener("message", onMessage);

const timeout = setTimeout(() => {
if (!messageReceived.current) {
websocket.close();
setConnectionStarted(false);
alert("Connection timed out. No response from server.");
}
}, 3000);

return () => {
websocket.close();
websocket.removeEventListener("message", onMessage);
websocket.removeEventListener("open", () => {});
websocket.removeEventListener("close", () => {});
websocket.removeEventListener("error", () => {});
clearTimeout(timeout);
};
} catch (error) {
console.error("error", error);
if (error instanceof Error) {
setConnectionStarted(false);
alert(error.message);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [connectionStarted]);

return {
start,
messageReceived,
states,
currentStateId,
setCurrentState,
};
};

0 comments on commit 60f2d0e

Please sign in to comment.