Compare commits

...

2 Commits

Author SHA1 Message Date
Askar Akhmetkhanov
dd10b080e8 0.4.0 2024-10-09 17:38:38 +03:00
Askar Akhmetkhanov
4cf909c607 Chat, scrolling, sockets and timestamps updated 2024-10-09 17:37:50 +03:00
9 changed files with 384 additions and 422 deletions

171
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "enterfront",
"version": "0.3.0",
"version": "0.4.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "enterfront",
"version": "0.3.0",
"version": "0.4.0",
"dependencies": {
"@brojs/cli": "^1.0.0",
"@brojs/create": "^1.0.0",
@ -23,6 +23,8 @@
"react-icons": "^5.3.0",
"react-router-dom": "^6.26.1",
"react-toastify": "^10.0.5",
"socket.io": "^4.8.0",
"socket.io-client": "^4.8.0",
"styled-components": "^6.1.13",
"typescript": "^5.5.4",
"ws": "^8.18.0"
@ -1853,30 +1855,6 @@
"node": ">=18"
}
},
"node_modules/@brojs/cli/node_modules/i18next": {
"version": "23.15.1",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.15.1.tgz",
"integrity": "sha512-wB4abZ3uK7EWodYisHl/asf8UYEhrI/vj/8aoSsrj/ZDxj4/UXPOa1KvFt1Fq5hkUHquNqwFlDprmjZ8iySgYA==",
"funding": [
{
"type": "individual",
"url": "https://locize.com"
},
{
"type": "individual",
"url": "https://locize.com/i18next.html"
},
{
"type": "individual",
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.23.2"
}
},
"node_modules/@brojs/cli/node_modules/i18next-browser-languagedetector": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz",
@ -2691,19 +2669,6 @@
"ijl-cli": "bin/ijl-cli"
}
},
"node_modules/@ijl/cli/node_modules/@types/react": {
"version": "17.0.83",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.83.tgz",
"integrity": "sha512-l0m4ArKJvmFtR4e8UmKrj1pB4tUgOhJITf+mADyF/p69Ts1YAR/E+G9XEM0mHXKVRa1dQNHseyyDNzeuAXfXQw==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "^0.16",
"csstype": "^3.0.2"
}
},
"node_modules/@ijl/cli/node_modules/loader-utils": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
@ -2717,35 +2682,6 @@
"node": ">=8.9.0"
}
},
"node_modules/@ijl/cli/node_modules/react": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@ijl/cli/node_modules/react-dom": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"scheduler": "^0.20.2"
},
"peerDependencies": {
"react": "17.0.2"
}
},
"node_modules/@ijl/cli/node_modules/react-hot-loader": {
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.13.1.tgz",
@ -2774,17 +2710,6 @@
}
}
},
"node_modules/@ijl/cli/node_modules/scheduler": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"node_modules/@ijl/cli/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
@ -3071,12 +2996,14 @@
"node_modules/@types/cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
"license": "MIT"
},
"node_modules/@types/cors": {
"version": "2.8.17",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
"integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
@ -3138,14 +3065,6 @@
"@types/react": "*"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/@types/source-list-map": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.6.tgz",
@ -3735,6 +3654,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
"license": "MIT",
"engines": {
"node": "^4.5.0 || >= 5.9"
}
@ -4364,6 +4284,7 @@
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
@ -4783,9 +4704,10 @@
}
},
"node_modules/engine.io": {
"version": "6.5.5",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
"integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.1.tgz",
"integrity": "sha512-NEpDCw9hrvBW+hVEOK4T7v0jFJ++KgtPl4jKFwsZVfG1XhS0dCrSb3VMb9gPAd7VAdW52VT1EnaNiU2vM8C0og==",
"license": "MIT",
"dependencies": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
@ -4802,6 +4724,40 @@
"node": ">=10.2.0"
}
},
"node_modules/engine.io-client": {
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz",
"integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.17.1",
"xmlhttprequest-ssl": "~2.1.1"
}
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/engine.io-parser": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
@ -4814,6 +4770,7 @@
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
@ -8959,15 +8916,16 @@
}
},
"node_modules/socket.io": {
"version": "4.7.5",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
"integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz",
"integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"cors": "~2.8.5",
"debug": "~4.3.2",
"engine.io": "~6.5.2",
"engine.io": "~6.6.0",
"socket.io-adapter": "~2.5.2",
"socket.io-parser": "~4.2.4"
},
@ -9005,6 +8963,21 @@
}
}
},
"node_modules/socket.io-client": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.0.tgz",
"integrity": "sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.6.1",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
@ -10147,6 +10120,14 @@
}
}
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz",
"integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@ -15,6 +15,8 @@
"react-icons": "^5.3.0",
"react-router-dom": "^6.26.1",
"react-toastify": "^10.0.5",
"socket.io": "^4.8.0",
"socket.io-client": "^4.8.0",
"styled-components": "^6.1.13",
"typescript": "^5.5.4",
"ws": "^8.18.0"
@ -27,5 +29,5 @@
"clean": "rimraf dist"
},
"name": "enterfront",
"version": "0.3.0"
"version": "0.4.0"
}

View File

@ -1,43 +0,0 @@
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}`);
}
});
});
});

31
src/backend/server.js Normal file
View File

@ -0,0 +1,31 @@
const express = require("express");
const http = require("http");
const { Server } = require("socket.io");
require("dotenv").config();
const app = express();
const server = http.createServer(app);
const io = new Server(server);
io.on("connection", (socket) => {
console.log("New connection:", socket.id);
// For messages
socket.on("sendMessage", (message) => {
console.log("Message received:", message);
socket.broadcast.emit("receiveMessage", message);
});
socket.on("disconnect", () => {
console.log("User disconnected:", socket.id);
});
});
app.get("/", (req, res) => {
res.send("Socket.IO Server is running");
});
const PORT = process.env.PORT || 8099;
server.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

View File

@ -1,76 +1,92 @@
import React, {useEffect, useState} from 'react';
import React, { useEffect, useState } from "react";
import Card from "./Card.jsx";
import {get} from "../../backend/api";
import {displayMessage} from "../../backend/notifications/notifications";
import {MessageType} from "../../backend/notifications/message";
import { get } from "../../backend/api";
import { displayMessage } from "../../backend/notifications/notifications";
import { MessageType } from "../../backend/notifications/message";
const ChatsList = (props) => {
const { chats } = props;
const { chats } = props;
const [customChats, setCustomChats] = useState([]);
const [customChats, setCustomChats] = useState([]);
const updateList = async () => {
const username = localStorage.getItem("username");
if (!username) return null;
const updateList = async () => {
const username = localStorage.getItem("username");
if (!username) {return null;}
const updatedChats = await Promise.all(
chats.map(async (chat) => {
const interlocutorId = chat.id1 === username ? chat.id2 : chat.id1;
const updatedChats = await Promise.all(
chats.map(async (chat) => {
const interlocutorId = chat.id1 === username ? chat.id2 : chat.id1
const { ok, data } = await get("/auth/" + interlocutorId);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return null;
}
const {ok, data} = await get('/auth/' + interlocutorId);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return null;
}
const interlocutor = data.user;
const interlocutor = data.user;
const lastMessage =
chat.messages.length > 0
? chat.messages[chat.messages.length - 1]
: { data: "", timestamp: new Date(0).toISOString() };
return {
id: interlocutorId,
name: interlocutor.nickname,
lastMessage: chat.messages.length > 0 ? chat.messages[chat.messages.length - 1].data : "",
}
})
);
return {
id: interlocutorId,
name: interlocutor.nickname,
lastMessageData: lastMessage.data,
lastMessageTimestamp: lastMessage.timestamp,
};
})
);
setCustomChats(updatedChats.filter(chat => chat !== null));
};
const validChats = updatedChats.filter((chat) => chat !== null);
useEffect(() => {updateList().then();}, [chats])
validChats.sort(
(a, b) =>
new Date(b.lastMessageTimestamp) - new Date(a.lastMessageTimestamp)
);
setCustomChats(validChats);
};
const colorMap = {
orange: 'FFA500FF',
aqua: '00FFFFFF',
crimson: 'DC143CFF',
red: 'FF0000FF',
violet: '8A2BE2FF',
seagreen: '20B2AAFF',
green: 'ADFF2FFF',
blue: '0000FFFF',
pink: 'FF1493FF',
cyan: '72FAFAFF'
}
useEffect(() => {
updateList().then();
}, [chats]);
function getColor(chatId) {
const keys = Object.keys(colorMap);
const numericId = Array.from(chatId).reduce((sum, char) => sum + char.charCodeAt(0), 0);
const index = numericId % keys.length;
return colorMap[keys[index]];
}
const colorMap = {
orange: "FFA500FF",
aqua: "00FFFFFF",
crimson: "DC143CFF",
red: "FF0000FF",
violet: "8A2BE2FF",
seagreen: "20B2AAFF",
green: "ADFF2FFF",
blue: "0000FFFF",
pink: "FF1493FF",
cyan: "72FAFAFF",
};
function getColor(chatId) {
const keys = Object.keys(colorMap);
const numericId = Array.from(chatId).reduce(
(sum, char) => sum + char.charCodeAt(0),
0
);
const index = numericId % keys.length;
return colorMap[keys[index]];
}
return (
<div className="ChatsList">
{customChats.map((item, index) => (
<Card
key={index}
name={item.name}
lastMessage={item.lastMessage}
id={item.id}
color={getColor(item.id)}
/>
))}
</div>
<div className="ChatsList">
{customChats.map((item, index) => (
<Card
key={index}
name={item.name}
lastMessage={item.lastMessageData}
id={item.id}
color={getColor(item.id)}
/>
))}
</div>
);
};

View File

@ -2,9 +2,10 @@ import React, { useEffect, useState, useRef } from "react";
import { useNavigate } from "react-router-dom";
import "./css/Chat.css";
import { FaPaperPlane, FaSmile } from "react-icons/fa";
import {get, post} from "../backend/api";
import {displayMessage} from "../backend/notifications/notifications";
import {MessageType} from "../backend/notifications/message";
import { get, post } from "../backend/api";
import { displayMessage } from "../backend/notifications/notifications";
import { MessageType } from "../backend/notifications/message";
import io from "socket.io-client";
const emojis = [
"😀",
@ -88,8 +89,7 @@ const Chat = () => {
const [myId, setMyId] = useState("");
useEffect(() => {
// const id = parseInt(localStorage.getItem("interlocutorId"), 10) || 0;
const id = localStorage.getItem("interlocutorId")
const id = localStorage.getItem("interlocutorId");
setInterlocutorId(id);
const username = localStorage.getItem("username");
@ -100,36 +100,24 @@ const Chat = () => {
return () => {};
}
socket.current = new WebSocket("ws://localhost:8080");
socket.current = io("http://localhost:8099");
socket.current.onopen = () => {
console.log("WebSocket connected");
socket.current.send(
JSON.stringify({ type: "register", userId: "yourUserId" })
);
};
socket.current.on("receiveMessage", (message) => {
setMessages((prev) => [...prev, message]);
});
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");
};
socket.current.on("connect_error", (err) => {
console.error("Connection Error:", err.message);
});
return () => {
socket.current.close();
socket.current.disconnect();
};
}, []);
useEffect(() => {
retrieveMessages().then();
}, [myId, interlocutorId])
}, [myId, interlocutorId]);
useEffect(() => {
if (chatRef.current) {
@ -137,25 +125,24 @@ const Chat = () => {
}
}, [messages]);
// The function for sending message to the DB
async function sendMessageToDB (messageData) {
const { ok, data } = post('/chat/message/' + myId + '/' + interlocutorId, { message: messageData });
async function sendMessageToDB(messageData) {
const { ok, data } = await post(
"/chat/message/" + myId + "/" + interlocutorId,
{ message: messageData }
);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
}
}
// The function retrieves messages from the DB for the current chat
async function retrieveMessages () {
if (!myId || !interlocutorId) {return;}
const { ok, data } = await get('/chat/item/' + myId + '/' + interlocutorId);
async function retrieveMessages() {
if (!myId || !interlocutorId) return;
const { ok, data } = await get("/chat/item/" + myId + "/" + interlocutorId);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return;
}
setMessages(data.chat.messages);
}
@ -165,15 +152,11 @@ const Chat = () => {
senderId: myId,
recipientId: interlocutorId,
data: newMessage,
timestamp: new Date().toLocaleTimeString(),
timestamp: new Date().toLocaleString(),
};
socket.current.send(JSON.stringify(messageData));
socket.current.emit("sendMessage", messageData);
setMessages((prev) => [...prev, messageData]);
sendMessageToDB(messageData).then();
console.log('format:', messageData);
setNewMessage("");
}
};
@ -198,8 +181,7 @@ const Chat = () => {
className="home-button"
>
Home
</button>{" "}
{}
</button>
</div>
<div className="chat-messages" ref={chatRef}>
{messages.map((msg, index) => (
@ -210,8 +192,7 @@ const Chat = () => {
}`}
>
<div className="message-content">
<b>{msg.senderId === myId ? "You" : "They"}:</b>{" "}
{msg.data}
<b>{msg.senderId === myId ? "You" : "They"}:</b> {msg.data}
</div>
<span className="message-timestamp">{msg.timestamp}</span>
</div>

