diff --git a/hooks/useLineData.js b/hooks/useLineData.js
index b5c7267d7..e89ca06a7 100644
--- a/hooks/useLineData.js
+++ b/hooks/useLineData.js
@@ -1,8 +1,12 @@
// /hooks/useLineData.js
import { useEffect, useState } from "react";
import { SERVER_URL } from "../config/urls";
+import { useDispatch, useSelector } from "react-redux";
+import { connectWebSocket, disconnectWebSocket } from "../redux/actions";
const useLineData = (webserviceGisLinesStatusUrl, setLineStatusData) => {
+ const dispatch = useDispatch();
+ const messages = useSelector((state) => state.messages);
const [lineColors, setLineColors] = useState({});
const [tooltipContents, setTooltipContents] = useState({});
diff --git a/package-lock.json b/package-lock.json
index 2c403330d..691d7655d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,9 +23,13 @@
"postcss": "^8.4.40",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-redux": "^9.1.2",
"react-toastify": "^10.0.5",
"recoil": "^0.7.7",
- "tailwindcss": "^3.4.7"
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "tailwindcss": "^3.4.7",
+ "ws": "^8.18.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
@@ -3828,6 +3832,11 @@
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
"dev": true
},
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
+ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
+ },
"node_modules/@types/yargs": {
"version": "17.0.32",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
@@ -10580,6 +10589,28 @@
"dev": true,
"peer": true
},
+ "node_modules/react-redux": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz",
+ "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==",
+ "dependencies": {
+ "@types/use-sync-external-store": "^0.0.3",
+ "use-sync-external-store": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.2.25",
+ "react": "^18.0",
+ "redux": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "redux": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-toastify": {
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz",
@@ -10673,6 +10704,19 @@
"node": ">=8"
}
},
+ "node_modules/redux": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
+ "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
+ },
+ "node_modules/redux-thunk": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
+ "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
+ "peerDependencies": {
+ "redux": "^5.0.0"
+ }
+ },
"node_modules/regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@@ -11818,6 +11862,14 @@
"requires-port": "^1.0.0"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
+ "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -12152,7 +12204,6 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
- "dev": true,
"engines": {
"node": ">=10.0.0"
},
diff --git a/package.json b/package.json
index 971c1fc6d..90097e17d 100644
--- a/package.json
+++ b/package.json
@@ -18,9 +18,13 @@
"postcss": "^8.4.40",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-redux": "^9.1.2",
"react-toastify": "^10.0.5",
"recoil": "^0.7.7",
- "tailwindcss": "^3.4.7"
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "tailwindcss": "^3.4.7",
+ "ws": "^8.18.0"
},
"scripts": {
"dev": "next dev",
diff --git a/pages/_app.js b/pages/_app.js
index fc74da978..1a2213940 100644
--- a/pages/_app.js
+++ b/pages/_app.js
@@ -1,13 +1,17 @@
// Pfad: pages/_app.js
import React from "react";
import { RecoilRoot } from "recoil";
+import { Provider } from "react-redux";
+import store from "../redux/store";
import "../styles/global.css";
function MyApp({ Component, pageProps }) {
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/pages/api/websocket.js b/pages/api/websocket.js
new file mode 100644
index 000000000..20b4c6716
--- /dev/null
+++ b/pages/api/websocket.js
@@ -0,0 +1,27 @@
+// /pages/api/websocket.js
+import { Server } from "ws";
+
+export default function handler(req, res) {
+ if (!res.socket.server.ws) {
+ console.log("Starting WebSocket server");
+ const wss = new Server({ server: res.socket.server });
+
+ wss.on("connection", (ws) => {
+ console.log("New WebSocket connection");
+
+ ws.on("message", (message) => {
+ console.log("Received:", message);
+ // Beispielnachricht an den Client senden
+ ws.send(JSON.stringify({ message: "Hallo von WebSocket-Server" }));
+ });
+
+ // Schließe die Verbindung
+ ws.on("close", () => {
+ console.log("WebSocket connection closed");
+ });
+ });
+
+ res.socket.server.ws = wss;
+ }
+ res.end();
+}
diff --git a/redux/actions.js b/redux/actions.js
new file mode 100644
index 000000000..d147d7d35
--- /dev/null
+++ b/redux/actions.js
@@ -0,0 +1,9 @@
+// /redux/actions.js
+export const connectWebSocket = (url) => ({
+ type: "WS_CONNECT",
+ payload: { url },
+});
+
+export const disconnectWebSocket = () => ({
+ type: "WS_DISCONNECT",
+});
diff --git a/redux/reducer.js b/redux/reducer.js
new file mode 100644
index 000000000..008b8d138
--- /dev/null
+++ b/redux/reducer.js
@@ -0,0 +1,18 @@
+// /redux/reducer.js
+const initialState = {
+ messages: [],
+};
+
+const websocketReducer = (state = initialState, action) => {
+ switch (action.type) {
+ case "WS_MESSAGE_RECEIVED":
+ return {
+ ...state,
+ messages: [...state.messages, action.payload],
+ };
+ default:
+ return state;
+ }
+};
+
+export default websocketReducer;
diff --git a/redux/store.js b/redux/store.js
new file mode 100644
index 000000000..cf3e43428
--- /dev/null
+++ b/redux/store.js
@@ -0,0 +1,8 @@
+// /redux/store.js
+import { createStore, applyMiddleware } from "redux";
+import websocketMiddleware from "./websocketMiddleware";
+import websocketReducer from "./reducer";
+
+const store = createStore(websocketReducer, applyMiddleware(websocketMiddleware));
+
+export default store;
diff --git a/redux/websocketMiddleware.js b/redux/websocketMiddleware.js
new file mode 100644
index 000000000..75c39637d
--- /dev/null
+++ b/redux/websocketMiddleware.js
@@ -0,0 +1,29 @@
+// /redux/websocketMiddleware.js
+const websocketMiddleware = () => {
+ let socket;
+
+ return ({ dispatch }) =>
+ (next) =>
+ (action) => {
+ if (action.type === "WS_CONNECT") {
+ socket = new WebSocket(action.payload.url);
+
+ socket.onmessage = (event) => {
+ const data = JSON.parse(event.data);
+ dispatch({ type: "WS_MESSAGE_RECEIVED", payload: data });
+ };
+
+ socket.onclose = () => {
+ dispatch({ type: "WS_DISCONNECTED" });
+ };
+ }
+
+ if (action.type === "WS_DISCONNECT" && socket) {
+ socket.close();
+ }
+
+ return next(action);
+ };
+};
+
+export default websocketMiddleware;