Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/LWF-17_messages #70

Merged
merged 31 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7604d7a
build: add new packages
JeriRov May 19, 2024
fcba00b
build: remove no-undef from typescript
JeriRov May 19, 2024
2df31d5
feat: add new icon
JeriRov May 19, 2024
5a3b1aa
refactor: rename system alert message helper
JeriRov May 19, 2024
af4cdbc
feat: add new socket types
JeriRov May 19, 2024
cc53913
feat: update api
JeriRov May 19, 2024
df6da4c
refactor: auth utils
JeriRov May 19, 2024
aace39a
build: new theme toggle
JeriRov May 23, 2024
107af90
chore: add new icons
JeriRov May 23, 2024
9085dd4
feat: add useLayout auth hook
JeriRov May 23, 2024
88fb090
feat: update format date helper
JeriRov May 23, 2024
f7d80ac
feat: is token expired helper
JeriRov May 23, 2024
1434707
feat: separate token required endpoints and not
JeriRov May 23, 2024
bd70007
feat: add socket handlers
JeriRov May 23, 2024
f9835f4
feat: add message sound
JeriRov May 23, 2024
7e763e8
refactor: update user slice types
JeriRov May 23, 2024
6331244
feat: update colors
JeriRov May 23, 2024
e64b451
feat: update theme toggle
JeriRov May 23, 2024
ca141c2
feat: update avatar component
JeriRov May 23, 2024
29b32c4
feat: update status component
JeriRov May 23, 2024
b2d42c3
feat: update scroll list
JeriRov May 23, 2024
9e234e2
feat: create modal component
JeriRov May 23, 2024
799f9be
feat: update home container
JeriRov May 23, 2024
d0a24a8
feat: update double check icon component
JeriRov May 23, 2024
492374b
feat: add chat socket handlers
JeriRov May 23, 2024
1b991d4
feat: update interactive list
JeriRov May 23, 2024
53a3af2
feat: update messages and chat components
JeriRov May 23, 2024
063f194
feat: makes types for handlers
JeriRov May 23, 2024
0d6449d
feat: update user items
JeriRov May 23, 2024
ccb898d
feat: update new features
JeriRov May 23, 2024
8a04907
feat: add sound provider
JeriRov May 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading