Commit
This commit is contained in:
43
src/backend/chat-server.js
Normal file
43
src/backend/chat-server.js
Normal file
@@ -0,0 +1,43 @@
|
||||
const WebSocket = require("ws");
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
|
||||
const clients = new Map();
|
||||
|
||||
wss.on("connection", (ws, req) => {
|
||||
console.log("New client connected");
|
||||
|
||||
ws.on("message", (message) => {
|
||||
try {
|
||||
const parsedMessage = JSON.parse(message);
|
||||
if (parsedMessage.type === "register") {
|
||||
clients.set(parsedMessage.userId, ws);
|
||||
console.log(`User registered: ${parsedMessage.userId}`);
|
||||
} else if (parsedMessage.type === "message") {
|
||||
const recipientWs = clients.get(parsedMessage.recipientId);
|
||||
if (recipientWs) {
|
||||
recipientWs.send(
|
||||
JSON.stringify({
|
||||
senderId: parsedMessage.senderId,
|
||||
message: parsedMessage.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
);
|
||||
} else {
|
||||
console.error(`User ${parsedMessage.recipientId} is not connected.`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error processing message:", err.message);
|
||||
}
|
||||
});
|
||||
|
||||
ws.on("close", () => {
|
||||
console.log("Client disconnected");
|
||||
[...clients.entries()].forEach(([userId, clientWs]) => {
|
||||
if (clientWs === ws) {
|
||||
clients.delete(userId);
|
||||
console.log(`User disconnected: ${userId}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,8 @@
|
||||
export default class Interlocutor {
|
||||
constructor(id, name) {
|
||||
this.name = name
|
||||
this.id = id
|
||||
}
|
||||
static name;
|
||||
static id;
|
||||
}
|
||||
constructor(id, name) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
}
|
||||
static name;
|
||||
static id;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
export default class User {
|
||||
|
||||
}
|
||||
constructor(id, name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.status = "online";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import React from 'react';
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import React from "react";
|
||||
import { Routes, Route } from "react-router-dom";
|
||||
|
||||
import { URLs } from './__data__/urls';
|
||||
import { URLs } from "./__data__/urls";
|
||||
|
||||
import Home from './pages/Home.jsx'
|
||||
import Init from './pages/Init.jsx'
|
||||
import Account from './pages/Account.jsx'
|
||||
import Chat from './pages/Chat.jsx'
|
||||
import SignIn from './pages/SignIn.jsx'
|
||||
import SignUp from './pages/SignUp.jsx'
|
||||
import Home from "./pages/Home.jsx";
|
||||
import Init from "./pages/Init.jsx";
|
||||
import Account from "./pages/Account.jsx";
|
||||
import Chat from "./pages/Chat.jsx";
|
||||
import SignIn from "./pages/SignIn.jsx";
|
||||
import SignUp from "./pages/SignUp.jsx";
|
||||
|
||||
export const Dashboard = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path={URLs.baseUrl} element={<Init/>}/>
|
||||
<Route path={URLs.home.url} element={<Home/>}/>
|
||||
<Route path={URLs.chat.url} element={<Chat/>}/>
|
||||
<Route path={URLs.auth.url} element={<SignIn/>}/>
|
||||
<Route path={URLs.reg.url} element={<SignUp/>}/>
|
||||
<Route path={URLs.account.url} element={<Account/>}/>
|
||||
<Route path="*" element={<h1>404 page not found</h1>}/>
|
||||
<Route path={URLs.baseUrl} element={<Init />} />
|
||||
<Route path={URLs.home.url} element={<Home />} />
|
||||
<Route path={URLs.chat.url} element={<Chat />} />
|
||||
<Route path={URLs.auth.url} element={<SignIn />} />
|
||||
<Route path={URLs.reg.url} element={<SignUp />} />
|
||||
<Route path={URLs.account.url} element={<Account />} />
|
||||
<Route path="*" element={<h1>404 page not found</h1>} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
const Account = () => {
|
||||
return (
|
||||
<h1>Account</h1>
|
||||
)
|
||||
}
|
||||
return <h1>Account</h1>;
|
||||
};
|
||||
|
||||
export default Account;
|
||||
export default Account;
|
||||
|
||||
@@ -1,20 +1,212 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import "./css/Chat.css";
|
||||
import { FaPaperPlane, FaSmile } from "react-icons/fa";
|
||||
|
||||
const emojis = [
|
||||
"😀",
|
||||
"😁",
|
||||
"😂",
|
||||
"🤣",
|
||||
"😃",
|
||||
"😄",
|
||||
"😅",
|
||||
"😆",
|
||||
"😉",
|
||||
"😊",
|
||||
"😋",
|
||||
"😎",
|
||||
"😍",
|
||||
"😘",
|
||||
"🥰",
|
||||
"😗",
|
||||
"😙",
|
||||
"😚",
|
||||
"🙂",
|
||||
"🤗",
|
||||
"🤩",
|
||||
"🤔",
|
||||
"😐",
|
||||
"😑",
|
||||
"😶",
|
||||
"🙄",
|
||||
"😏",
|
||||
"😣",
|
||||
"😥",
|
||||
"😮",
|
||||
"🤐",
|
||||
"😯",
|
||||
"😪",
|
||||
"😫",
|
||||
"😴",
|
||||
"😌",
|
||||
"😛",
|
||||
"😜",
|
||||
"🤪",
|
||||
"🤨",
|
||||
"😝",
|
||||
"🤑",
|
||||
"😒",
|
||||
"😓",
|
||||
"😔",
|
||||
"😕",
|
||||
"😖",
|
||||
"😞",
|
||||
"😟",
|
||||
"😠",
|
||||
"😡",
|
||||
"🤬",
|
||||
"😱",
|
||||
"😨",
|
||||
"😧",
|
||||
"😇",
|
||||
"🥳",
|
||||
"🥺",
|
||||
"😻",
|
||||
"😼",
|
||||
"😽",
|
||||
"🙈",
|
||||
"🙉",
|
||||
"🙊",
|
||||
"💀",
|
||||
"👻",
|
||||
"👽",
|
||||
];
|
||||
|
||||
const Chat = () => {
|
||||
const [interlocutorId, setInterlocutorId] = useState(0); // State to hold the interlocutorId
|
||||
const [interlocutorId, setInterlocutorId] = useState(0);
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [newMessage, setNewMessage] = useState("");
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
||||
const socket = useRef(null);
|
||||
const chatRef = useRef(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
function getInterlocutorId() {
|
||||
const id = localStorage.getItem('interlocutorId');
|
||||
return id ? id : 0;
|
||||
useEffect(() => {
|
||||
const id = parseInt(localStorage.getItem("interlocutorId"), 10) || 0;
|
||||
setInterlocutorId(id);
|
||||
|
||||
socket.current = new WebSocket("ws://localhost:8080");
|
||||
|
||||
socket.current.onopen = () => {
|
||||
console.log("WebSocket connected");
|
||||
socket.current.send(
|
||||
JSON.stringify({ type: "register", userId: "yourUserId" })
|
||||
);
|
||||
};
|
||||
|
||||
socket.current.onmessage = (event) => {
|
||||
const receivedData = JSON.parse(event.data);
|
||||
setMessages((prev) => [...prev, receivedData]);
|
||||
};
|
||||
|
||||
socket.current.onerror = (event) => {
|
||||
console.error("WebSocket error observed:", event);
|
||||
};
|
||||
|
||||
socket.current.onclose = () => {
|
||||
console.log("WebSocket closed");
|
||||
};
|
||||
|
||||
return () => {
|
||||
socket.current.close();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (chatRef.current) {
|
||||
chatRef.current.scrollTop = chatRef.current.scrollHeight;
|
||||
}
|
||||
}, [messages]);
|
||||
|
||||
useEffect(() => {
|
||||
const id = getInterlocutorId();
|
||||
setInterlocutorId(id);
|
||||
}, []);
|
||||
const sendMessage = () => {
|
||||
if (newMessage.trim()) {
|
||||
const messageData = {
|
||||
type: "message",
|
||||
senderId: "yourUserId",
|
||||
recipientId: interlocutorId,
|
||||
message: newMessage,
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
};
|
||||
socket.current.send(JSON.stringify(messageData));
|
||||
setMessages((prev) => [...prev, messageData]);
|
||||
setNewMessage("");
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyPress = (e) => {
|
||||
if (e.key === "Enter") {
|
||||
sendMessage();
|
||||
}
|
||||
};
|
||||
|
||||
const handleEmojiSelect = (emoji) => {
|
||||
setNewMessage((prev) => prev + emoji);
|
||||
setShowEmojiPicker(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<h2>Chat with ... (id = {interlocutorId})</h2>
|
||||
<div className="chat-container">
|
||||
<div className="chat-header">
|
||||
<h2>Chat with ... (id = {interlocutorId})</h2>
|
||||
<button
|
||||
onClick={() => navigate("/enterfront/home")}
|
||||
className="home-button"
|
||||
>
|
||||
Home
|
||||
</button>{" "}
|
||||
{}
|
||||
</div>
|
||||
<div className="chat-messages" ref={chatRef}>
|
||||
{messages.map((msg, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`message-bubble ${
|
||||
msg.senderId === "yourUserId" ? "sent" : "received"
|
||||
}`}
|
||||
>
|
||||
<div className="message-content">
|
||||
<b>{msg.senderId === "yourUserId" ? "You" : "Interlocutor"}:</b>{" "}
|
||||
{msg.message}
|
||||
</div>
|
||||
<span className="message-timestamp">{msg.timestamp}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="chat-input-container">
|
||||
<input
|
||||
type="text"
|
||||
value={newMessage}
|
||||
onChange={(e) => setNewMessage(e.target.value)}
|
||||
placeholder="Type a message..."
|
||||
className="chat-input"
|
||||
onKeyPress={handleKeyPress}
|
||||
/>
|
||||
<button
|
||||
className="emoji-button"
|
||||
onClick={() => setShowEmojiPicker((prev) => !prev)}
|
||||
>
|
||||
<FaSmile />
|
||||
</button>
|
||||
<button onClick={sendMessage} className="send-button">
|
||||
<FaPaperPlane />
|
||||
</button>
|
||||
{showEmojiPicker && (
|
||||
<div className="emoji-picker">
|
||||
{emojis.map((emoji, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="emoji"
|
||||
onClick={() => handleEmojiSelect(emoji)}
|
||||
style={{ cursor: "pointer", padding: "5px" }}
|
||||
>
|
||||
{emoji}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
145
src/pages/css/Chat.css
Normal file
145
src/pages/css/Chat.css
Normal file
@@ -0,0 +1,145 @@
|
||||
.chat-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
padding: 10px;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.chat-messages {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.message-bubble {
|
||||
margin: 5px;
|
||||
padding: 10px 15px;
|
||||
border-radius: 10px;
|
||||
max-width: 70%;
|
||||
word-wrap: break-word;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.sent {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
align-self: flex-end;
|
||||
text-align: right;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.received {
|
||||
background-color: #f1f0f0;
|
||||
align-self: flex-start;
|
||||
text-align: left;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.message-timestamp {
|
||||
font-size: 10px;
|
||||
color: #999;
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.chat-input-container {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
border-top: 1px solid #ddd;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.chat-input {
|
||||
flex-grow: 1;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.send-button,
|
||||
.emoji-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-size: 20px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.send-button:hover,
|
||||
.emoji-button:hover {
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.send-button {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.emoji-button {
|
||||
color: #f0c040;
|
||||
}
|
||||
|
||||
.chat-input:focus {
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
.emoji-picker {
|
||||
position: absolute;
|
||||
bottom: 60px;
|
||||
right: 10px;
|
||||
z-index: 10;
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.emoji:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.home-button {
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.home-button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
0
src/pages/css/account.css
Normal file
0
src/pages/css/account.css
Normal file
Reference in New Issue
Block a user