Skip to content

Commit

Permalink
add chat with websockets
Browse files Browse the repository at this point in the history
  • Loading branch information
nefelitav committed Feb 25, 2024
1 parent abd7f36 commit 6ef1610
Show file tree
Hide file tree
Showing 10 changed files with 443 additions and 13 deletions.
84 changes: 81 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.0",
"socket.io-client": "^4.7.4",
"tailwindcss": "^3.4.1"
},
"devDependencies": {
Expand Down
126 changes: 126 additions & 0 deletions src/components/Chat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@

import '../index.css'
import React, { useState, useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { UsersList, Profile } from './';
import {
ChatProps,
MessageData,
ChatBodyProps,
ChatHeaderProps,
} from "../types";

const Chat = ({ socket }: ChatProps) => {
const [showProfile, setShowProfile] = useState<boolean>(false);
const [messages, setMessages] = useState<MessageData[]>([]);
const lastMessageRef = useRef<HTMLDivElement>(null);

useEffect(() => {
socket.on('messageResponse', (data: MessageData) => setMessages([...messages, data]));
}, [socket, messages]);

useEffect(() => {
lastMessageRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);

return (
<div className="chat">
<UsersList setShowProfile={setShowProfile}/>
<div className="chat__main">
<ChatHeader setShowProfile={setShowProfile} />
{!showProfile && (
<>
<ChatBody messages={messages} lastMessageRef={lastMessageRef}/>
<ChatFooter socket={socket}/>
</>
)}
{showProfile && <Profile />}
</div>
</div>
);
};

const ChatHeader: React.FC<ChatHeaderProps> = ({ setShowProfile }) => {
const navigate = useNavigate();

const handleLeaveChat = () => {
localStorage.removeItem('usnermae');
navigate('/');
window.location.reload();
};

return (
<>
<header className="chat__mainHeader">
<p>Username</p>
<button style={{float:"right", marginRight:"10px"}} className="leaveChat__btn" onClick={() => setShowProfile(true)}>
Profile
</button>
<button style={{float:"right", marginRight:"10px"}} className="leaveChat__btn" onClick={handleLeaveChat}>
Logout
</button>
</header>

</>
);
};

const ChatBody: React.FC<ChatBodyProps> = ({ messages, lastMessageRef }) => {
return (
<>
<div className="message__container">
{messages.map((message: MessageData) =>
message.name === localStorage.getItem('username') ? (
<div className="message__chats" key={message.id}>
<p className="sender__name">You</p>
<div className="message__sender">
<p>{message.text}</p>
</div>
</div>
) : (
<div className="message__chats" key={message.id}>
<p>{message.name}</p>
<div className="message__recipient">
<p>{message.text}</p>
</div>
</div>
)
)}
<div ref={lastMessageRef} />
</div>
</>
);
};

const ChatFooter = ({ socket }: ChatProps) => {
const [message, setMessage] = useState<string>('');

const handleSendMessage = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (message.trim() && localStorage.getItem('username')) {
socket.emit('message', {
text: message,
name: localStorage.getItem('username'),
id: `${socket.id}${Math.random()}`,
socketID: socket.id,
});
}
setMessage('');
};
return (
<div className="chat__footer">
<form className="form" onSubmit={handleSendMessage}>
<input
type="text"
placeholder="Write message"
className="message"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<button className="sendBtn">SEND</button>
</form>
</div>
);
};

export default Chat;
2 changes: 1 addition & 1 deletion src/components/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ const Profile: React.FC = () => {
}
return (
<>
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<div className="justify-center px-6 py-12 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-sm">
<img
className="mx-auto h-10 w-auto"
Expand Down
45 changes: 45 additions & 0 deletions src/components/UsersList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

Check failure on line 2 in src/components/UsersList.tsx

View workflow job for this annotation

GitHub Actions / build

'useNavigate' is defined but never used
import {
ChatHeaderProps,
UserData,
} from "../types";

const UsersList: React.FC<ChatHeaderProps> = ({ setShowProfile }) => {
const [users, setUsers] = useState<UserData[]>([]);
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await fetch('users');
if (!response.ok) {
throw new Error('Failed to fetch users');
}
const data = await response.json();
setUsers(data.users);
} catch (error) {
console.error('Error fetching users:', error);
}
};

fetchUsers();
}, []);

return (
<div className="chat__sidebar">
<h2>CHAT APP</h2>
<div>
<h4 className="chat__header">Users</h4>
<div className="chat__users">
{users.map((user: UserData, index) => (
// go to this chat
<button key={index} onClick={() => setShowProfile(false)} style={{width:"100%", backgroundColor:"transparent", textAlign:"left", paddingLeft:"6px", height:"50pt", color:"grey", fontSize:"15px"}}><p style={{fontSize:"18px", color:"black"}}><b>{user.firstname} {user.lastname} ({user.username})</b></p> <i>Hey i missed you so much, how are you?...</i></button>
))}
<button onClick={() => setShowProfile(false)} style={{width:"100%", backgroundColor:"transparent", textAlign:"left", paddingLeft:"6px", height:"50pt", color:"grey", fontSize:"15px"}}><p style={{fontSize:"18px", color:"black"}}><b>Nefeli</b></p> <i>Hey, how are you?...</i></button>
<button onClick={() => setShowProfile(false)} style={{width:"100%", backgroundColor:"transparent", textAlign:"left", paddingLeft:"6px", height:"50pt", color:"grey", fontSize:"15px"}}><p style={{fontSize:"18px", color:"black"}}><b>Mariana</b></p> <i>Hey, how are you?...</i></button>
</div>
</div>
</div>
);
};

export default UsersList;
2 changes: 2 additions & 0 deletions src/components/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export { default as Login } from "./Login";
export { default as Register } from "./Register";
export { default as Profile } from "./Profile";
export { default as Chat } from "./Chat";
export { default as UsersList } from "./UsersList";
export * from "./UserProvider";
Loading

0 comments on commit 6ef1610

Please sign in to comment.