Initial server state
This commit is contained in:
parent
5ab0b61f80
commit
11b3b48caa
@ -1,30 +1,64 @@
|
|||||||
import {createContext} from "react";
|
import { createContext, useState, useEffect, useCallback } from 'react';
|
||||||
import {io} from "socket.io-client";
|
import { io } from 'socket.io-client';
|
||||||
|
|
||||||
export const SocketContext = createContext({});
|
export const SocketContext = createContext();
|
||||||
|
|
||||||
export const SocketProvider = ({ children }) => {
|
export const SocketProvider = ({ children }) => {
|
||||||
const socket = io("/", {autoConnect: false});
|
const [socket, setSocket] = useState(null);
|
||||||
|
const [connected, setConnected] = useState(false);
|
||||||
|
|
||||||
const connect = () => {
|
const connect = useCallback(() => {
|
||||||
socket.connect();
|
if (!socket) {
|
||||||
|
const newSocket = io(window.location.hostname === 'localhost' ? 'http://localhost:5287' : '/');
|
||||||
|
|
||||||
|
newSocket.on('connect', () => {
|
||||||
|
setConnected(true);
|
||||||
|
console.log('Socket connected');
|
||||||
|
});
|
||||||
|
|
||||||
|
newSocket.on('disconnect', () => {
|
||||||
|
setConnected(false);
|
||||||
|
console.log('Socket disconnected');
|
||||||
|
});
|
||||||
|
|
||||||
|
setSocket(newSocket);
|
||||||
}
|
}
|
||||||
|
}, [socket]);
|
||||||
|
|
||||||
const send = (event, data) => {
|
const disconnect = useCallback(() => {
|
||||||
socket.emit(event, data);
|
if (socket) {
|
||||||
|
socket.disconnect();
|
||||||
|
setSocket(null);
|
||||||
|
setConnected(false);
|
||||||
}
|
}
|
||||||
|
}, [socket]);
|
||||||
|
|
||||||
const disconnect = () => {
|
const send = useCallback((event, data) => {
|
||||||
socket.disconnect();
|
if (socket) {
|
||||||
|
socket.emit(event, data);
|
||||||
|
} else {
|
||||||
|
console.error('Cannot send event, socket not connected');
|
||||||
}
|
}
|
||||||
|
}, [socket]);
|
||||||
|
|
||||||
const addListener = (event, callback) => {
|
const on = useCallback((event, callback) => {
|
||||||
socket.on(event, callback);
|
if (socket) {
|
||||||
|
socket.on(event, callback);
|
||||||
}
|
}
|
||||||
|
return () => {
|
||||||
|
if (socket) socket.off(event, callback);
|
||||||
|
};
|
||||||
|
}, [socket]);
|
||||||
|
|
||||||
return (
|
useEffect(() => {
|
||||||
<SocketContext.Provider value={{connect, disconnect, send, addListener}}>
|
return () => {
|
||||||
{children}
|
if (socket) socket.disconnect();
|
||||||
</SocketContext.Provider>
|
};
|
||||||
)
|
}, [socket]);
|
||||||
}
|
|
||||||
|
return (
|
||||||
|
<SocketContext.Provider value={{ connected, connect, disconnect, send, on, socket }}>
|
||||||
|
{children}
|
||||||
|
</SocketContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import "./styles.sass";
|
import "./styles.sass";
|
||||||
import {StateContext} from "@/common/contexts/StateContext";
|
import {StateContext} from "@/common/contexts/StateContext";
|
||||||
|
import {SocketContext} from "@/common/contexts/SocketContext";
|
||||||
import {useContext, useState, useEffect, useRef} from "react";
|
import {useContext, useState, useEffect, useRef} from "react";
|
||||||
import MusicSlider from "@/pages/Game/components/MusicSlider";
|
import MusicSlider from "@/pages/Game/components/MusicSlider";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
@ -7,10 +8,59 @@ import {faMessage} from "@fortawesome/free-solid-svg-icons";
|
|||||||
|
|
||||||
export const Game = () => {
|
export const Game = () => {
|
||||||
const {setCurrentState} = useContext(StateContext);
|
const {setCurrentState} = useContext(StateContext);
|
||||||
const [messages, setMessages] = useState([{sender: "Marco", text: "Hallo!"}]);
|
const {send, on, socket} = useContext(SocketContext);
|
||||||
|
const [messages, setMessages] = useState([]);
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
|
const [connectedUsers, setConnectedUsers] = useState([]);
|
||||||
|
const [username, setUsername] = useState("");
|
||||||
const messageEndRef = useRef(null);
|
const messageEndRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleChatMessage = (messageData) => {
|
||||||
|
setMessages(prev => [...prev, messageData]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUserConnected = (userData) => {
|
||||||
|
setMessages(prev => [...prev, {
|
||||||
|
system: true,
|
||||||
|
text: `${userData.name} ist beigetreten`
|
||||||
|
}]);
|
||||||
|
setConnectedUsers(prev => [...prev, userData]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUserDisconnected = (userId) => {
|
||||||
|
setConnectedUsers(prev => {
|
||||||
|
const user = prev.find(u => u.id === userId);
|
||||||
|
if (user) {
|
||||||
|
setMessages(prevMsgs => [...prevMsgs, {
|
||||||
|
system: true,
|
||||||
|
text: `${user.name} hat den Raum verlassen`
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
return prev.filter(u => u.id !== userId);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (socket && socket.id) {
|
||||||
|
send("get-user-info");
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUserInfo = (userInfo) => {
|
||||||
|
setUsername(userInfo.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cleanupChatMessage = on("chat-message", handleChatMessage);
|
||||||
|
const cleanupUserConnected = on("user-connected", handleUserConnected);
|
||||||
|
const cleanupUserDisconnected = on("user-disconnected", handleUserDisconnected);
|
||||||
|
const cleanupUserInfo = on("user-info", handleUserInfo);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cleanupChatMessage();
|
||||||
|
cleanupUserConnected();
|
||||||
|
cleanupUserDisconnected();
|
||||||
|
cleanupUserInfo();
|
||||||
|
};
|
||||||
|
}, [on, send, socket]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
messageEndRef.current?.scrollIntoView({behavior: "smooth"});
|
messageEndRef.current?.scrollIntoView({behavior: "smooth"});
|
||||||
@ -18,7 +68,12 @@ export const Game = () => {
|
|||||||
|
|
||||||
const handleSendMessage = () => {
|
const handleSendMessage = () => {
|
||||||
if (inputValue.trim()) {
|
if (inputValue.trim()) {
|
||||||
setMessages([...messages, {sender: "User", text: inputValue}]);
|
const messageData = {
|
||||||
|
text: inputValue,
|
||||||
|
sender: username
|
||||||
|
};
|
||||||
|
send("send-message", messageData);
|
||||||
|
setMessages(prev => [...prev, messageData]);
|
||||||
setInputValue("");
|
setInputValue("");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -47,9 +102,15 @@ export const Game = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="chat-messages">
|
<div className="chat-messages">
|
||||||
{messages.map((message, index) => (
|
{messages.map((message, index) => (
|
||||||
<div key={index} className="message">
|
<div key={index} className={`message ${message.system ? 'system-message' : ''}`}>
|
||||||
<span className="message-sender">{message.sender}:</span>
|
{message.system ? (
|
||||||
<span className="message-text">{message.text}</span>
|
<span className="message-text system">{message.text}</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span className="message-sender">{message.sender}:</span>
|
||||||
|
<span className="message-text">{message.text}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div ref={messageEndRef}></div>
|
<div ref={messageEndRef}></div>
|
||||||
|
@ -210,6 +210,16 @@
|
|||||||
.message-text
|
.message-text
|
||||||
margin-left: 5px
|
margin-left: 5px
|
||||||
|
|
||||||
|
.message-text.system
|
||||||
|
font-style: italic
|
||||||
|
color: #888
|
||||||
|
|
||||||
|
.system-message
|
||||||
|
text-align: center
|
||||||
|
font-style: italic
|
||||||
|
color: #888
|
||||||
|
padding: 5px 0
|
||||||
|
|
||||||
.chat-input
|
.chat-input
|
||||||
display: flex
|
display: flex
|
||||||
padding: 15px
|
padding: 15px
|
||||||
|
@ -5,35 +5,63 @@ import CreateForm from "@/pages/Home/components/CreateForm";
|
|||||||
import {faArrowRightToBracket, faPlusSquare} from "@fortawesome/free-solid-svg-icons";
|
import {faArrowRightToBracket, faPlusSquare} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {StateContext} from "@/common/contexts/StateContext";
|
import {StateContext} from "@/common/contexts/StateContext";
|
||||||
import {SocketContext} from "@/common/contexts/SocketContext";
|
import {SocketContext} from "@/common/contexts/SocketContext";
|
||||||
import {useContext, useState} from "react";
|
import {useContext, useState, useEffect} from "react";
|
||||||
|
|
||||||
export const Home = () => {
|
export const Home = () => {
|
||||||
const {setCurrentState} = useContext(StateContext);
|
const {setCurrentState} = useContext(StateContext);
|
||||||
const {connect, send} = useContext(SocketContext);
|
const {connect, send, on} = useContext(SocketContext);
|
||||||
const [homeState, setHomeState] = useState("initial"); // initial, joining, creating
|
const [homeState, setHomeState] = useState("initial"); // initial, joining, creating
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
connect();
|
||||||
|
|
||||||
|
const handleRoomCreated = (roomId) => {
|
||||||
|
console.log("Room created", roomId);
|
||||||
|
setCurrentState("Game");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRoomJoined = (roomId) => {
|
||||||
|
console.log("Room joined", roomId);
|
||||||
|
setCurrentState("Game");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRoomNotFound = (roomId) => {
|
||||||
|
setError(`Room ${roomId} not found`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cleanupRoomCreated = on("room-created", handleRoomCreated);
|
||||||
|
const cleanupRoomJoined = on("room-joined", handleRoomJoined);
|
||||||
|
const cleanupRoomNotFound = on("room-not-found", handleRoomNotFound);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cleanupRoomCreated();
|
||||||
|
cleanupRoomJoined();
|
||||||
|
cleanupRoomNotFound();
|
||||||
|
};
|
||||||
|
}, [connect, setCurrentState, on]);
|
||||||
|
|
||||||
const handleJoinClick = () => {
|
const handleJoinClick = () => {
|
||||||
setHomeState("joining");
|
setHomeState("joining");
|
||||||
|
setError("");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateClick = () => {
|
const handleCreateClick = () => {
|
||||||
setHomeState("creating");
|
setHomeState("creating");
|
||||||
|
setError("");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
setHomeState("initial");
|
setHomeState("initial");
|
||||||
|
setError("");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleJoinSubmit = (name, roomCode) => {
|
const handleJoinSubmit = (name, roomCode) => {
|
||||||
connect();
|
send("join-room", {roomId: roomCode, name});
|
||||||
send("joinRoom", {name, roomCode});
|
|
||||||
setCurrentState("Game");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateSubmit = (name) => {
|
const handleCreateSubmit = (name) => {
|
||||||
connect();
|
send("create-room", {name});
|
||||||
send("createRoom", {name});
|
|
||||||
setCurrentState("Game");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -55,13 +83,13 @@ export const Home = () => {
|
|||||||
|
|
||||||
<div className={`form-container ${homeState === 'joining' ? 'active' : ''}`}>
|
<div className={`form-container ${homeState === 'joining' ? 'active' : ''}`}>
|
||||||
{homeState === 'joining' && (
|
{homeState === 'joining' && (
|
||||||
<JoinForm onSubmit={handleJoinSubmit} onBack={handleBack} />
|
<JoinForm onSubmit={handleJoinSubmit} onBack={handleBack} error={error} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={`form-container ${homeState === 'creating' ? 'active' : ''}`}>
|
<div className={`form-container ${homeState === 'creating' ? 'active' : ''}`}>
|
||||||
{homeState === 'creating' && (
|
{homeState === 'creating' && (
|
||||||
<CreateForm onSubmit={handleCreateSubmit} onBack={handleBack} />
|
<CreateForm onSubmit={handleCreateSubmit} onBack={handleBack} error={error} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
import "@/common/styles/forms.sass";
|
import "@/common/styles/forms.sass";
|
||||||
import {useState} from "react";
|
import {useState, useEffect} from "react";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faArrowLeft, faArrowRightToBracket} from "@fortawesome/free-solid-svg-icons";
|
import {faArrowLeft, faArrowRightToBracket} from "@fortawesome/free-solid-svg-icons";
|
||||||
import FormInput from "@/common/components/FormInput";
|
import FormInput from "@/common/components/FormInput";
|
||||||
|
|
||||||
export const JoinForm = ({onSubmit, onBack}) => {
|
export const JoinForm = ({onSubmit, onBack, error}) => {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [roomCode, setRoomCode] = useState("");
|
const [roomCode, setRoomCode] = useState("");
|
||||||
const [errors, setErrors] = useState({});
|
const [errors, setErrors] = useState({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (error) {
|
||||||
|
setErrors(prev => ({...prev, roomCode: error}));
|
||||||
|
}
|
||||||
|
}, [error]);
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -8,7 +8,7 @@ module.exports.connectUserToRoom = (roomId, user) => {
|
|||||||
} else {
|
} else {
|
||||||
rooms[roomId] = {members: [{...user, creator: true}], settings: {}};
|
rooms[roomId] = {members: [{...user, creator: true}], settings: {}};
|
||||||
}
|
}
|
||||||
console.log(JSON.stringify(rooms));
|
console.log(`User ${user.name} connected to room ${roomId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.getUserRoom = (userId) => {
|
module.exports.getUserRoom = (userId) => {
|
||||||
@ -18,6 +18,21 @@ module.exports.getUserRoom = (userId) => {
|
|||||||
|
|
||||||
if (memberIndex !== -1) return roomId;
|
if (memberIndex !== -1) return roomId;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getRoomUsers = (roomId) => {
|
||||||
|
if (rooms[roomId]) {
|
||||||
|
return rooms[roomId].members;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getRoomCreator = (roomId) => {
|
||||||
|
if (rooms[roomId]) {
|
||||||
|
return rooms[roomId].members.find(member => member.creator);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.disconnectUser = (userId) => {
|
module.exports.disconnectUser = (userId) => {
|
||||||
@ -26,12 +41,17 @@ module.exports.disconnectUser = (userId) => {
|
|||||||
const memberIndex = room.members.findIndex(member => member.id === userId);
|
const memberIndex = room.members.findIndex(member => member.id === userId);
|
||||||
|
|
||||||
if (memberIndex !== -1) {
|
if (memberIndex !== -1) {
|
||||||
if (room.members[memberIndex].creator) {
|
if (room.members[memberIndex].creator && room.members.length > 1) {
|
||||||
if (room.members.length > 1) room.members[1].creator = true;
|
room.members[1].creator = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
room.members.splice(memberIndex, 1);
|
room.members.splice(memberIndex, 1);
|
||||||
if (room.members.length === 0) delete rooms[roomId];
|
console.log(`User ${userId} disconnected from room ${roomId}`);
|
||||||
|
|
||||||
|
if (room.members.length === 0) {
|
||||||
|
delete rooms[roomId];
|
||||||
|
console.log(`Room ${roomId} deleted because it's empty`);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
const {connectUserToRoom, roomExists, disconnectUser, getUserRoom} = require("../controller/room");
|
const {connectUserToRoom, roomExists, disconnectUser, getUserRoom, getRoomUsers} = require("../controller/room");
|
||||||
|
|
||||||
module.exports = (socket) => {
|
module.exports = (socket) => {
|
||||||
let currentRoomId = null;
|
let currentRoomId = null;
|
||||||
|
let currentUser = null;
|
||||||
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
const roomId = getUserRoom(socket.id);
|
const roomId = getUserRoom(socket.id);
|
||||||
@ -14,9 +15,10 @@ module.exports = (socket) => {
|
|||||||
if (currentRoomId) return socket.emit("already-in-room", currentRoomId);
|
if (currentRoomId) return socket.emit("already-in-room", currentRoomId);
|
||||||
|
|
||||||
if (roomExists(roomId.toString())) {
|
if (roomExists(roomId.toString())) {
|
||||||
connectUserToRoom(roomId, {id: socket.id, name: name.toString()});
|
currentUser = {id: socket.id, name: name.toString()};
|
||||||
|
connectUserToRoom(roomId, currentUser);
|
||||||
socket.join(roomId);
|
socket.join(roomId);
|
||||||
socket.to(roomId).emit("user-connected", {name: name.toString(), id: socket.id});
|
socket.to(roomId).emit("user-connected", currentUser);
|
||||||
socket.emit("room-joined", roomId);
|
socket.emit("room-joined", roomId);
|
||||||
currentRoomId = roomId;
|
currentRoomId = roomId;
|
||||||
} else {
|
} else {
|
||||||
@ -27,10 +29,31 @@ module.exports = (socket) => {
|
|||||||
socket.on("create-room", ({name}) => {
|
socket.on("create-room", ({name}) => {
|
||||||
if (!name) return socket.emit("room-name-required");
|
if (!name) return socket.emit("room-name-required");
|
||||||
const roomId = Math.random().toString(36).substring(7);
|
const roomId = Math.random().toString(36).substring(7);
|
||||||
connectUserToRoom(roomId, {id: socket.id, name: name?.toString()});
|
currentUser = {id: socket.id, name: name?.toString()};
|
||||||
|
connectUserToRoom(roomId, currentUser);
|
||||||
socket.join(roomId);
|
socket.join(roomId);
|
||||||
socket.emit("room-created", roomId);
|
socket.emit("room-created", roomId);
|
||||||
currentRoomId = roomId;
|
currentRoomId = roomId;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("send-message", (messageData) => {
|
||||||
|
const roomId = getUserRoom(socket.id);
|
||||||
|
if (roomId) {
|
||||||
|
socket.to(roomId).emit("chat-message", messageData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("get-user-info", () => {
|
||||||
|
if (currentUser) {
|
||||||
|
socket.emit("user-info", currentUser);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("get-room-users", () => {
|
||||||
|
const roomId = getUserRoom(socket.id);
|
||||||
|
if (roomId) {
|
||||||
|
const users = getRoomUsers(roomId);
|
||||||
|
socket.emit("room-users", users);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user