From fcd860c346e4753fc56e4b40e74caac962329a00 Mon Sep 17 00:00:00 2001 From: DUDEbehindDUDE Date: Wed, 20 Nov 2024 18:39:29 -0500 Subject: [PATCH 1/6] Add button component --- react/src/App.css | 38 ------------ react/src/App.test.tsx | 4 +- react/src/App.tsx | 43 ++++++++----- react/src/components/Button.tsx | 14 +++++ react/src/components/StatusIndicator.tsx | 7 +++ react/src/css/App.css | 79 ++++++++++++++++++++++++ react/src/css/Button.css | 16 +++++ react/src/types/button.ts | 7 +++ react/src/types/status.ts | 7 +++ 9 files changed, 158 insertions(+), 57 deletions(-) delete mode 100644 react/src/App.css create mode 100644 react/src/components/Button.tsx create mode 100644 react/src/components/StatusIndicator.tsx create mode 100644 react/src/css/App.css create mode 100644 react/src/css/Button.css create mode 100644 react/src/types/button.ts create mode 100644 react/src/types/status.ts diff --git a/react/src/App.css b/react/src/App.css deleted file mode 100644 index 74b5e05..0000000 --- a/react/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/react/src/App.test.tsx b/react/src/App.test.tsx index d76787e..75fc074 100644 --- a/react/src/App.test.tsx +++ b/react/src/App.test.tsx @@ -2,8 +2,8 @@ import React from "react"; import { render, screen } from "@testing-library/react"; import App from "./App"; -test("renders learn react link", () => { +test("renders start button", () => { render(); - const linkElement = screen.getByText(/learn react/i); + const linkElement = screen.getByText(/start/i); expect(linkElement).toBeInTheDocument(); }); diff --git a/react/src/App.tsx b/react/src/App.tsx index 9cc63d9..1a9e6e8 100644 --- a/react/src/App.tsx +++ b/react/src/App.tsx @@ -1,24 +1,33 @@ -import React from "react"; -import logo from "./logo.svg"; -import "./App.css"; +import { useEffect, useState } from "react"; +import "./css/App.css"; +import Button from "./components/Button"; +import { Status } from "./types/status"; function App() { + const [botStatus, setBotStatus] = useState(Status.Offline); + + function startBot() { + if (botStatus === Status.Ok) return; + if (botStatus === Status.Offline) { + setBotStatus(Status.Loading); + // set to Status.Ok in 5s + } + } + + useEffect(() => { + if (botStatus === Status.Loading) { + const timeoutId = setTimeout(() => { + setBotStatus(Status.Ok); + }, 5000); + + return () => clearTimeout(timeoutId); // Cleanup function + } + }, [botStatus]); + return (
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
+
); } diff --git a/react/src/components/Button.tsx b/react/src/components/Button.tsx new file mode 100644 index 0000000..401b1f0 --- /dev/null +++ b/react/src/components/Button.tsx @@ -0,0 +1,14 @@ +import "../css/Button.css"; +import StatusIndicator from "./StatusIndicator"; +import { ButtonProps } from "../types/button"; + +function Button(props: ButtonProps) { + return ( + + ); +} + +export default Button; diff --git a/react/src/components/StatusIndicator.tsx b/react/src/components/StatusIndicator.tsx new file mode 100644 index 0000000..72e8898 --- /dev/null +++ b/react/src/components/StatusIndicator.tsx @@ -0,0 +1,7 @@ +import { Status } from "../types/button"; + +function StatusIndicator(props: { status?: Status }) { + return <>{props.status && }; +} + +export default StatusIndicator; diff --git a/react/src/css/App.css b/react/src/css/App.css new file mode 100644 index 0000000..c8cdf43 --- /dev/null +++ b/react/src/css/App.css @@ -0,0 +1,79 @@ +.App { + text-align: center; + min-height: 100vh; + /* display: flex; */ + /* flex-direction: row; */ + /* align-items: center; */ + /* justify-content: center; */ + font-size: calc(10px + 2vmin); + color: white; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.StatusOk { + display: inline-block; + margin-right: .25em; + width: .5em; + height: .5em; + border-radius: 1em; + background-color: limegreen; +} + +.StatusLoading { + display: inline-block; + margin-right: .25em; + width: .5em; + height: .5em; + border-radius: 1em; + background-color: magenta; +} + +.StatusWarning { + display: inline-block; + margin-right: .25em; + width: .5em; + height: .5em; + border-radius: 1em; + background-color: orange; +} + +.StatusError { + display: inline-block; + margin-right: .25em; + width: .5em; + height: .5em; + border-radius: 1em; + background-color: red; +} + +.StatusOffline { + display: inline-block; + margin-right: .25em; + width: .5em; + height: .5em; + border-radius: 1em; + background-color: lightgray; +} diff --git a/react/src/css/Button.css b/react/src/css/Button.css new file mode 100644 index 0000000..09767e4 --- /dev/null +++ b/react/src/css/Button.css @@ -0,0 +1,16 @@ +.Button { + /* display: inline; */ + padding: 4px; + margin: 2px; + border: 2px solid #a49665; + background-color: #005035; + color: white; +} + +.Button:hover { + background-color: #003d29; +} + +.Button:active { + background-color: #002719; +} \ No newline at end of file diff --git a/react/src/types/button.ts b/react/src/types/button.ts new file mode 100644 index 0000000..845dd50 --- /dev/null +++ b/react/src/types/button.ts @@ -0,0 +1,7 @@ +import { Status } from "./status"; + +export type ButtonProps = { + name: string; + onClick?: () => void; + status?: Status; +}; diff --git a/react/src/types/status.ts b/react/src/types/status.ts new file mode 100644 index 0000000..9cd3585 --- /dev/null +++ b/react/src/types/status.ts @@ -0,0 +1,7 @@ +export enum Status { + Ok = "StatusOk", + Loading = "StatusLoading", + Warning = "StatusWarning", + Error = "StatusError", + Offline = "StatusOffline", +} From f6fd7997264dad90d62b135ee4b514eb36c3b07f Mon Sep 17 00:00:00 2001 From: DUDEbehindDUDE Date: Wed, 20 Nov 2024 18:49:43 -0500 Subject: [PATCH 2/6] Fix prettier formatting --- react/src/css/App.css | 62 ++++++++++++++++++++-------------------- react/src/css/Button.css | 18 ++++++------ 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/react/src/css/App.css b/react/src/css/App.css index c8cdf43..b45537b 100644 --- a/react/src/css/App.css +++ b/react/src/css/App.css @@ -1,6 +1,6 @@ .App { text-align: center; - min-height: 100vh; + min-height: 100vh; /* display: flex; */ /* flex-direction: row; */ /* align-items: center; */ @@ -34,46 +34,46 @@ } .StatusOk { - display: inline-block; - margin-right: .25em; - width: .5em; - height: .5em; - border-radius: 1em; - background-color: limegreen; + display: inline-block; + margin-right: 0.25em; + width: 0.5em; + height: 0.5em; + border-radius: 1em; + background-color: limegreen; } .StatusLoading { - display: inline-block; - margin-right: .25em; - width: .5em; - height: .5em; - border-radius: 1em; - background-color: magenta; + display: inline-block; + margin-right: 0.25em; + width: 0.5em; + height: 0.5em; + border-radius: 1em; + background-color: magenta; } .StatusWarning { - display: inline-block; - margin-right: .25em; - width: .5em; - height: .5em; - border-radius: 1em; - background-color: orange; + display: inline-block; + margin-right: 0.25em; + width: 0.5em; + height: 0.5em; + border-radius: 1em; + background-color: orange; } .StatusError { - display: inline-block; - margin-right: .25em; - width: .5em; - height: .5em; - border-radius: 1em; - background-color: red; + display: inline-block; + margin-right: 0.25em; + width: 0.5em; + height: 0.5em; + border-radius: 1em; + background-color: red; } .StatusOffline { - display: inline-block; - margin-right: .25em; - width: .5em; - height: .5em; - border-radius: 1em; - background-color: lightgray; + display: inline-block; + margin-right: 0.25em; + width: 0.5em; + height: 0.5em; + border-radius: 1em; + background-color: lightgray; } diff --git a/react/src/css/Button.css b/react/src/css/Button.css index 09767e4..740284c 100644 --- a/react/src/css/Button.css +++ b/react/src/css/Button.css @@ -1,16 +1,16 @@ .Button { - /* display: inline; */ - padding: 4px; - margin: 2px; - border: 2px solid #a49665; - background-color: #005035; - color: white; + /* display: inline; */ + padding: 4px; + margin: 2px; + border: 2px solid #a49665; + background-color: #005035; + color: white; } .Button:hover { - background-color: #003d29; + background-color: #003d29; } .Button:active { - background-color: #002719; -} \ No newline at end of file + background-color: #002719; +} From 860e0b1608b850fa2b540853897020ff5ae4d891 Mon Sep 17 00:00:00 2001 From: DUDEbehindDUDE Date: Wed, 20 Nov 2024 18:52:42 -0500 Subject: [PATCH 3/6] Use absolute imports --- react/src/App.tsx | 6 +++--- react/src/components/Button.tsx | 4 ++-- react/src/components/StatusIndicator.tsx | 2 +- react/src/types/button.ts | 1 + react/tsconfig.json | 3 ++- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/react/src/App.tsx b/react/src/App.tsx index 1a9e6e8..7ca633f 100644 --- a/react/src/App.tsx +++ b/react/src/App.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; -import "./css/App.css"; -import Button from "./components/Button"; -import { Status } from "./types/status"; +import "css/App.css"; +import Button from "components/Button"; +import { Status } from "types/status"; function App() { const [botStatus, setBotStatus] = useState(Status.Offline); diff --git a/react/src/components/Button.tsx b/react/src/components/Button.tsx index 401b1f0..3528ea4 100644 --- a/react/src/components/Button.tsx +++ b/react/src/components/Button.tsx @@ -1,6 +1,6 @@ -import "../css/Button.css"; +import { ButtonProps } from "types/button"; +import "css/Button.css"; import StatusIndicator from "./StatusIndicator"; -import { ButtonProps } from "../types/button"; function Button(props: ButtonProps) { return ( diff --git a/react/src/components/StatusIndicator.tsx b/react/src/components/StatusIndicator.tsx index 72e8898..7e9b4af 100644 --- a/react/src/components/StatusIndicator.tsx +++ b/react/src/components/StatusIndicator.tsx @@ -1,4 +1,4 @@ -import { Status } from "../types/button"; +import { Status } from "types/button"; function StatusIndicator(props: { status?: Status }) { return <>{props.status && }; diff --git a/react/src/types/button.ts b/react/src/types/button.ts index 845dd50..2bb6ed3 100644 --- a/react/src/types/button.ts +++ b/react/src/types/button.ts @@ -5,3 +5,4 @@ export type ButtonProps = { onClick?: () => void; status?: Status; }; +export { Status }; diff --git a/react/tsconfig.json b/react/tsconfig.json index 810b7f6..75fbeaa 100644 --- a/react/tsconfig.json +++ b/react/tsconfig.json @@ -14,7 +14,8 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx" + "jsx": "react-jsx", + "baseUrl": "src" }, "include": ["src"] } From afc7615767243bfbe53280e20107193cdf1871fb Mon Sep 17 00:00:00 2001 From: DUDEbehindDUDE <66212375+DUDEbehindDUDE@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:33:22 -0500 Subject: [PATCH 4/6] Read ros websocket data --- react/package.json | 1 + react/src/App.tsx | 13 ++- react/src/components/Console.tsx | 93 +++++++++++++++++++ react/src/components/WebSocketContext.tsx | 106 ++++++++++++++++++++++ react/src/css/App.css | 1 - react/src/css/Console.css | 25 +++++ react/src/types/websocket.ts | 24 +++++ react/yarn.lock | 5 + 8 files changed, 265 insertions(+), 3 deletions(-) create mode 100644 react/src/components/Console.tsx create mode 100644 react/src/components/WebSocketContext.tsx create mode 100644 react/src/css/Console.css create mode 100644 react/src/types/websocket.ts diff --git a/react/package.json b/react/package.json index caf5ab6..21e739c 100644 --- a/react/package.json +++ b/react/package.json @@ -11,6 +11,7 @@ "@types/node": "^16.7.13", "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", + "date-fns": "^4.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-scripts": "5.0.1", diff --git a/react/src/App.tsx b/react/src/App.tsx index 7ca633f..2ba9545 100644 --- a/react/src/App.tsx +++ b/react/src/App.tsx @@ -2,6 +2,8 @@ import { useEffect, useState } from "react"; import "css/App.css"; import Button from "components/Button"; import { Status } from "types/status"; +import Console from "components/Console"; +import { WebSocketProvider } from "components/WebSocketContext"; function App() { const [botStatus, setBotStatus] = useState(Status.Offline); @@ -13,6 +15,10 @@ function App() { // set to Status.Ok in 5s } } + function stopBot() { + if (botStatus !== Status.Ok) return; + setBotStatus(Status.Offline); + } useEffect(() => { if (botStatus === Status.Loading) { @@ -26,8 +32,11 @@ function App() { return (
-
); } diff --git a/react/src/components/Console.tsx b/react/src/components/Console.tsx new file mode 100644 index 0000000..7d69a8a --- /dev/null +++ b/react/src/components/Console.tsx @@ -0,0 +1,93 @@ +import { useEffect, useRef, useState } from "react"; +import { format } from "date-fns"; +import { useWebSocket } from "./WebSocketContext"; +import "css/Console.css"; + +function Console() { + const consoleOutputRef = useRef(null); + const [socketHistory, setSocketHistory] = useState([]); + const [autoScroll, setAutoScroll] = useState(true); + const maxHistoryLength = 1000; // in lines + const { data } = useWebSocket({ + // All the topics we want to subscribe to. This should be pretty + // much every topic that exists on ros. Currently topic and topic2 + // for testing but it won't error if the topics don't exist. + includeTopics: ["/topic", "/topic2"], + }); + + // update history when receiving new data + useEffect(() => { + if (data === null) return; + + setSocketHistory((prev: string[]) => { + const time = format(new Date(), "HH:mm:ss.SSS"); + const topic = data.topic; + const body = data.msg.data; + const nextMsg = `[${time}] [${topic}]: ${body}`; + const newHistory = [...prev, nextMsg]; + if (newHistory.length > maxHistoryLength) { + newHistory.splice(0, 1); + } + return newHistory; + }); + }, [data]); + + // Console scroll stuff + useEffect(() => { + if (!consoleOutputRef.current) return; + + // Keeps the console scrolled at the bottom + if (autoScroll) { + const { current } = consoleOutputRef; + current.scrollTop = current.scrollHeight; + } + + // Scrolls up when console history is full and more lines get added to prevent it from shifting down + if (!autoScroll && socketHistory.length === maxHistoryLength) { + const { current } = consoleOutputRef; + current.scrollTop -= 14; // 14px represents roughly 1 line + } + }, [socketHistory, autoScroll]); + + function checkEnableAutoScroll() { + if (consoleOutputRef.current) { + const { current } = consoleOutputRef; + // this confused me, it's total height - amount scrolled - height of what's visible + let distFromBottom = current.scrollHeight - current.scrollTop; + distFromBottom -= current.clientHeight; + + // 10px to add a little bit of margin + if (distFromBottom < 10) { + // Re-enable auto scroll when the user scrolls to the bottom + setAutoScroll(true); + } else { + // Disable auto scroll when the user scrolls manually + setAutoScroll(false); + } + } + } + + function renderConsoleText() { + let text = socketHistory.join("\n"); + if (text === "") text = "Waiting for messages..."; + if (socketHistory.length === maxHistoryLength) { + text = `...history limited to ${maxHistoryLength} lines\n${text}`; + } + return text; + } + + return ( +
+

Console

+
+
{renderConsoleText()}
+
+
+ ); +} + +export default Console; diff --git a/react/src/components/WebSocketContext.tsx b/react/src/components/WebSocketContext.tsx new file mode 100644 index 0000000..386a177 --- /dev/null +++ b/react/src/components/WebSocketContext.tsx @@ -0,0 +1,106 @@ +import { + createContext, + useCallback, + useContext, + useEffect, + useRef, + useState, +} from "react"; +import { + RosDataType, + SocketResponse, + WebSocketContextType, +} from "types/websocket"; + +const WebSocketContext = createContext( + undefined +); + +export function WebSocketProvider({ children }: { children: React.ReactNode }) { + const [socket, setSocket] = useState(null); + const listeners = useRef void>>(new Set()); + + useEffect(() => { + // if it already exists it already has all this stuff set + if (socket !== null) return; + + const toSubscribe = [ + { topic: "/topic", type: RosDataType.String }, + { topic: "/topic2", type: RosDataType.String }, + ]; + + const _socket = new WebSocket("ws://127.0.0.1:9090"); + _socket.onopen = () => { + console.log("Connected to ROS bridge"); + toSubscribe.forEach((item) => { + _socket.send(JSON.stringify({ op: "subscribe", ...item })); + }); + }; + + _socket.onmessage = (event) => { + const data: SocketResponse = JSON.parse(event.data); + listeners.current.forEach((listener) => listener(data)); + }; + + _socket.onerror = (error) => { + console.error("WebSocket error", error); + }; + + _socket.onclose = () => { + console.log("Socket closed"); + setSocket(null); + }; + + setSocket(_socket); + }, [socket]); + + function sendMessage(msg: object) { + if (socket && socket.readyState === WebSocket.OPEN) { + socket.send(JSON.stringify(msg)); + } + } + + const addMessageListener = useCallback((callback: (data: any) => void) => { + listeners.current.add(callback); + return () => { + listeners.current.delete(callback); + }; + }, []); + + return ( + + {children} + + ); +} + +// Custom hook to use WebSocket data with filtering options +export function useWebSocket(filterOptions: { + includeTopics?: string[]; + excludeTopics?: string[]; +}) { + const context = useContext(WebSocketContext); + if (!context) { + throw new Error("useWebSocket must be used within a WebSocketProvider"); + } + + const { addMessageListener, sendMessage } = context; + const [data, setData] = useState(null); + + useEffect(() => { + function handleData(data: SocketResponse) { + const { includeTopics, excludeTopics } = filterOptions; + const topic = data.topic; + + if (includeTopics && !includeTopics.includes(topic)) return; + if (excludeTopics && excludeTopics.includes(topic)) return; + + setData(data); + } + + const removeListener = addMessageListener(handleData); + return removeListener; + }, [addMessageListener, filterOptions]); + + return { data, sendMessage }; +} diff --git a/react/src/css/App.css b/react/src/css/App.css index b45537b..0174b40 100644 --- a/react/src/css/App.css +++ b/react/src/css/App.css @@ -6,7 +6,6 @@ /* align-items: center; */ /* justify-content: center; */ font-size: calc(10px + 2vmin); - color: white; } .App-logo { diff --git a/react/src/css/Console.css b/react/src/css/Console.css new file mode 100644 index 0000000..d821f66 --- /dev/null +++ b/react/src/css/Console.css @@ -0,0 +1,25 @@ +.Console { + width: 100vw; + height: 60vh; + display: flex; + flex-direction: column; + justify-content: center; + h1 { + margin: 10px; + font-size: xx-large; + } +} + +.ConsoleBody { + align-self: center; + font-size: small; + background-color: #000000d8; + color: white; + text-align: left; + width: 80vw; + height: 60vh; + text-align: left; + overflow-y: scroll; + border-radius: 1em; + padding: 0.5em; +} diff --git a/react/src/types/websocket.ts b/react/src/types/websocket.ts new file mode 100644 index 0000000..3074ecf --- /dev/null +++ b/react/src/types/websocket.ts @@ -0,0 +1,24 @@ +export enum RosDataType { + String = "std_msgs/String", +} + +export type RosSubscriber = { + topic: string; + type: RosDataType | string; +}; + +export type WebSocketContextType = { + // object for now; temporary + sendMessage: (msg: object) => void; + addMessageListener: (callback: (data: any) => void) => () => void; +}; + +export type SocketResponse = { + msg: SocketResponseMsg; + topic: string; + op: string; +}; + +export type SocketResponseMsg = { + data: any; +}; diff --git a/react/yarn.lock b/react/yarn.lock index 0d71100..467d046 100644 --- a/react/yarn.lock +++ b/react/yarn.lock @@ -3700,6 +3700,11 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +date-fns@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14" + integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== + debug@2.6.9, debug@^2.6.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" From e482110e6d1330d324c6dd7fb9cec67c0ded91c2 Mon Sep 17 00:00:00 2001 From: DUDEbehindDUDE <66212375+DUDEbehindDUDE@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:35:07 -0500 Subject: [PATCH 5/6] Move App and tests to their respective folders --- react/src/{ => components}/App.tsx | 0 react/src/index.tsx | 6 +++--- react/src/{ => test}/App.test.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename react/src/{ => components}/App.tsx (100%) rename react/src/{ => test}/App.test.tsx (87%) diff --git a/react/src/App.tsx b/react/src/components/App.tsx similarity index 100% rename from react/src/App.tsx rename to react/src/components/App.tsx diff --git a/react/src/index.tsx b/react/src/index.tsx index 4fa2a47..2f3b8e9 100644 --- a/react/src/index.tsx +++ b/react/src/index.tsx @@ -1,16 +1,16 @@ import React from "react"; import ReactDOM from "react-dom/client"; import "./index.css"; -import App from "./App"; +import App from "./components/App"; import reportWebVitals from "./reportWebVitals"; const root = ReactDOM.createRoot( - document.getElementById("root") as HTMLElement, + document.getElementById("root") as HTMLElement ); root.render( - , + ); // If you want to start measuring performance in your app, pass a function diff --git a/react/src/App.test.tsx b/react/src/test/App.test.tsx similarity index 87% rename from react/src/App.test.tsx rename to react/src/test/App.test.tsx index 75fc074..dbb2640 100644 --- a/react/src/App.test.tsx +++ b/react/src/test/App.test.tsx @@ -1,6 +1,6 @@ import React from "react"; import { render, screen } from "@testing-library/react"; -import App from "./App"; +import App from "components/App"; test("renders start button", () => { render(); From 68d54c1786ecd46fb38cc5f01218534c82ba135d Mon Sep 17 00:00:00 2001 From: DUDEbehindDUDE <66212375+DUDEbehindDUDE@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:43:39 -0500 Subject: [PATCH 6/6] Use prettier yarn package, add prettier configs, force lf line endings --- .github/workflows/build.yml | 2 +- react/.prettierignore | 4 ++++ react/.prettierrc | 3 +++ react/package.json | 3 ++- react/src/components/WebSocketContext.tsx | 2 +- react/src/index.tsx | 4 ++-- react/yarn.lock | 5 +++++ 7 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 react/.prettierignore create mode 100644 react/.prettierrc diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 78519ac..228a445 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,6 @@ jobs: cache: "yarn" cache-dependency-path: react/yarn.lock - run: yarn - - run: npx prettier . --check - run: yarn build --if-present - run: yarn test + - run: yarn prettier . --check diff --git a/react/.prettierignore b/react/.prettierignore new file mode 100644 index 0000000..e665aa0 --- /dev/null +++ b/react/.prettierignore @@ -0,0 +1,4 @@ +# Ignore yarn.lock and other Yarn-specific files +yarn.lock +package.json +**/node_modules/* \ No newline at end of file diff --git a/react/.prettierrc b/react/.prettierrc new file mode 100644 index 0000000..a598fca --- /dev/null +++ b/react/.prettierrc @@ -0,0 +1,3 @@ +{ + "endOfLine": "lf" +} diff --git a/react/package.json b/react/package.json index 21e739c..ff63405 100644 --- a/react/package.json +++ b/react/package.json @@ -19,7 +19,8 @@ "web-vitals": "^2.1.0" }, "devDependencies": { - "@babel/plugin-proposal-private-property-in-object": "^7.21.11" + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "prettier": "^3.3.3" }, "scripts": { "start": "react-scripts start", diff --git a/react/src/components/WebSocketContext.tsx b/react/src/components/WebSocketContext.tsx index 386a177..ab1f89a 100644 --- a/react/src/components/WebSocketContext.tsx +++ b/react/src/components/WebSocketContext.tsx @@ -13,7 +13,7 @@ import { } from "types/websocket"; const WebSocketContext = createContext( - undefined + undefined, ); export function WebSocketProvider({ children }: { children: React.ReactNode }) { diff --git a/react/src/index.tsx b/react/src/index.tsx index 2f3b8e9..e23932a 100644 --- a/react/src/index.tsx +++ b/react/src/index.tsx @@ -5,12 +5,12 @@ import App from "./components/App"; import reportWebVitals from "./reportWebVitals"; const root = ReactDOM.createRoot( - document.getElementById("root") as HTMLElement + document.getElementById("root") as HTMLElement, ); root.render( - + , ); // If you want to start measuring performance in your app, pass a function diff --git a/react/yarn.lock b/react/yarn.lock index 467d046..ea9771c 100644 --- a/react/yarn.lock +++ b/react/yarn.lock @@ -7727,6 +7727,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== +prettier@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" + integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== + pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"