View File

@ -1,77 +1,86 @@
import React, {useEffect, useState} from "react";
import React, { useEffect, useState } from "react";
import HomeTitle from "../components/home/HomeTitle.jsx";
import ChatsList from "../components/home/ChatsList.jsx";
import Header from "../components/home/Header.jsx";
import {displayMessage} from "../backend/notifications/notifications";
import {MessageType} from "../backend/notifications/message";
import {get, post} from "../backend/api";
import { displayMessage } from "../backend/notifications/notifications";
import { MessageType } from "../backend/notifications/message";
import { get, post } from "../backend/api";
import InputField from "../components/reg/InputField.jsx";
import Search from "../components/home/Search.jsx";
import {URLs} from "../__data__/urls";
import { URLs } from "../__data__/urls";
const Home = () => {
const [chats, setChats] = useState([])
const [interlocutor, setInterlocutor] = useState("")
const [chats, setChats] = useState([]);
const [interlocutor, setInterlocutor] = useState("");
async function retrieveChats() {
const username = localStorage.getItem("username");
if (!username) {
displayMessage("You're not logged in!", MessageType.WARN);
return;
}
const {ok, data} = await get('/chat/list/' + username);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return;
}
setChats(data.chats);
async function retrieveChats() {
const username = localStorage.getItem("username");
if (!username) {
displayMessage("You're not logged in!", MessageType.WARN);
return;
}
async function createChat(alias) {
const username = localStorage.getItem("username");
if (!username) {
displayMessage("You're not logged in!", MessageType.WARN);
return;
}
displayMessage("Sent", MessageType.INFO);
const {ok, data} = await post('/chat/item/' + username + '/' + alias);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
} else {
localStorage.setItem('message', 'Successfully opened chat!');
localStorage.setItem('interlocutorId', alias);
window.location.href = URLs.chat.url;
}
const { ok, data } = await get("/chat/list/" + username);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return;
}
useEffect(() => {retrieveChats().then()}, [])
const sortedChats = data.chats.sort((a, b) => {
const lastMessageA = new Date(a.lastMessageTimestamp);
const lastMessageB = new Date(b.lastMessageTimestamp);
return lastMessageB - lastMessageA;
});
return (
<div className="homeWrapper">
<div className="headerPos">
<Header/>
</div>
setChats(sortedChats);
}
<HomeTitle/>
async function createChat(alias) {
const username = localStorage.getItem("username");
if (!username) {
displayMessage("You're not logged in!", MessageType.WARN);
return;
}
<div className="search-input">
<InputField
title="Create new chat"
value={interlocutor}
setValue={setInterlocutor}
placeholder="Enter the username (id)"
/>
</div>
displayMessage("Sent", MessageType.INFO);
<Search search={createChat} item={interlocutor}/>
const { ok, data } = await post("/chat/item/" + username + "/" + alias);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
} else {
localStorage.setItem("message", "Successfully opened chat!");
localStorage.setItem("interlocutorId", alias);
window.location.href = URLs.chat.url;
}
}
<p>Your chats</p>
<ChatsList chats={chats} />
useEffect(() => {
retrieveChats();
}, []);
return (
<div className="homeWrapper">
<div className="headerPos">
<Header />
</div>
)
}
export default Home
<HomeTitle />
<div className="search-input">
<InputField
title="Create new chat"
value={interlocutor}
setValue={setInterlocutor}
placeholder="Enter the username (id)"
/>
</div>
<Search search={createChat} item={interlocutor} />
<p>Your chats</p>
<ChatsList chats={chats} />
</div>
);
};
export default Home;

