25 Commits

Author SHA1 Message Date
79289457c9 39% but error 2024-10-19 10:57:44 +03:00
5f386c0f4e some progress 2024-10-19 10:48:21 +03:00
33c8f863a1 AccountButton done 2024-10-19 10:28:37 +03:00
Nikolai Petukhov
dd16f42995 working tests 2024-10-19 10:13:06 +03:00
f3e93bae19 coverage 2024-10-19 08:44:11 +03:00
07728cbbb0 tests 2024-10-19 08:16:51 +03:00
17697d7f77 tests 2024-10-19 07:48:30 +03:00
Nikolai Petukhov
2829c11e4c 0.5.4 2024-10-16 23:48:44 +03:00
Nikolai Petukhov
0bcdb95b57 change to dev api 2024-10-16 23:48:26 +03:00
Nikolai Petukhov
cabe02be57 0.5.3 2024-10-16 23:43:37 +03:00
Nikolai Petukhov
59d4a44079 integrated redux library 2024-10-16 23:40:34 +03:00
Nikolai Petukhov
fde1f8ecfe fix chats sorting 2024-10-16 23:16:06 +03:00
Nikolai Petukhov
9b4870995f 0.5.2 2024-10-12 12:44:15 +03:00
Nikolai Petukhov
2f447cef1a fixed init routes 2024-10-12 12:44:04 +03:00
Nikolai Petukhov
964ca236e8 0.5.1 2024-10-12 12:33:46 +03:00
Nikolai Petukhov
9c1c670ccb small fix with routes 2024-10-12 12:25:38 +03:00
Nikolai Petukhov
ff9bd3ac8c 0.5.0 2024-10-12 12:24:16 +03:00
Nikolai Petukhov
51618c8858 small changes 2024-10-12 12:23:25 +03:00
Nikolai Petukhov
54f6c5c053 config fix 2024-10-12 11:39:17 +03:00
Nikolai Petukhov
8fecf7cb1f I have added intervals for messages 2024-10-11 13:39:15 +03:00
Nikolai Petukhov
49a8af611f done 2024-10-10 12:03:37 +03:00
Nikolai Petukhov
7c4457dea4 done 2024-10-10 12:02:10 +03:00
Nikolai Petukhov
6096bdc4cb done 2024-10-10 12:01:49 +03:00
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
40 changed files with 5314 additions and 1012 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
node_modules
.idea
coverage/

1
__mocks__/fileMock.js Normal file
View File

@@ -0,0 +1 @@
module.exports = 'test-file-stub';

View File

