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", "name": "enterfront",
"version": "0.3.0", "version": "0.4.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "enterfront", "name": "enterfront",
"version": "0.3.0", "version": "0.4.0",
"dependencies": { "dependencies": {
"@brojs/cli": "^1.0.0", "@brojs/cli": "^1.0.0",
"@brojs/create": "^1.0.0", "@brojs/create": "^1.0.0",
@ -23,6 +23,8 @@
"react-icons": "^5.3.0", "react-icons": "^5.3.0",
"react-router-dom": "^6.26.1", "react-router-dom": "^6.26.1",
"react-toastify": "^10.0.5", "react-toastify": "^10.0.5",
"socket.io": "^4.8.0",
"socket.io-client": "^4.8.0",
"styled-components": "^6.1.13", "styled-components": "^6.1.13",
"typescript": "^5.5.4", "typescript": "^5.5.4",
"ws": "^8.18.0" "ws": "^8.18.0"
@ -1853,30 +1855,6 @@
"node": ">=18" "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": { "node_modules/@brojs/cli/node_modules/i18next-browser-languagedetector": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz", "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz",
@ -2691,19 +2669,6 @@
"ijl-cli": "bin/ijl-cli" "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": { "node_modules/@ijl/cli/node_modules/loader-utils": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
@ -2717,35 +2682,6 @@
"node": ">=8.9.0" "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": { "node_modules/@ijl/cli/node_modules/react-hot-loader": {
"version": "4.13.1", "version": "4.13.1",
"resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.13.1.tgz", "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": { "node_modules/@ijl/cli/node_modules/source-map": {
"version": "0.7.4", "version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
@ -3071,12 +2996,14 @@
"node_modules/@types/cookie": { "node_modules/@types/cookie": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", "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": { "node_modules/@types/cors": {
"version": "2.8.17", "version": "2.8.17",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
"integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
} }
@ -3138,14 +3065,6 @@
"@types/react": "*" "@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": { "node_modules/@types/source-list-map": {
"version": "0.1.6", "version": "0.1.6",
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.6.tgz", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.6.tgz",
@ -3735,6 +3654,7 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
"license": "MIT",
"engines": { "engines": {
"node": "^4.5.0 || >= 5.9" "node": "^4.5.0 || >= 5.9"
} }
@ -4364,6 +4284,7 @@
"version": "2.8.5", "version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"license": "MIT",
"dependencies": { "dependencies": {
"object-assign": "^4", "object-assign": "^4",
"vary": "^1" "vary": "^1"
@ -4783,9 +4704,10 @@
} }
}, },
"node_modules/engine.io": { "node_modules/engine.io": {
"version": "6.5.5", "version": "6.6.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.1.tgz",
"integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", "integrity": "sha512-NEpDCw9hrvBW+hVEOK4T7v0jFJ++KgtPl4jKFwsZVfG1XhS0dCrSb3VMb9gPAd7VAdW52VT1EnaNiU2vM8C0og==",
"license": "MIT",
"dependencies": { "dependencies": {
"@types/cookie": "^0.4.1", "@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12", "@types/cors": "^2.8.12",
@ -4802,6 +4724,40 @@
"node": ">=10.2.0" "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": { "node_modules/engine.io-parser": {
"version": "5.2.3", "version": "5.2.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
@ -4814,6 +4770,7 @@
"version": "0.4.2", "version": "0.4.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
"license": "MIT",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
@ -8959,15 +8916,16 @@
} }
}, },
"node_modules/socket.io": { "node_modules/socket.io": {
"version": "4.7.5", "version": "4.8.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz",
"integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", "integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==",
"license": "MIT",
"dependencies": { "dependencies": {
"accepts": "~1.3.4", "accepts": "~1.3.4",
"base64id": "~2.0.0", "base64id": "~2.0.0",
"cors": "~2.8.5", "cors": "~2.8.5",
"debug": "~4.3.2", "debug": "~4.3.2",
"engine.io": "~6.5.2", "engine.io": "~6.6.0",
"socket.io-adapter": "~2.5.2", "socket.io-adapter": "~2.5.2",
"socket.io-parser": "~4.2.4" "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": { "node_modules/socket.io-parser": {
"version": "4.2.4", "version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", "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": { "node_modules/y18n": {
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@ -15,6 +15,8 @@
"react-icons": "^5.3.0", "react-icons": "^5.3.0",
"react-router-dom": "^6.26.1", "react-router-dom": "^6.26.1",
"react-toastify": "^10.0.5", "react-toastify": "^10.0.5",
"socket.io": "^4.8.0",
"socket.io-client": "^4.8.0",
"styled-components": "^6.1.13", "styled-components": "^6.1.13",
"typescript": "^5.5.4", "typescript": "^5.5.4",
"ws": "^8.18.0" "ws": "^8.18.0"
@ -27,5 +29,5 @@
"clean": "rimraf dist" "clean": "rimraf dist"
}, },
"name": "enterfront", "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 Card from "./Card.jsx";
import {get} from "../../backend/api"; import { get } from "../../backend/api";
import {displayMessage} from "../../backend/notifications/notifications"; import { displayMessage } from "../../backend/notifications/notifications";
import {MessageType} from "../../backend/notifications/message"; import { MessageType } from "../../backend/notifications/message";
const ChatsList = (props) => { 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 updatedChats = await Promise.all(
const username = localStorage.getItem("username"); chats.map(async (chat) => {
if (!username) {return null;} const interlocutorId = chat.id1 === username ? chat.id2 : chat.id1;
const updatedChats = await Promise.all( const { ok, data } = await get("/auth/" + interlocutorId);
chats.map(async (chat) => { if (!ok) {
const interlocutorId = chat.id1 === username ? chat.id2 : chat.id1 displayMessage(data.message, MessageType.ERROR);
return null;
}
const {ok, data} = await get('/auth/' + interlocutorId); const interlocutor = data.user;
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return null;
}
const interlocutor = data.user; const lastMessage =
chat.messages.length > 0
? chat.messages[chat.messages.length - 1]
: { data: "", timestamp: new Date(0).toISOString() };
return { return {
id: interlocutorId, id: interlocutorId,
name: interlocutor.nickname, name: interlocutor.nickname,
lastMessage: chat.messages.length > 0 ? chat.messages[chat.messages.length - 1].data : "", 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 = { useEffect(() => {
orange: 'FFA500FF', updateList().then();
aqua: '00FFFFFF', }, [chats]);
crimson: 'DC143CFF',
red: 'FF0000FF',
violet: '8A2BE2FF',
seagreen: '20B2AAFF',
green: 'ADFF2FFF',
blue: '0000FFFF',
pink: 'FF1493FF',
cyan: '72FAFAFF'
}
function getColor(chatId) { const colorMap = {
const keys = Object.keys(colorMap); orange: "FFA500FF",
const numericId = Array.from(chatId).reduce((sum, char) => sum + char.charCodeAt(0), 0); aqua: "00FFFFFF",
const index = numericId % keys.length; crimson: "DC143CFF",
return colorMap[keys[index]]; 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 ( return (
<div className="ChatsList"> <div className="ChatsList">
{customChats.map((item, index) => ( {customChats.map((item, index) => (
<Card <Card
key={index} key={index}
name={item.name} name={item.name}
lastMessage={item.lastMessage} lastMessage={item.lastMessageData}
id={item.id} id={item.id}
color={getColor(item.id)} color={getColor(item.id)}
/> />
))} ))}
</div> </div>
); );
}; };

View File

@ -2,9 +2,10 @@ import React, { useEffect, useState, useRef } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import "./css/Chat.css"; import "./css/Chat.css";
import { FaPaperPlane, FaSmile } from "react-icons/fa"; import { FaPaperPlane, FaSmile } from "react-icons/fa";
import {get, post} from "../backend/api"; import { get, post } from "../backend/api";
import {displayMessage} from "../backend/notifications/notifications"; import { displayMessage } from "../backend/notifications/notifications";
import {MessageType} from "../backend/notifications/message"; import { MessageType } from "../backend/notifications/message";
import io from "socket.io-client";
const emojis = [ const emojis = [
"😀", "😀",
@ -88,8 +89,7 @@ const Chat = () => {
const [myId, setMyId] = useState(""); const [myId, setMyId] = useState("");
useEffect(() => { useEffect(() => {
// const id = parseInt(localStorage.getItem("interlocutorId"), 10) || 0; const id = localStorage.getItem("interlocutorId");
const id = localStorage.getItem("interlocutorId")
setInterlocutorId(id); setInterlocutorId(id);
const username = localStorage.getItem("username"); const username = localStorage.getItem("username");
@ -100,36 +100,24 @@ const Chat = () => {
return () => {}; return () => {};
} }
socket.current = new WebSocket("ws://localhost:8080"); socket.current = io("http://localhost:8099");
socket.current.onopen = () => { socket.current.on("receiveMessage", (message) => {
console.log("WebSocket connected"); setMessages((prev) => [...prev, message]);
socket.current.send( });
JSON.stringify({ type: "register", userId: "yourUserId" })
);
};
socket.current.onmessage = (event) => { socket.current.on("connect_error", (err) => {
const receivedData = JSON.parse(event.data); console.error("Connection Error:", err.message);
setMessages((prev) => [...prev, receivedData]); });
};
socket.current.onerror = (event) => {
console.error("WebSocket error observed:", event);
};
socket.current.onclose = () => {
console.log("WebSocket closed");
};
return () => { return () => {
socket.current.close(); socket.current.disconnect();
}; };
}, []); }, []);
useEffect(() => { useEffect(() => {
retrieveMessages().then(); retrieveMessages().then();
}, [myId, interlocutorId]) }, [myId, interlocutorId]);
useEffect(() => { useEffect(() => {
if (chatRef.current) { if (chatRef.current) {
@ -137,25 +125,24 @@ const Chat = () => {
} }
}, [messages]); }, [messages]);
// The function for sending message to the DB async function sendMessageToDB(messageData) {
async function sendMessageToDB (messageData) { const { ok, data } = await post(
const { ok, data } = post('/chat/message/' + myId + '/' + interlocutorId, { message: messageData }); "/chat/message/" + myId + "/" + interlocutorId,
{ message: messageData }
);
if (!ok) { if (!ok) {
displayMessage(data.message, MessageType.ERROR); displayMessage(data.message, MessageType.ERROR);
} }
} }
// The function retrieves messages from the DB for the current chat async function retrieveMessages() {
async function retrieveMessages () { if (!myId || !interlocutorId) return;
if (!myId || !interlocutorId) {return;}
const { ok, data } = await get('/chat/item/' + myId + '/' + interlocutorId);
const { ok, data } = await get("/chat/item/" + myId + "/" + interlocutorId);
if (!ok) { if (!ok) {
displayMessage(data.message, MessageType.ERROR); displayMessage(data.message, MessageType.ERROR);
return; return;
} }
setMessages(data.chat.messages); setMessages(data.chat.messages);
} }
@ -165,15 +152,11 @@ const Chat = () => {
senderId: myId, senderId: myId,
recipientId: interlocutorId, recipientId: interlocutorId,
data: newMessage, 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]); setMessages((prev) => [...prev, messageData]);
sendMessageToDB(messageData).then(); sendMessageToDB(messageData).then();
console.log('format:', messageData);
setNewMessage(""); setNewMessage("");
} }
}; };
@ -198,8 +181,7 @@ const Chat = () => {
className="home-button" className="home-button"
> >
Home Home
</button>{" "} </button>
{}
</div> </div>
<div className="chat-messages" ref={chatRef}> <div className="chat-messages" ref={chatRef}>
{messages.map((msg, index) => ( {messages.map((msg, index) => (
@ -210,8 +192,7 @@ const Chat = () => {
}`} }`}
> >
<div className="message-content"> <div className="message-content">
<b>{msg.senderId === myId ? "You" : "They"}:</b>{" "} <b>{msg.senderId === myId ? "You" : "They"}:</b> {msg.data}
{msg.data}
</div> </div>
<span className="message-timestamp">{msg.timestamp}</span> <span className="message-timestamp">{msg.timestamp}</span>
</div> </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 HomeTitle from "../components/home/HomeTitle.jsx";
import ChatsList from "../components/home/ChatsList.jsx"; import ChatsList from "../components/home/ChatsList.jsx";
import Header from "../components/home/Header.jsx"; import Header from "../components/home/Header.jsx";
import {displayMessage} from "../backend/notifications/notifications"; import { displayMessage } from "../backend/notifications/notifications";
import {MessageType} from "../backend/notifications/message"; import { MessageType } from "../backend/notifications/message";
import {get, post} from "../backend/api"; import { get, post } from "../backend/api";
import InputField from "../components/reg/InputField.jsx"; import InputField from "../components/reg/InputField.jsx";
import Search from "../components/home/Search.jsx"; import Search from "../components/home/Search.jsx";
import {URLs} from "../__data__/urls"; import { URLs } from "../__data__/urls";
const Home = () => { const Home = () => {
const [chats, setChats] = useState([]) const [chats, setChats] = useState([]);
const [interlocutor, setInterlocutor] = useState("") const [interlocutor, setInterlocutor] = useState("");
async function retrieveChats() { async function retrieveChats() {
const username = localStorage.getItem("username"); const username = localStorage.getItem("username");
if (!username) { if (!username) {
displayMessage("You're not logged in!", MessageType.WARN); displayMessage("You're not logged in!", MessageType.WARN);
return; return;
}
const {ok, data} = await get('/chat/list/' + username);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return;
}
setChats(data.chats);
} }
async function createChat(alias) { const { ok, data } = await get("/chat/list/" + username);
const username = localStorage.getItem("username"); if (!ok) {
if (!username) { displayMessage(data.message, MessageType.ERROR);
displayMessage("You're not logged in!", MessageType.WARN); return;
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;
}
} }
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 ( setChats(sortedChats);
<div className="homeWrapper"> }
<div className="headerPos">
<Header/>
</div>
<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"> displayMessage("Sent", MessageType.INFO);
<InputField
title="Create new chat"
value={interlocutor}
setValue={setInterlocutor}
placeholder="Enter the username (id)"
/>
</div>
<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> useEffect(() => {
<ChatsList chats={chats} /> retrieveChats();
}, []);
return (
<div className="homeWrapper">
<div className="headerPos">
<Header />
</div> </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 { .chat-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -11,10 +15,13 @@
} }
.chat-header { .chat-header {
padding: 10px; padding: 15px;
background-color: #007bff; background-color: #007bff;
color: white; color: white;
text-align: center; text-align: center;
font-weight: bold;
letter-spacing: 1px;
border-bottom: 2px solid #0056b3;
} }
.chat-messages { .chat-messages {
@ -24,6 +31,7 @@
padding: 10px; padding: 10px;
overflow-y: auto; overflow-y: auto;
background-color: #fff; background-color: #fff;
max-height: 400px;
} }
.message-bubble { .message-bubble {
@ -33,6 +41,12 @@
max-width: 70%; max-width: 70%;
word-wrap: break-word; word-wrap: break-word;
display: inline-block; 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 { .sent {
@ -58,8 +72,9 @@
.message-timestamp { .message-timestamp {
font-size: 10px; font-size: 10px;
color: #999; color: black;
margin-top: 5px; text-align: right;
margin-top: 4px;
display: block; display: block;
} }

View File

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