View File

@ -1,3 +1,7 @@
body {
background: linear-gradient(to right, #e0f7fa, #fffde7);
}
.chat-container {
display: flex;
flex-direction: column;
@ -11,10 +15,13 @@
}
.chat-header {
padding: 10px;
padding: 15px;
background-color: #007bff;
color: white;
text-align: center;
font-weight: bold;
letter-spacing: 1px;
border-bottom: 2px solid #0056b3;
}
.chat-messages {
@ -24,6 +31,7 @@
padding: 10px;
overflow-y: auto;
background-color: #fff;
max-height: 400px;
}
.message-bubble {
@ -33,6 +41,12 @@
max-width: 70%;
word-wrap: break-word;
display: inline-block;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
}
.message-bubble:hover {
transform: translateY(-2px);
}
.sent {
@ -58,8 +72,9 @@
.message-timestamp {
font-size: 10px;
color: #999;
margin-top: 5px;
color: black;
text-align: right;
margin-top: 4px;
display: block;
}

View File

@ -7,25 +7,25 @@
"data": "Hello Bob!",
"senderId": "alice",
"recipientId": "bobsm",
"timestamp": "07:00:00 AM"
"timestamp": "09.10.2024 07:00:00"
},
{
"data": "Hey Alice, how are you?",
"senderId": "bobsm",
"recipientId": "alice",
"timestamp": "07:05:00 AM"
"timestamp": "09.10.2024 07:05:00"
},
{
"data": "I'm good, thanks for asking.",
"senderId": "alice",
"recipientId": "bobsm",
"timestamp": "07:10:00 AM"
"timestamp": "09.10.2024 07:10:00"
},
{
"data": "Glad to hear!",
"senderId": "bobsm",
"recipientId": "alice",
"timestamp": "07:15:00 AM"
"timestamp": "09.10.2024 07:15:00"
}
]
},
@ -37,25 +37,25 @@
"data": "How's the project going?",
"senderId": "alice",
"recipientId": "charl",
"timestamp": "07:20:00 AM"
"timestamp": "09.10.2024 07:20:00"
},
{
"data": "It's coming along, almost done!",
"senderId": "charl",
"recipientId": "alice",
"timestamp": "07:25:00 AM"
"timestamp": "09.10.2024 07:25:00"
},
{
"data": "That's great to hear!",
"senderId": "alice",
"recipientId": "charl",
"timestamp": "07:30:00 AM"
"timestamp": "09.10.2024 07:30:00"
},
{
"data": "Thanks for checking in.",
"senderId": "charl",
"recipientId": "alice",
"timestamp": "07:35:00 AM"
"timestamp": "09.10.2024 07:35:00"
}
]
},
@ -67,55 +67,25 @@
"data": "Did you get the files?",
"senderId": "david",
"recipientId": "alice",
"timestamp": "07:40:00 AM"
"timestamp": "09.10.2024 07:40:00"
},
{
"data": "Yes, I did. Thank you!",
"senderId": "alice",
"recipientId": "david",
"timestamp": "07:45:00 AM"
"timestamp": "09.10.2024 07:45:00"
},
{
"data": "You're welcome.",
"senderId": "david",
"recipientId": "alice",
"timestamp": "07:50:00 AM"
"timestamp": "09.10.2024 07:50:00"
},
{
"data": "Let me know if you need anything else.",
"senderId": "alice",
"recipientId": "david",
"timestamp": "07:55:00 AM"
}
]
},
{
"id1": "alice",
"id2": "evead",
"messages": [
{
"data": "Eve, do you have the meeting details?",
"senderId": "alice",
"recipientId": "evead",
"timestamp": "08:00:00 AM"
},
{
"data": "Yes, I just sent them to you.",
"senderId": "evead",
"recipientId": "alice",
"timestamp": "08:05:00 AM"
},
{
"data": "Got it, thanks!",
"senderId": "alice",
"recipientId": "evead",
"timestamp": "08:10:00 AM"
},
{
"data": "You're welcome.",
"senderId": "evead",
"recipientId": "alice",
"timestamp": "08:15:00 AM"
"timestamp": "09.10.2024 07:55:00"
}
]
},
@ -127,25 +97,25 @@
"data": "Can you review this document for me?",
"senderId": "alice",
"recipientId": "frank",
"timestamp": "08:20:00 AM"
"timestamp": "09.10.2024 08:20:00"
},
{
"data": "Sure, I'll take a look.",
"senderId": "frank",
"recipientId": "alice",
"timestamp": "08:25:00 AM"
"timestamp": "09.10.2024 08:25:00"
},
{
"data": "Thanks, much appreciated!",
"senderId": "alice",
"recipientId": "frank",
"timestamp": "08:30:00 AM"
"timestamp": "09.10.2024 08:30:00"
},
{
"data": "No problem.",
"senderId": "frank",
"recipientId": "alice",
"timestamp": "08:35:00 AM"
"timestamp": "09.10.2024 08:35:00"
}
]
},
@ -157,25 +127,25 @@
"data": "Hey Grace, let's meet up for coffee!",
"senderId": "alice",
"recipientId": "grace",
"timestamp": "08:40:00 AM"
"timestamp": "09.10.2024 08:40:00"
},
{
"data": "Sounds good, when are you free?",
"senderId": "grace",
"recipientId": "alice",
"timestamp": "08:45:00 AM"
"timestamp": "09.10.2024 08:45:00"
},
{
"data": "How about tomorrow afternoon?",
"senderId": "alice",
"recipientId": "grace",
"timestamp": "08:50:00 AM"
"timestamp": "09.10.2024 08:50:00"
},
{
"data": "Works for me!",
"senderId": "grace",
"recipientId": "alice",
"timestamp": "08:55:00 AM"
"timestamp": "09.10.2024 08:55:00"
}
]
},
@ -187,25 +157,25 @@
"data": "Hannah, do you have a moment?",
"senderId": "alice",
"recipientId": "hanna",
"timestamp": "09:00:00 AM"
"timestamp": "09.10.2024 09:00:00"
},
{
"data": "Sure, what's up?",
"senderId": "hanna",
"recipientId": "alice",
"timestamp": "09:05:00 AM"
"timestamp": "09.10.2024 09:05:00"
},
{
"data": "Just wanted to check on the report.",
"senderId": "alice",
"recipientId": "hanna",
"timestamp": "09:10:00 AM"
"timestamp": "09.10.2024 09:10:00"
},
{
"data": "I'll send it soon.",
"senderId": "hanna",
"recipientId": "alice",
"timestamp": "09:15:00 AM"
"timestamp": "09.10.2024 09:15:00"
}
]
},
@ -217,25 +187,25 @@
"data": "Ian, have you completed the review?",
"senderId": "alice",
"recipientId": "ianda",
"timestamp": "09:20:00 AM"
"timestamp": "09.10.2024 09:20:00"
},
{
"data": "Yes, I sent my feedback.",
"senderId": "ianda",
"recipientId": "alice",
"timestamp": "09:25:00 AM"
"timestamp": "09.10.2024 09:25:00"
},
{
"data": "Thanks for that.",
"senderId": "alice",
"recipientId": "ianda",
"timestamp": "09:30:00 AM"
"timestamp": "09.10.2024 09:30:00"
},
{
"data": "Anytime!",
"senderId": "ianda",
"recipientId": "alice",
"timestamp": "09:35:00 AM"
"timestamp": "09.10.2024 09:35:00"
}
]
},
@ -247,25 +217,25 @@
"data": "Jill, let's schedule a catch-up meeting.",
"senderId": "alice",
"recipientId": "jillt",
"timestamp": "09:40:00 AM"
"timestamp": "09.10.2024 09:40:00"
},
{
"data": "Sounds good, when works for you?",
"senderId": "jillt",
"recipientId": "alice",
"timestamp": "09:45:00 AM"
"timestamp": "09.10.2024 09:45:00"
},
{
"data": "Tomorrow afternoon?",
"senderId": "alice",
"recipientId": "jillt",
"timestamp": "09:50:00 AM"
"timestamp": "09.10.2024 09:50:00"
},
{
"data": "That works for me!",
"senderId": "jillt",
"recipientId": "alice",
"timestamp": "09:55:00 AM"
"timestamp": "09.10.2024 09:55:00"
}
]
},
@ -277,25 +247,25 @@
"data": "Eve, did you send the schedule?",
"senderId": "alice",
"recipientId": "evead",
"timestamp": "10:00:00 AM"
"timestamp": "09.10.2024 10:00:00"
},
{
"data": "Yes, just sent it.",
"senderId": "evead",
"recipientId": "alice",
"timestamp": "10:05:00 AM"
"timestamp": "09.10.2024 10:05:00"
},
{
"data": "Thanks, much appreciated!",
"senderId": "alice",
"recipientId": "evead",
"timestamp": "10:10:00 AM"
"timestamp": "09.10.2024 10:10:00"
},
{
"data": "No problem!",
"senderId": "evead",
"recipientId": "alice",
"timestamp": "10:15:00 AM"
"timestamp": "09.10.2024 10:15:00"
}
]
},
@ -307,25 +277,25 @@
"data": "How's everything going?",
"senderId": "bobsm",
"recipientId": "charl",
"timestamp": "10:20:00 AM"
"timestamp": "09.10.2024 10:20:00"
},
{
"data": "Pretty good, how about you?",
"senderId": "charl",
"recipientId": "bobsm",
"timestamp": "10:25:00 AM"
"timestamp": "09.10.2024 10:25:00"
},
{
"data": "Can't complain!",
"senderId": "bobsm",
"recipientId": "charl",
"timestamp": "10:30:00 AM"
"timestamp": "09.10.2024 10:30:00"
},
{
"data": "Glad to hear that.",
"senderId": "charl",
"recipientId": "bobsm",
"timestamp": "10:35:00 AM"
"timestamp": "09.10.2024 10:35:00"
}
]
},
@ -337,25 +307,25 @@
"data": "Can you send the report?",
"senderId": "bobsm",
"recipientId": "david",
"timestamp": "10:40:00 AM"
"timestamp": "09.10.2024 10:40:00"
},
{
"data": "I'll send it in an hour.",
"senderId": "david",
"recipientId": "bobsm",
"timestamp": "10:45:00 AM"
"timestamp": "09.10.2024 10:45:00"
},
{
"data": "Perfect, thanks.",
"senderId": "bobsm",
"recipientId": "david",
"timestamp": "10:50:00 AM"
"timestamp": "09.10.2024 10:50:00"
},
{
"data": "No problem.",
"senderId": "david",
"recipientId": "bobsm",
"timestamp": "10:55:00 AM"
"timestamp": "09.10.2024 10:55:00"
}
]
},
@ -367,25 +337,25 @@
"data": "Hey Eve, how's it going?",
"senderId": "charl",
"recipientId": "evead",
"timestamp": "11:00:00 AM"
"timestamp": "09.10.2024 11:00:00"
},
{
"data": "Good, how about you?",
"senderId": "evead",
"recipientId": "charl",
"timestamp": "11:05:00 AM"
"timestamp": "09.10.2024 11:05:00"
},
{
"data": "Can't complain!",
"senderId": "charl",
"recipientId": "evead",
"timestamp": "11:10:00 AM"
"timestamp": "09.10.2024 11:10:00"
},
{
"data": "Glad to hear.",
"senderId": "evead",
"recipientId": "charl",
"timestamp": "11:15:00 AM"
"timestamp": "09.10.2024 11:15:00"
}
]
},
@ -397,25 +367,25 @@
"data": "Do you have time to talk today?",
"senderId": "charl",
"recipientId": "frank",
"timestamp": "11:20:00 AM"
"timestamp": "09.10.2024 11:20:00"
},
{
"data": "I have a meeting, but I can chat afterward.",
"senderId": "frank",
"recipientId": "charl",
"timestamp": "11:25:00 AM"
"timestamp": "09.10.2024 11:25:00"
},
{
"data": "Sounds good.",
"senderId": "charl",
"recipientId": "frank",
"timestamp": "11:30:00 AM"
"timestamp": "09.10.2024 11:30:00"
},
{
"data": "I'll message you after.",
"senderId": "frank",
"recipientId": "charl",
"timestamp": "11:35:00 AM"
"timestamp": "09.10.2024 11:35:00"
}
]
},
@ -427,25 +397,25 @@
"data": "Did you review the document?",
"senderId": "david",
"recipientId": "frank",
"timestamp": "11:40:00 AM"
"timestamp": "09.10.2024 11:40:00"
},
{
"data": "Yes, it's all good.",
"senderId": "frank",
"recipientId": "david",
"timestamp": "11:45:00 AM"
"timestamp": "09.10.2024 11:45:00"
},
{
"data": "Great, thanks for the quick turnaround!",
"senderId": "david",
"recipientId": "frank",
"timestamp": "11:50:00 AM"
"timestamp": "09.10.2024 11:50:00"
},
{
"data": "No worries!",
"senderId": "frank",
"recipientId": "david",
"timestamp": "11:55:00 AM"
"timestamp": "09.10.2024 11:55:00"
}
]
},
@ -457,25 +427,25 @@
"data": "Grace, can you send the updated schedule?",
"senderId": "david",
"recipientId": "grace",
"timestamp": "12:00:00 PM"
"timestamp": "09.10.2024 12:00:00"
},
{
"data": "Yes, I'll send it in a few minutes.",
"senderId": "grace",
"recipientId": "david",
"timestamp": "12:05:00 PM"
"timestamp": "09.10.2024 12:05:00"
},
{
"data": "Thanks, much appreciated!",
"senderId": "david",
"recipientId": "grace",
"timestamp": "12:10:00 PM"
"timestamp": "09.10.2024 12:10:00"
},
{
"data": "You're welcome!",
"senderId": "grace",
"recipientId": "david",
"timestamp": "12:15:00 PM"
"timestamp": "09.10.2024 12:15:00"
}
]
},
@ -487,25 +457,25 @@
"data": "How are you today?",
"senderId": "frank",
"recipientId": "grace",
"timestamp": "12:20:00 PM"
"timestamp": "09.10.2024 12:20:00"
},
{
"data": "I'm doing well, thanks for asking.",
"senderId": "grace",
"recipientId": "frank",
"timestamp": "12:25:00 PM"
"timestamp": "09.10.2024 12:25:00"
},
{
"data": "Glad to hear that.",
"senderId": "frank",
"recipientId": "grace",
"timestamp": "12:30:00 PM"
"timestamp": "09.10.2024 12:30:00"
},
{
"data": "How about you?",
"senderId": "grace",
"recipientId": "frank",
"timestamp": "12:35:00 PM"
"timestamp": "09.10.2024 12:35:00"
}
]
},
@ -517,25 +487,25 @@
"data": "Did you attend the meeting?",
"senderId": "frank",
"recipientId": "hanna",
"timestamp": "12:40:00 PM"
"timestamp": "09.10.2024 12:40:00"
},
{
"data": "Yes, it was productive.",
"senderId": "hanna",
"recipientId": "frank",
"timestamp": "12:45:00 PM"
"timestamp": "09.10.2024 12:45:00"
},
{
"data": "Good to hear!",
"senderId": "frank",
"recipientId": "hanna",
"timestamp": "12:50:00 PM"
"timestamp": "09.10.2024 12:50:00"
},
{
"data": "Indeed, lots to follow up on.",
"senderId": "hanna",
"recipientId": "frank",
"timestamp": "12:55:00 PM"
"timestamp": "09.10.2024 12:55:00"
}
]
},
@ -547,25 +517,25 @@
"data": "Can we meet later today?",
"senderId": "grace",
"recipientId": "hanna",
"timestamp": "01:00:00 PM"
"timestamp": "09.10.2024 01:00:00"
},
{
"data": "Sure, what's a good time?",
"senderId": "hanna",
"recipientId": "grace",
"timestamp": "01:05:00 PM"
"timestamp": "09.10.2024 01:05:00"
},
{
"data": "How about 3 PM?",
"data": "How about 3?",
"senderId": "grace",
"recipientId": "hanna",
"timestamp": "01:10:00 PM"
"timestamp": "09.10.2024 01:10:00"
},
{
"data": "Works for me.",
"senderId": "hanna",
"recipientId": "grace",
"timestamp": "01:15:00 PM"
"timestamp": "09.10.2024 01:15:00"
}
]
},
@ -577,25 +547,25 @@
"data": "Ian, did you get the message I sent?",
"senderId": "grace",
"recipientId": "ianda",
"timestamp": "01:20:00 PM"
"timestamp": "09.10.2024 01:20:00"
},
{
"data": "Yes, I'll respond soon.",
"senderId": "ianda",
"recipientId": "grace",
"timestamp": "01:25:00 PM"
"timestamp": "09.10.2024 01:25:00"
},
{
"data": "Thanks, appreciate it!",
"senderId": "grace",
"recipientId": "ianda",
"timestamp": "01:30:00 PM"
"timestamp": "09.10.2024 01:30:00"
},
{
"data": "You're welcome!",
"senderId": "ianda",
"recipientId": "grace",
"timestamp": "01:35:00 PM"
"timestamp": "09.10.2024 01:35:00"
}
]
},
@ -607,25 +577,25 @@
"data": "Ian, do you have a minute?",
"senderId": "hanna",
"recipientId": "ianda",
"timestamp": "01:40:00 PM"
"timestamp": "09.10.2024 01:40:00"
},
{
"data": "Yes, what do you need?",
"senderId": "ianda",
"recipientId": "hanna",
"timestamp": "01:45:00 PM"
"timestamp": "09.10.2024 01:45:00"
},
{
"data": "Just a quick update on the project.",
"senderId": "hanna",
"recipientId": "ianda",
"timestamp": "01:50:00 PM"
"timestamp": "09.10.2024 01:50:00"
},
{
"data": "I'll email you the details.",
"senderId": "ianda",
"recipientId": "hanna",
"timestamp": "01:55:00 PM"
"timestamp": "09.10.2024 01:55:00"
}
]
},
@ -637,25 +607,25 @@
"data": "Jill, can we talk tomorrow?",
"senderId": "hanna",
"recipientId": "jillt",
"timestamp": "02:00:00 PM"
"timestamp": "09.10.2024 02:00:00"
},
{
"data": "Yes, I'm free after 2 PM.",
"data": "Yes, I'm free after 2.",
"senderId": "jillt",
"recipientId": "hanna",
"timestamp": "02:05:00 PM"
"timestamp": "09.10.2024 02:05:00"
},
{
"data": "Perfect, see you then.",
"senderId": "hanna",
"recipientId": "jillt",
"timestamp": "02:10:00 PM"
"timestamp": "09.10.2024 02:10:00"
},
{
"data": "Looking forward to it.",
"senderId": "jillt",
"recipientId": "hanna",
"timestamp": "02:15:00 PM"
"timestamp": "09.10.2024 02:15:00"
}
]
},
@ -667,25 +637,25 @@
"data": "Jill, I have the files you requested.",
"senderId": "ianda",
"recipientId": "jillt",
"timestamp": "02:20:00 PM"
"timestamp": "09.10.2024 02:20:00"
},
{
"data": "Thanks, please send them over.",
"senderId": "jillt",
"recipientId": "ianda",
"timestamp": "02:25:00 PM"
"timestamp": "09.10.2024 02:25:00"
},
{
"data": "I'll send them right now.",
"senderId": "ianda",
"recipientId": "jillt",
"timestamp": "02:30:00 PM"
"timestamp": "09.10.2024 02:30:00"
},
{
"data": "Great, thanks again!",
"senderId": "jillt",
"recipientId": "ianda",
"timestamp": "02:35:00 PM"
"timestamp": "09.10.2024 02:35:00"
}
]
}