@@ -0,0 +1,11 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import AccountButtons from '../src/components/account/AccountButtons.jsx';
describe('AccountButtons Component', () => {
it('should render the Back link', () => {
render(<AccountButtons registered={false} />);
const backLinkElement = screen.getByRole('link', { name: /back/i });
expect(backLinkElement).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,18 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import ActionButton from '../src/components/account/ActionButton.jsx';
describe('ActionButton Component', () => {
it('should render with the correct title and call the action when clicked', () => {
const mockAction = jest.fn();
const title = 'Click Me';
render(<ActionButton action={mockAction} title={title} />);
const buttonElement = screen.getByText(title);
expect(buttonElement).toBeInTheDocument();
fireEvent.click(buttonElement);
expect(mockAction).toHaveBeenCalledTimes(1);
});
});

23
__tests__/Card.test.tsx Normal file
View File

@@ -0,0 +1,23 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Card from '../src/components/home/Card.jsx';
describe('Card Component', () => {
it('should render the Card component with the given ID', () => {
const testId = '123';
render(<Card id={testId} color={"FFA500FF"}/>);
const cardElement = screen.getByText(/123/i);
expect(cardElement).toBeInTheDocument();
});
it('should store the ID in local storage when clicked', () => {
const testId = '456';
render(<Card id={testId} color={"FFA500FF"}/>);
const cardElement = screen.getByText(/456/i);
fireEvent.click(cardElement);
expect(localStorage.setItem).toHaveBeenCalledWith('selectedId', testId);
});
});

View File

@@ -0,0 +1,24 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import HelloItem from '../src/components/account/HelloItem.jsx';
describe('HelloItem Component', () => {
it('should display a personalized greeting when nickname is provided', () => {
const nickname = 'JohnDoe';
const id = '12345';
render(<HelloItem nickname={nickname} id={id} />);
const greetingElement = screen.getByText(`Hello, ${nickname}!`);
const idElement = screen.getByText(`Your ID: ${id}`);
expect(greetingElement).toBeInTheDocument();
expect(idElement).toBeInTheDocument();
});
it('should display a default message when nickname is not provided', () => {
render(<HelloItem nickname="" id="12345" />);
const defaultMessage = screen.getByText("You don't have an account :(");
expect(defaultMessage).toBeInTheDocument();
});
});

60
__tests__/Home.test.jsx Normal file
View File

@@ -0,0 +1,60 @@
import React from "react";
import { render, screen } from "@testing-library/react";
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";
import Home from "src/pages/Home";
import { useGetChatsQuery } from "src/backend/redux/api_slice";
// Mock Redux store
const mockStore = configureStore([]);
// Mock the useGetChatsQuery hook
jest.mock("src/backend/redux/api_slice", () => ({
useGetChatsQuery: jest.fn(),
}));
describe("Home Page", () => {
let store;
beforeEach(() => {
store = mockStore({});
});
test("renders Home page with loading state", () => {
useGetChatsQuery.mockReturnValue({ isLoading: true });
render(
<Provider store={store}>
<Home />
</Provider>
);
expect(screen.getByText(/loading/i)).toBeInTheDocument();
});
test("renders Home page with chat data", () => {
const chatsData = [{ id: 1, name: "Chat 1" }, { id: 2, name: "Chat 2" }];
useGetChatsQuery.mockReturnValue({ data: chatsData, isLoading: false });
render(
<Provider store={store}>
<Home />
</Provider>
);
expect(screen.getByText("Chat 1")).toBeInTheDocument();
expect(screen.getByText("Chat 2")).toBeInTheDocument();
});
test("renders error message when fetching chats fails", () => {
useGetChatsQuery.mockReturnValue({ error: true, isLoading: false });
render(
<Provider store={store}>
<Home />
</Provider>
);
expect(screen.getByText(/error/i)).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,41 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import InputField from '../src/components/reg/InputField.jsx';
describe('InputField Component', () => {
it('should render with the correct title and placeholder', () => {
const title = 'Username';
const placeholder = 'Enter your username';
render(<InputField title={title} placeholder={placeholder} value="" setValue={() => {}} />);
const titleElement = screen.getByText(title);
const inputElement = screen.getByPlaceholderText(placeholder);
expect(titleElement).toBeInTheDocument();
expect(inputElement).toBeInTheDocument();
});
it('should call setValue on input change', () => {
const mockSetValue = jest.fn();
const newValue = 'testUser';
render(<InputField title="Username" value="" setValue={mockSetValue} />);
const inputElement = screen.getByRole('textbox');
fireEvent.change(inputElement, { target: { value: newValue } });
expect(mockSetValue).toHaveBeenCalledWith(newValue);
});
it('should call submit function when Enter key is pressed', () => {
const mockSubmit = jest.fn();
render(<InputField title="Username" value="" setValue={() => {}} submit={mockSubmit} />);
const inputElement = screen.getByRole('textbox');
fireEvent.keyDown(inputElement, { key: 'Enter', code: 'Enter' });
expect(mockSubmit).toHaveBeenCalledTimes(1);
});
});

View File

@@ -0,0 +1,16 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import NavButton from '../src/components/init/NavButton.jsx';
describe('NavButton Component', () => {
it('should render the NavButton with the correct text and link', () => {
const navLink = '/home';
const buttonText = 'Go Home';
render(<NavButton nav={navLink} text={buttonText} />);
const linkElement = screen.getByText(buttonText);
expect(linkElement).toBeInTheDocument();
expect(linkElement.closest('a')).toHaveAttribute('href', navLink);
});
});

23
__tests__/Search.test.tsx Normal file
View File

@@ -0,0 +1,23 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Search from '../src/components/home/Search.jsx';
describe('Search Component', () => {
it('should render the Search button', () => {
render(<Search search={() => {}} item="testItem" />);
const searchButton = screen.getByText(/find/i);
expect(searchButton).toBeInTheDocument();
});
it('should call the search function with the correct item when clicked', () => {
const mockSearch = jest.fn();
const item = 'testItem';
render(<Search search={mockSearch} item={item} />);
const searchButton = screen.getByText(/find/i);
fireEvent.click(searchButton);
expect(mockSearch).toHaveBeenCalledWith(item);
});
});

52
__tests__/SignIn.test.jsx Normal file
View File

@@ -0,0 +1,52 @@
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import SignIn from "src/pages/SignIn";
import { displayMessage } from "src/backend/notifications/notifications";
import { post } from "src/backend/api";
// Mock the displayMessage and post functions
jest.mock("src/backend/notifications/notifications", () => ({
displayMessage: jest.fn(),
}));
jest.mock("src/backend/api", () => ({
post: jest.fn(),
}));
describe("SignIn Page", () => {
beforeEach(() => {
jest.clearAllMocks();
});
test("renders SignIn form", () => {
render(<SignIn />);
expect(screen.getByLabelText(/username/i)).toBeInTheDocument();
expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
expect(screen.getByText(/sign in/i)).toBeInTheDocument();
});
test("displays error message on failed login", async () => {
post.mockResolvedValueOnce({ ok: false, data: { message: "Invalid credentials" } });
render(<SignIn />);
fireEvent.change(screen.getByLabelText(/username/i), { target: { value: "user" } });
fireEvent.change(screen.getByLabelText(/password/i), { target: { value: "password" } });
fireEvent.click(screen.getByText(/sign in/i));
expect(await screen.findByText(/invalid credentials/i)).toBeInTheDocument();
expect(displayMessage).toHaveBeenCalledWith("Invalid credentials", "error");
});
test("displays additional info message after multiple login attempts", async () => {
post.mockResolvedValueOnce({ ok: false, data: { message: "Invalid credentials" } });
render(<SignIn />);
fireEvent.change(screen.getByLabelText(/username/i), { target: { value: "user" } });
fireEvent.change(screen.getByLabelText(/password/i), { target: { value: "password" } });
// Simulate two failed login attempts
fireEvent.click(screen.getByText(/sign in/i));
fireEvent.click(screen.getByText(/sign in/i));
expect(displayMessage).toHaveBeenCalledWith("Note that you need to enter your ID name", "info");
});
});

View File

@@ -0,0 +1,13 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
describe('Hello World Test', () => {
it('should display hello world', () => {
// Render a simple component
render(<div>Hello World</div>);
// Check if "Hello World" is in the document
const helloWorldElement = screen.getByText(/hello world/i);
expect(helloWorldElement).toBeInTheDocument();
});
});

7
babel.config.js Normal file
View File

@@ -0,0 +1,7 @@
module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-react',
'@babel/preset-typescript'],
};

28
jest.config.ts Normal file
View File

@@ -0,0 +1,28 @@
import type { Config } from 'jest';
import { defaults } from 'jest-config';
const config: Config = {
clearMocks: true,
collectCoverage: true,
collectCoverageFrom: [
"src/components/**/*.{js,jsx,ts,tsx}", // Include all components
"src/pages/**/*.{js,jsx,ts,tsx}",
"!src/**/*.test.{js,jsx,ts,tsx}", // Exclude test files
"!src/**/index.{js,jsx,ts,tsx}", // Optionally exclude index files
],
coverageDirectory: "coverage",
coverageProvider: "v8",
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
testEnvironment: "jsdom",
// transform: {
// '^.+\\.(ts|tsx|js|jsx)$': 'babel-jest',
// },
moduleNameMapper: {
"^@/src/(.*)$": "<rootDir>/src/$1", // Map '@/src' to the 'src' folder
"^src/(.*)$": "<rootDir>/src/$1", // Map 'src' to the 'src' folder
"\\.(svg|png|jpg|jpeg|gif)$": "<rootDir>/__mocks__/fileMock.js", // Add this line
},
moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx', 'js', 'jsx'],
};
export default config;

17
jest.setup.ts Normal file
View File

@@ -0,0 +1,17 @@
import { jest } from '@jest/globals';
import '@testing-library/jest-dom';
import mockedConfig from './bro.config.js'
jest.mock('@brojs/cli', () => {
return {
getNavigations() {
return mockedConfig.navigations
},
getNavigationsValue(key) {
return mockedConfig.navigations[key]
},
getConfigValue(key) {
return mockedConfig.config[key]
}
}
})

5187
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
"@brojs/cli": "^1.0.0",
"@brojs/create": "^1.0.0",
"@ijl/cli": "^5.1.0",
"@reduxjs/toolkit": "^2.3.0",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"dotenv": "^16.4.5",
@@ -13,8 +14,11 @@
"react-dom": "^18.3.1",
"react-emoji-picker": "^1.0.13",
"react-icons": "^5.3.0",
"react-redux": "^9.1.2",
"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"
@@ -24,8 +28,25 @@
"start": "brojs server --port=8099 --with-open-browser",
"build": "npm run clean && brojs build --dev",
"build:prod": "npm run clean && brojs build",
"clean": "rimraf dist"
"clean": "rimraf dist",
"test": "jest",
"test:watch": "jest --watchAll"
},
"name": "enterfront",
"version": "0.3.0"
"version": "0.5.4",
"devDependencies": {
"@babel/core": "^7.25.8",
"@babel/preset-env": "^7.25.8",
"@babel/preset-react": "^7.25.7",
"@babel/preset-typescript": "^7.25.7",
"@testing-library/jest-dom": "^6.6.2",
"@testing-library/react": "^16.0.1",
"@types/jest": "^29.5.13",
"babel-jest": "^29.7.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"redux-mock-store": "^1.5.4",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2"
}
}

View File

@@ -6,6 +6,9 @@ import { Dashboard } from './dashboard';
import {ToastContainer} from "react-toastify";
import 'react-toastify/dist/ReactToastify.css';
import { Provider } from 'react-redux';
import store from './backend/redux/store.js'; // Import your store
import './index.css'
import {displayMessage} from "./backend/notifications/notifications.js";
@@ -26,13 +29,13 @@ const App = () => {
}, []);
return(
<div>
<Provider store={store}>
<BrowserRouter>
<Dashboard />
</BrowserRouter>
<ToastContainer/>
</div>
</Provider>
)
}

View File

@@ -2,9 +2,9 @@ import {getConfigValue} from "@brojs/cli";
const LOCAL = "http://localhost:8099";
const DEV = "";
const DEV = "https://dev.bro-js.ru";
export const BASE_API_URL = LOCAL + getConfigValue("enterfront.api") + "/enterfront";
export const BASE_API_URL = LOCAL + getConfigValue("enterfront.api");
// fetch(`${BASE_API_URL}/books/list`)

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}`);
}
});
});
});

View File

@@ -1,8 +0,0 @@
export default class Interlocutor {
constructor(id, name) {
this.name = name;
this.id = id;
}
static name;
static id;
}

View File

@@ -0,0 +1,34 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import {getConfigValue} from "@brojs/cli";
import { BASE_API_URL } from "../api.js";
const baseQuery = fetchBaseQuery({
baseUrl: BASE_API_URL,
prepareHeaders: (headers) => {
const token = localStorage.getItem('token');
if (token) {
headers.set('Authorization', `Bearer ${token}`);
}
return headers;
},
});
export const apiSlice = createApi({
reducerPath: 'api',
baseQuery,
endpoints: (builder) => ({
getChats: builder.query({
query: (username) => `/chat/list/${username}`,
}),
postChat: builder.mutation({
query: ({ id1, id2 }) => ({
url: `/chat/item/${id1}/${id2}`,
method: 'POST',
}),
}),
}),
});
// Export hooks for usage in functional components
export const { useGetChatsQuery, usePostChatMutation } = apiSlice;

View File

@@ -0,0 +1,12 @@
import { configureStore } from '@reduxjs/toolkit';
import { apiSlice } from './api_slice';
const store = configureStore({
reducer: {
[apiSlice.reducerPath]: apiSlice.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(apiSlice.middleware),
});
export default store;

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,7 +0,0 @@
export default class User {
constructor(id, name) {
this.id = id;
this.name = name;
this.status = "online";
}
}

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

@@ -13,7 +13,7 @@ const InputField = (props) => {
onKeyDown={(e) => {
if (e.key === 'Enter') {
if (props.submit) {
props.enter(props.submit);
props.submit();
}
}
}}

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,29 @@ 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])
// retrieveMessages().then();
const interval = setInterval(() => {
retrieveMessages().then()
}, 2000);
return () => clearInterval(interval)
}, [myId, interlocutorId]);
useEffect(() => {
if (chatRef.current) {
@@ -137,25 +130,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 +157,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 +186,7 @@ const Chat = () => {
className="home-button"
>
Home
</button>{" "}
{}
</button>
</div>
<div className="chat-messages" ref={chatRef}>
{messages.map((msg, index) => (
@@ -210,8 +197,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,115 @@
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 { useGetChatsQuery, usePostChatMutation } from "../backend/redux/api_slice"; // Update the import based on your API slice
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([]); // Retained original variable name
const [interlocutor, setInterlocutor] = useState("");
async function retrieveChats() {
const username = localStorage.getItem("username");
if (!username) {
displayMessage("You're not logged in!", MessageType.WARN);
return;
}
const username = localStorage.getItem("username");
const {ok, data} = await get('/chat/list/' + username);
if (!ok) {
displayMessage(data.message, MessageType.ERROR);
return;
}
setChats(data.chats);
// Use Redux Queries
const { data: chatsData, error: getError, isLoading: isGetting } = useGetChatsQuery(username, {
skip: !username
});
console.log('From Redux:', chatsData);
const [createChat, { error: postError }] = usePostChatMutation();
useEffect(() => {
if (getError) {
displayMessage(getError.message, MessageType.ERROR);
}
if (getError) {
displayMessage(getError.message, MessageType.ERROR);
}
}, [getError, postError]);
useEffect(() => {
if (chatsData) {
// setChats(chatsData.chats);
let data = chatsData.chats;
try {
const sortedChats = [...data].sort((a, b) => {
const lastMessageA = a.messages[a.messages.length - 1];
const lastMessageB = b.messages[b.messages.length - 1];
const dateA = new Date(lastMessageA.timestamp);
const dateB = new Date(lastMessageB.timestamp);
return dateB - dateA;
});
setChats(sortedChats);
} catch (e) {
console.error(e);
}
}
}, [chatsData]);
const createChatHandler = async (alias) => {
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);
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;
}
try {
const data = await createChat({ id1: alias, id2: username }).unwrap(); // Using unwrap to handle promise rejection
localStorage.setItem("message", "Successfully opened chat!");
localStorage.setItem("interlocutorId", alias);
window.location.href = URLs.chat.url;
} catch (error) {
displayMessage(error.data.message, MessageType.ERROR);
}
};
useEffect(() => {retrieveChats().then()}, [])
return (
return (
<div className="homeWrapper">
<div className="headerPos">
<Header/>
</div>
<div className="headerPos">
<Header />
</div>
<HomeTitle/>
<HomeTitle />
<div className="search-input">
<InputField
title="Create new chat"
value={interlocutor}
setValue={setInterlocutor}
placeholder="Enter the username (id)"
/>
</div>
<div className="search-input">
<InputField
title="Create new chat"
value={interlocutor}
setValue={setInterlocutor}
placeholder="Enter the username (id)"
enter={createChatHandler}
submit={interlocutor}
/>
</div>
{isGetting ? (
<div>Loading...</div>
) : (
<>
<Search search={createChatHandler} item={interlocutor} />
<p>Your chats</p>
<ChatsList chats={chats} />
</>
)}
<Search search={createChat} item={interlocutor}/>
<p>Your chats</p>
<ChatsList chats={chats} />
</div>
)
}
);
};
export default Home
export default Home;

View File

@@ -6,6 +6,7 @@ import LoginTitle from "../components/reg/loginTitle.jsx";
import {MessageType} from "../backend/notifications/message.tsx";
import {displayMessage} from "../backend/notifications/notifications.js";
import {post} from "../backend/api.js";
import {URLs} from "../__data__/urls";
const SignIn = () => {
const [name, setName] = useState("");
@@ -37,7 +38,7 @@ const SignIn = () => {
setNameErrorsCounter(0);
localStorage.setItem('message', 'Successfully logged in!');
window.location.href = "/";
window.location.href = URLs.baseUrl;
}
return (

View File

@@ -5,6 +5,7 @@ import LoginTitle from "../components/reg/loginTitle.jsx";
import {post} from "../backend/api";
import {displayMessage} from "../backend/notifications/notifications";
import {MessageType} from "../backend/notifications/message";
import { URLs } from "../__data__/urls";
const SignUp = () => {
@@ -47,7 +48,7 @@ const SignUp = () => {
localStorage.setItem('username', name);
localStorage.setItem('message', 'Successfully signed up!');
window.location.href = "/";
window.location.href = URLs.baseUrl;
}
return (

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

@@ -3,8 +3,7 @@ const authRouter = require('express').Router();
// For creating tokens
const jwt = require('jsonwebtoken');
require('dotenv').config();
const TOKEN_KEY = process.env.TOKEN_KEY;
const { TOKEN_KEY } = require('../key')
module.exports = authRouter;
@@ -15,7 +14,6 @@ const { addUserToDB, getUserFromDB } = require('../db');
// Get a user by its id
authRouter.get('/:id', (req, res) => {
const user = getUserFromDB(req.params.id);
console.log("Request get in /auth:", req.params.id);
if (user) {
res.status(200).send({user});
@@ -27,7 +25,6 @@ authRouter.get('/:id', (req, res) => {
// For login (authorization)
authRouter.post('/login', (req, res) => {
const { name, password } = req.body;
console.log("Request login in /auth:", name);
const user = getUserFromDB(name);
@@ -54,7 +51,6 @@ authRouter.post('/login', (req, res) => {
authRouter.post('/reg', (req, res) => {
const { name, password, nickname } = req.body;
console.log("Request reg in /auth:", name);
const user = getUserFromDB(name);

View File

@@ -2,14 +2,11 @@ const changeRouter = require('express').Router();
module.exports = changeRouter;
const { users, getUserFromDB, deleteUserFromDB, addUserToDB } = require('../db');
const jwt = require("jsonwebtoken");
const { getUserFromDB, deleteUserFromDB, addUserToDB } = require('../db');
changeRouter.post('/nickname', (req, res) => {
const { id, newNickname } = req.body;
console.log("Request nickname in /change:", id);
const user = getUserFromDB(id);
@@ -36,7 +33,6 @@ changeRouter.post('/nickname', (req, res) => {
changeRouter.post('/password', (req, res) => {
const { id, newPassword } = req.body;
console.log("Request password in /change:", id);
const user = getUserFromDB(id);
@@ -62,7 +58,6 @@ changeRouter.post('/password', (req, res) => {
changeRouter.delete('/:id', (req, res) => {
const { id } = req.params;
console.log("Request delete in /change:", id);
deleteUserFromDB(id);
});

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"
}
]
}

View File

@@ -7,7 +7,6 @@ const { getChatFromDB, getUsersChats, addChatToDB, getUserFromDB,
chatRouter.get('/item/:id1/:id2', (req, res) => {
const { id1, id2 } = req.params;
console.log("Request get in /chat:", id1, id2);
if (id1 === id2) {
res.status(400).send({message: 'Ids should be different'});
@@ -25,7 +24,6 @@ chatRouter.get('/item/:id1/:id2', (req, res) => {
chatRouter.post('/item/:id1/:id2', (req, res) => {
const { id1, id2 } = req.params;
console.log("Request post in /chat:", id1, id2);
if (id1 === id2) {
res.status(400).send({message: 'Ids should be different'});
@@ -58,8 +56,6 @@ chatRouter.post('/item/:id1/:id2', (req, res) => {
chatRouter.get('/list/:id', (req, res) => {
const { id } = req.params;
console.log("Request get /list in /chat:", id);
const userChats = getUsersChats(id);
if (!userChats) {
@@ -72,7 +68,6 @@ chatRouter.get('/list/:id', (req, res) => {
chatRouter.post('/message/:sender/:receiver', (req, res) => {
const { sender, receiver } = req.params;
const { message } = req.body;
console.log("Request post /message in /chat:", sender, receiver, message);
const chat = getChatFromDB(sender, receiver);

View File

@@ -12,6 +12,6 @@ module.exports = router;
// router.use(delay(300));
// router.use('/books', delay, booksRouter);
router.use('/enterfront/auth', authRouter);
router.use('/enterfront/change', verify, changeRouter);
router.use('/enterfront/chat', verify, chatRouter)
router.use('/auth', authRouter);
router.use('/change', verify, changeRouter);
router.use('/chat', verify, chatRouter)

3
stubs/api/key.js Normal file
View File

@@ -0,0 +1,3 @@
const TOKEN_KEY = '5frv12e4few3r';
module.exports = { TOKEN_KEY }

View File

@@ -1,13 +1,12 @@
const jwt = require('jsonwebtoken');
require('dotenv').config();
const TOKEN_KEY = process.env.TOKEN_KEY;
const { TOKEN_KEY } = require('../key')
function verifyToken(req, res, next) {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) {
return res.status(403).send({ message: 'No token provided' });
return res.status(401).send({ message: 'No token provided' });
}
// Verify token

View File

@@ -13,7 +13,7 @@
"target": "es6",
"jsx": "react",
"typeRoots": ["node_modules/@types", "src/typings"],
"types" : ["webpack-env", "node"],
"types" : ["webpack-env", "node", "jest"],
"resolveJsonModule": true
},
"exclude": [