Skip to content

Commit

Permalink
Merge pull request #70 from L1nkWave/feature/LWF-17_messages
Browse files Browse the repository at this point in the history
feature/LWF-17_messages
  • Loading branch information
JeriRov committed May 23, 2024
2 parents 5954c89 + 8a04907 commit 9410bae
Show file tree
Hide file tree
Showing 59 changed files with 10,226 additions and 3,348 deletions.
8 changes: 8 additions & 0 deletions frontend/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,12 @@ module.exports = {
"import/prefer-default-export": "off",
"import/no-cycle": "error",
},
overrides: [
{
files: ["*.ts", "*.tsx"],
rules: {
"no-undef": "off",
},
},
],
};
11,928 changes: 8,995 additions & 2,933 deletions frontend/package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"dependencies": {
"@reduxjs/toolkit": "^2.0.1",
"@theme-toggles/react": "^4.1.0",
"@types/uuid": "^9.0.8",
"axios": "^1.6.5",
"date-fns": "^3.5.0",
"formik": "^2.4.5",
Expand All @@ -23,6 +23,8 @@
"react-dom": "^18.2.0",
"react-redux": "^9.1.0",
"react-toastify": "^10.0.4",
"react-toggle-dark-mode": "^1.1.1",
"uuid": "^9.0.1",
"yup": "^1.3.3"
},
"devDependencies": {
Expand Down
3 changes: 3 additions & 0 deletions frontend/public/icons/angle-down.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions frontend/public/icons/broken-link-outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions frontend/public/icons/clock-outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/public/icons/close-outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions frontend/public/icons/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
export { default as AddChatOutlineIcon } from "./add-chat-outline.svg";
export { default as AddCircleOutlineIcon } from "./add-circle-outline.svg";
export { default as AngleDownIcon } from "./angle-down.svg";
export { default as BrokenLinkOutlineIcon } from "./broken-link-outline.svg";
export { default as ChatPlusOutlineIcon } from "./chat-plus-outline.svg";
export { default as CheckIcon } from "./check.svg";
export { default as CloseOutlineIcon } from "./close-outline.svg";
export { default as CutCheckIcon } from "./cut-check.svg";
export { default as FindContactsOutlineIcon } from "./find-people-outline.svg";
export { default as FolderOutlineIcon } from "./folder-outline.svg";
export { default as FormOutlineIcon } from "./form-outline.svg";
export { default as GroupOutlineIcon } from "./group-outline.svg";
export { default as LeftAngleIcon } from "./left-angle.svg";
export { default as LineHorizontalIcon } from "./line-horizontal.svg";
export { default as LinkOutlineIcon } from "./link-outline.svg";
export { default as ListOutlineIcon } from "./list-outline.svg";
export { default as LockOutlineIcon } from "./lock-outline.svg";
Expand Down
3 changes: 3 additions & 0 deletions frontend/public/icons/line-horizontal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/sound/message.mp3
Binary file not shown.
22 changes: 13 additions & 9 deletions frontend/src/api/http/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { instance } from "@/api/http";
import axios from "axios";

import { AuthTypes } from "@/api/http/auth/auth.types";

export const authInstance = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
timeout: 10000,
headers: {
"Content-Type": "application/json",
},
});

export async function signUp(name: string, username: string, password: string) {
const body = {
name,
username,
password,
};

const { data } = await instance.post("users/register", body);
const { data } = await authInstance.post("users/register", body);
return data;
}

Expand All @@ -18,16 +27,11 @@ export async function signIn(username: string, password: string) {
password,
};

const { data } = await instance.post<AuthTypes>("auth/login", body, { withCredentials: true });
const { data } = await authInstance.post<AuthTypes>("auth/login", body, { withCredentials: true });
return data;
}

export async function refreshToken() {
const { data } = await instance.post<AuthTypes>("auth/refresh-tokens", {}, { withCredentials: true });
return data;
}

export async function logout() {
const { data } = await instance.post<AuthTypes>("auth/logout", {}, { withCredentials: true });
const { data } = await authInstance.post<AuthTypes>("auth/refresh-tokens", {}, { withCredentials: true });
return data;
}
20 changes: 18 additions & 2 deletions frontend/src/api/http/chat/chat.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { instance } from "@/api/http";
import { ChatParams } from "@/api/http/contacts/contacts.types";
import { ChatParams, MessageParams } from "@/api/http/contacts/contacts.types";

export type AddDuoChatParams = {
id: string;
createdAt: number;
};

export async function addDuoChat(userId: string) {
const body = {
recipient: userId,
};
const { data } = await instance.post(`chats`, body);
const { data } = await instance.post<AddDuoChatParams>(`chats`, body);
console.log("add", data);
return data;
}

Expand All @@ -20,5 +26,15 @@ export async function getChats(offset: number = 0, limit: number = 10) {
data.forEach(chat => {
chats.set(chat.id, chat);
});
console.log(chats);
return chats;
}

export async function getMessagesByChatId(chatId: string, limit: number = 40, offset: number = 0) {
const { data } = await instance.get<MessageParams[]>(`chats/${chatId}/messages?limit=${limit}&offset=${offset}`);
const messages = new Map<string, MessageParams>();
data.forEach(chat => {
messages.set(chat.id, chat);
});
return messages;
}
7 changes: 1 addition & 6 deletions frontend/src/api/http/contacts/contacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { instance } from "@/api/http";
import { ContactParams, UserParams } from "@/api/http/contacts/contacts.types";

export async function getContacts() {
const { data } = await instance.get<ContactParams[]>("users/contacts?username=&limit=10&offset=0");
const { data } = await instance.get<ContactParams[]>("users/contacts?search=&limit=10&offset=0");
const contacts = new Map<number, ContactParams>();
data.forEach(contact => {
contacts.set(contact.user.id, contact);
Expand All @@ -11,11 +11,6 @@ export async function getContacts() {
return contacts;
}

export async function getContactById(contactId: string) {
const { data } = await instance.get<ContactParams>(`users/contacts/${contactId}`);
return data;
}

export async function addContact(userId: string, alias: string) {
const body = {
userId,
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/api/http/contacts/contacts.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type UserParams = {
username: string;
createdAt: string;
avatarPath?: string;
deleted?: boolean;
};

export type ContactParams = {
Expand All @@ -18,17 +19,21 @@ export type ContactParams = {
export type MessageParams = {
action: string;
author: UserParams;
createdAt: string;
createdAt: number;
edited: boolean;
id: string;
isRead: boolean;
reactions: string[];
text: string;
sending?: boolean;
};

export type ChatParams = {
createdAt: string;
id: string;
unreadMessages: number;
type: string;
createdAt: number;
lastMessage: MessageParams;
type: number;
user: UserParams;
avatarAvailable: boolean;
};
20 changes: 16 additions & 4 deletions frontend/src/api/http/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import axios, { AxiosInstance } from "axios";

import { refreshToken } from "@/api/http/auth/auth";
import { AuthTypes } from "@/api/http/auth/auth.types";
import { isTokenExpired } from "@/helpers/DecodeToken/decodeToken";
import { store } from "@/lib/store";

const baseURL = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8080/";
Expand All @@ -12,15 +15,24 @@ export const instance: AxiosInstance = axios.create({
},
});

// Request interceptor for authInstance
instance.interceptors.request.use(
config => {
async config => {
const newConfig = config;

newConfig.headers.Authorization = `Bearer ${store.getState().user.accessToken}`;
const { accessToken } = store.getState().user;
let validToken = accessToken;
if (!accessToken || (accessToken && isTokenExpired(accessToken))) {
const newToken = await refreshToken();
validToken = newToken.accessToken;
}
newConfig.headers.Authorization = `Bearer ${validToken}`;
return newConfig;
},
() => {
return Promise.reject(new Error("Network timeout"));
}
);

export async function logout() {
const { data } = await instance.post<AuthTypes>("auth/logout", {}, { withCredentials: true });
return data;
}
26 changes: 21 additions & 5 deletions frontend/src/api/socket/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { ChatType } from "@/api/socket/index.types";

const socketUrl = process.env.NEXT_PUBLIC_WEB_SOCKET_URL;

export const connectToSocket = (token: string) => {
Expand All @@ -10,10 +8,28 @@ export const connectToSocket = (token: string) => {
return socket;
};

export const sendChatMessage = (socket: WebSocket, message: string, chatType: ChatType, chatId: number | string) => {
socket.send(`path=/chat/${chatId}/send
export const sendChatMessage = (socket: WebSocket, chatId: string, message: string, tempId: string) => {
const path = `path=/chat/${chatId}/send`;
const payload = JSON.stringify({
tmpMessageId: tempId,
text: message,
});

socket.send(`${path}
${payload}`);
};

export const checkUnreadMessages = (socket: WebSocket) => {
const path = `path=/chat/unread_messages`;
socket.send(path);
};

export const readMessages = (socket: WebSocket, chatId: string, timestamp: number) => {
const path = `path=/chat/${chatId}/read`;
const payload = JSON.stringify({ timestamp });
socket.send(`${path}
${message}
${payload}
`);
};
11 changes: 7 additions & 4 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import localFont from "next/font/local";
import React from "react";

import { ToastManager } from "@/components/ToastManager/ToastManager";
import { SocketProvider } from "@/context/SoundContext/SoundProvider";
import StoreProvider from "@/context/StoreProvider/StoreProvider";
import { ThemeProvider } from "@/context/ThemeProvider/ThemeProvider";

Expand Down Expand Up @@ -47,10 +48,12 @@ export default function RootLayout({
<html lang="en" className="no-scrollbar" suppressHydrationWarning>
<body className={`${ggSans.className} bg-white h-screen dark:bg-dark-400 dark:text-white`}>
<StoreProvider>
<ThemeProvider>
<ToastManager />
{children}
</ThemeProvider>
<SocketProvider>
<ThemeProvider>
<ToastManager />
{children}
</ThemeProvider>
</SocketProvider>
</StoreProvider>
</body>
</html>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Auth/auth.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FormikValues } from "formik";
import { toast } from "react-toastify";

import { messages, usernameInput } from "@/components/Auth/auth.config";
import { formatMessage } from "@/helpers/formatMessage";
import { formatSystemAlertMessage } from "@/helpers/formatSystemAlertMessage";

export const handleUsernameBlur = (formik: FormikValues) => {
if (!formik.values.username.startsWith("@")) {
Expand All @@ -14,7 +14,7 @@ export const handleUsernameBlur = (formik: FormikValues) => {

export const axiosErrorHandler = (error: unknown) => {
if (error instanceof AxiosError) {
toast.error(formatMessage(error.response?.data.message) ?? messages.DEFAULT_ERROR_MESSAGE);
toast.error(formatSystemAlertMessage(error.response?.data.message) ?? messages.DEFAULT_ERROR_MESSAGE);
} else {
toast.error(messages.DEFAULT_ERROR_MESSAGE);
}
Expand Down
11 changes: 6 additions & 5 deletions frontend/src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { AvatarProps } from "@/components/Avatar/avatar.types";
import { Status } from "@/components/Status/Status";
import { defaultUserAvatar } from "@/helpers/defaultUserAvatar";

export function Avatar({ item, className, online, status = true, ...props }: AvatarProps) {
export function Avatar({ item, statusClassName, className, online, status = true, ...props }: AvatarProps) {
return (
<span>
<span className="relative flex h-full">
<Image
className={`object-cover rounded-full ${className}`}
width={64}
Expand All @@ -15,9 +15,10 @@ export function Avatar({ item, className, online, status = true, ...props }: Ava
{...props}
/>
{status && online && (
<div className="flex justify-end items-end">
<Status className="absolute" online={online} />
</div>
<Status
className={`absolute bottom-1.5 left-12 transform translate-x-1/2 translate-y-1/2 ${statusClassName ?? statusClassName}`}
online={online}
/>
)}
</span>
);
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/Avatar/avatar.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export type AvatarProps = {
item: ItemParams;
status?: boolean;
online?: boolean;
statusClassName?: string;
} & Omit<ImageProps, "src">;
Loading

0 comments on commit 9410bae

Please sign in to comment.