Spaces:
Runtime error
Runtime error
Commit
·
ee1fc35
1
Parent(s):
bf65a6c
made it working
Browse files- client/src/App.js +13 -13
- client/src/PrivateRoutes.jsx +5 -4
- client/src/UserProvider.jsx +1 -27
- client/src/components/Chat/Chat.jsx +35 -6
- client/src/components/ChatFooter/ChatFooter.css +1 -0
- client/src/components/ChatFooter/ChatFooter.jsx +11 -7
- client/src/components/ChatMain/ChatMain.jsx +24 -9
- client/src/index.css +1 -0
- client/src/index.js +2 -2
- server/index.ts +10 -9
client/src/App.js
CHANGED
|
@@ -12,23 +12,23 @@ function App() {
|
|
| 12 |
|
| 13 |
return (
|
| 14 |
<div className="App">
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
<Route element={<SignupPage />} path="/signup" />
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
-
|
| 23 |
-
<Route element={<PrivateRoutes />}>
|
| 24 |
-
<Route element={<ChatPage />} path="/" />
|
| 25 |
-
</Route>
|
| 26 |
-
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
|
|
|
|
| 32 |
|
| 33 |
|
| 34 |
</div>
|
|
|
|
| 12 |
|
| 13 |
return (
|
| 14 |
<div className="App">
|
| 15 |
+
<UserProvider>
|
| 16 |
+
<Router>
|
| 17 |
+
<Routes>
|
| 18 |
+
<Route element={<LoginPage />} path="/login" />
|
| 19 |
<Route element={<SignupPage />} path="/signup" />
|
| 20 |
+
{/* <Route element={<ChatPage />} path="/" /> */}
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
<Route element={<PrivateRoutes />}>
|
| 24 |
+
<Route element={<ChatPage />} path="/" />
|
| 25 |
+
</Route>
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
+
</Routes>
|
| 29 |
+
</Router>
|
|
|
|
| 30 |
|
| 31 |
+
</UserProvider>
|
| 32 |
|
| 33 |
|
| 34 |
</div>
|
client/src/PrivateRoutes.jsx
CHANGED
|
@@ -1,10 +1,11 @@
|
|
| 1 |
-
import React, { useState, useEffect } from 'react'
|
| 2 |
import { Outlet, Navigate } from "react-router-dom";
|
|
|
|
| 3 |
|
| 4 |
function PrivateRoutes() {
|
| 5 |
-
|
| 6 |
const [isAuth, setIsAuth] = useState(undefined);
|
| 7 |
-
|
| 8 |
|
| 9 |
|
| 10 |
const getAuthStatus = async () => {
|
|
@@ -13,6 +14,7 @@ function PrivateRoutes() {
|
|
| 13 |
credentials: "include"
|
| 14 |
})
|
| 15 |
|
|
|
|
| 16 |
return res.ok;
|
| 17 |
}
|
| 18 |
|
|
@@ -23,7 +25,6 @@ function PrivateRoutes() {
|
|
| 23 |
})
|
| 24 |
}, [])
|
| 25 |
|
| 26 |
-
|
| 27 |
if (isAuth === undefined) {
|
| 28 |
return <p>
|
| 29 |
Loading ...
|
|
|
|
| 1 |
+
import React, { useState, useEffect, useContext } from 'react'
|
| 2 |
import { Outlet, Navigate } from "react-router-dom";
|
| 3 |
+
import { UserContext } from './UserProvider';
|
| 4 |
|
| 5 |
function PrivateRoutes() {
|
| 6 |
+
|
| 7 |
const [isAuth, setIsAuth] = useState(undefined);
|
| 8 |
+
const { user, setUser } = useContext(UserContext);
|
| 9 |
|
| 10 |
|
| 11 |
const getAuthStatus = async () => {
|
|
|
|
| 14 |
credentials: "include"
|
| 15 |
})
|
| 16 |
|
| 17 |
+
if (res.ok) { setUser(await res.json()) };
|
| 18 |
return res.ok;
|
| 19 |
}
|
| 20 |
|
|
|
|
| 25 |
})
|
| 26 |
}, [])
|
| 27 |
|
|
|
|
| 28 |
if (isAuth === undefined) {
|
| 29 |
return <p>
|
| 30 |
Loading ...
|
client/src/UserProvider.jsx
CHANGED
|
@@ -5,34 +5,8 @@ export const UserContext = createContext();
|
|
| 5 |
export const UserProvider = ({ children }) => {
|
| 6 |
const [user, setUser] = useState(null);
|
| 7 |
|
| 8 |
-
|
| 9 |
-
const getUser = async () => {
|
| 10 |
-
|
| 11 |
-
const res = await fetch(process.env.REACT_APP_BACKEND_URL + "/session", {
|
| 12 |
-
credentials: "include"
|
| 13 |
-
})
|
| 14 |
-
var data;
|
| 15 |
-
if(res.ok){
|
| 16 |
-
data = await res.json();
|
| 17 |
-
setUser(data);
|
| 18 |
-
}
|
| 19 |
-
else{
|
| 20 |
-
setUser({});
|
| 21 |
-
}
|
| 22 |
-
console.log("user",data);
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
}
|
| 26 |
-
|
| 27 |
-
useEffect(()=>{
|
| 28 |
-
getUser();
|
| 29 |
-
},[])
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
return (
|
| 35 |
-
<UserContext.Provider value={user}>
|
| 36 |
{children}
|
| 37 |
</UserContext.Provider>
|
| 38 |
);
|
|
|
|
| 5 |
export const UserProvider = ({ children }) => {
|
| 6 |
const [user, setUser] = useState(null);
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
return (
|
| 9 |
+
<UserContext.Provider value={{user, setUser}}>
|
| 10 |
{children}
|
| 11 |
</UserContext.Provider>
|
| 12 |
);
|
client/src/components/Chat/Chat.jsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import React, { useContext, useEffect, useState } from 'react';
|
| 2 |
import "./Chat.css";
|
| 3 |
import ChatHeader from '../ChatHeader';
|
| 4 |
import ChatMain from '../ChatMain';
|
|
@@ -10,25 +10,52 @@ function Chat({
|
|
| 10 |
}) {
|
| 11 |
const [messages, setMessages] = useState([]);
|
| 12 |
const socket = useContext(SocketContext);
|
|
|
|
| 13 |
|
| 14 |
const getMessages = async () => {
|
| 15 |
const res = await fetch(process.env.REACT_APP_BACKEND_URL + `/messages/${currentContact.id}`, { credentials: "include" });
|
| 16 |
const data = await res.json();
|
| 17 |
if (res.ok) {
|
| 18 |
-
console.log(data.messages);
|
| 19 |
setMessages(data.messages);
|
| 20 |
}
|
| 21 |
}
|
| 22 |
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
-
if (Object.keys(currentContact).length === 0) return;
|
| 26 |
|
|
|
|
|
|
|
|
|
|
| 27 |
getMessages();
|
|
|
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
-
}, [currentContact])
|
| 31 |
|
|
|
|
|
|
|
| 32 |
if (Object.keys(currentContact).length === 0) {
|
| 33 |
|
| 34 |
return (
|
|
@@ -37,10 +64,12 @@ function Chat({
|
|
| 37 |
</div>
|
| 38 |
)
|
| 39 |
}
|
|
|
|
|
|
|
| 40 |
return (
|
| 41 |
<div className='chat'>
|
| 42 |
<ChatHeader name={currentContact.username} />
|
| 43 |
-
<ChatMain messages={messages} />
|
| 44 |
<ChatFooter currentContactId={currentContact.id} setMessages={setMessages} />
|
| 45 |
</div>
|
| 46 |
)
|
|
|
|
| 1 |
+
import React, { useContext, useEffect, useState, useRef } from 'react';
|
| 2 |
import "./Chat.css";
|
| 3 |
import ChatHeader from '../ChatHeader';
|
| 4 |
import ChatMain from '../ChatMain';
|
|
|
|
| 10 |
}) {
|
| 11 |
const [messages, setMessages] = useState([]);
|
| 12 |
const socket = useContext(SocketContext);
|
| 13 |
+
const chatMainRef = useRef();
|
| 14 |
|
| 15 |
const getMessages = async () => {
|
| 16 |
const res = await fetch(process.env.REACT_APP_BACKEND_URL + `/messages/${currentContact.id}`, { credentials: "include" });
|
| 17 |
const data = await res.json();
|
| 18 |
if (res.ok) {
|
| 19 |
+
// console.log(data.messages);
|
| 20 |
setMessages(data.messages);
|
| 21 |
}
|
| 22 |
}
|
| 23 |
|
| 24 |
+
const scrollToBottom = () => {
|
| 25 |
+
if(chatMainRef.current){
|
| 26 |
+
chatMainRef.current.scrollTo(0, chatMainRef.current.scrollHeight);
|
| 27 |
+
}
|
| 28 |
+
};
|
| 29 |
|
|
|
|
| 30 |
|
| 31 |
+
// to get Messages of currently opened chat
|
| 32 |
+
useEffect(() => {
|
| 33 |
+
if (Object.keys(currentContact).length === 0) return;
|
| 34 |
getMessages();
|
| 35 |
+
}, [currentContact])
|
| 36 |
|
| 37 |
+
// to add receive_message listener
|
| 38 |
+
useEffect(() => {
|
| 39 |
+
if (socket) {
|
| 40 |
+
socket.on("receive_message", (message) => {
|
| 41 |
+
// console.log(message);
|
| 42 |
+
setMessages((prev) => [...prev, message]);
|
| 43 |
+
});
|
| 44 |
+
|
| 45 |
+
return () => {
|
| 46 |
+
socket.off("receive-message");
|
| 47 |
+
};
|
| 48 |
+
}
|
| 49 |
+
}, [socket]);
|
| 50 |
+
|
| 51 |
+
// when ever a chat's messages are updated, scrollToBottom
|
| 52 |
+
useEffect(() => {
|
| 53 |
+
scrollToBottom();
|
| 54 |
+
}, [messages]);
|
| 55 |
|
|
|
|
| 56 |
|
| 57 |
+
|
| 58 |
+
// Until no chat is selected
|
| 59 |
if (Object.keys(currentContact).length === 0) {
|
| 60 |
|
| 61 |
return (
|
|
|
|
| 64 |
</div>
|
| 65 |
)
|
| 66 |
}
|
| 67 |
+
|
| 68 |
+
// once a chat is selected
|
| 69 |
return (
|
| 70 |
<div className='chat'>
|
| 71 |
<ChatHeader name={currentContact.username} />
|
| 72 |
+
<ChatMain messages={messages} ref={chatMainRef} />
|
| 73 |
<ChatFooter currentContactId={currentContact.id} setMessages={setMessages} />
|
| 74 |
</div>
|
| 75 |
)
|
client/src/components/ChatFooter/ChatFooter.css
CHANGED
|
@@ -24,6 +24,7 @@
|
|
| 24 |
|
| 25 |
.text_box{
|
| 26 |
height:100%;
|
|
|
|
| 27 |
flex-grow:1;
|
| 28 |
border:1px solid var(--BORDER-COLOR);
|
| 29 |
border-radius: 5px;
|
|
|
|
| 24 |
|
| 25 |
.text_box{
|
| 26 |
height:100%;
|
| 27 |
+
min-width:20px;
|
| 28 |
flex-grow:1;
|
| 29 |
border:1px solid var(--BORDER-COLOR);
|
| 30 |
border-radius: 5px;
|
client/src/components/ChatFooter/ChatFooter.jsx
CHANGED
|
@@ -11,29 +11,33 @@ function ChatFooter({
|
|
| 11 |
const socket = useContext(SocketContext);
|
| 12 |
const messageRef = useRef();
|
| 13 |
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
|
|
|
| 18 |
}
|
| 19 |
-
}
|
| 20 |
|
| 21 |
const sendMessage = (message) => {
|
| 22 |
|
|
|
|
|
|
|
| 23 |
const curr_time = Math.floor(new Date().getTime() / 1000);
|
| 24 |
const message_bundle= {
|
| 25 |
message,
|
| 26 |
send_at:curr_time,
|
| 27 |
-
|
| 28 |
}
|
| 29 |
|
| 30 |
setMessages((prev) => [...prev, message_bundle]);
|
| 31 |
|
| 32 |
if(socket){
|
| 33 |
-
|
| 34 |
socket.emit("send_message",message_bundle);
|
| 35 |
}
|
| 36 |
|
|
|
|
|
|
|
| 37 |
}
|
| 38 |
|
| 39 |
const handleEnter = (e) => {
|
|
|
|
| 11 |
const socket = useContext(SocketContext);
|
| 12 |
const messageRef = useRef();
|
| 13 |
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
useEffect(()=>{
|
| 17 |
+
if(messageRef.current){
|
| 18 |
+
messageRef.current.focus();
|
| 19 |
}
|
| 20 |
+
}) // risky this running all the time
|
| 21 |
|
| 22 |
const sendMessage = (message) => {
|
| 23 |
|
| 24 |
+
if(message.trim()==="") return;
|
| 25 |
+
|
| 26 |
const curr_time = Math.floor(new Date().getTime() / 1000);
|
| 27 |
const message_bundle= {
|
| 28 |
message,
|
| 29 |
send_at:curr_time,
|
| 30 |
+
send_to:currentContactId,
|
| 31 |
}
|
| 32 |
|
| 33 |
setMessages((prev) => [...prev, message_bundle]);
|
| 34 |
|
| 35 |
if(socket){
|
|
|
|
| 36 |
socket.emit("send_message",message_bundle);
|
| 37 |
}
|
| 38 |
|
| 39 |
+
messageRef.current.value = "";
|
| 40 |
+
|
| 41 |
}
|
| 42 |
|
| 43 |
const handleEnter = (e) => {
|
client/src/components/ChatMain/ChatMain.jsx
CHANGED
|
@@ -1,27 +1,42 @@
|
|
| 1 |
-
import React from 'react'
|
| 2 |
import Message from '../Message';
|
| 3 |
import "./ChatMain.css";
|
|
|
|
| 4 |
|
| 5 |
-
|
| 6 |
messages,
|
| 7 |
-
}) {
|
| 8 |
|
| 9 |
-
const
|
| 10 |
-
|
|
|
|
|
|
|
| 11 |
return d.toLocaleTimeString();
|
| 12 |
// return timestamp;
|
| 13 |
}
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
return (
|
| 16 |
-
<div className='chat_main'>
|
| 17 |
{
|
| 18 |
|
| 19 |
-
messages.map((message,idx) => (
|
| 20 |
-
<Message
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
))
|
| 22 |
}
|
| 23 |
</div>
|
| 24 |
)
|
| 25 |
-
}
|
| 26 |
|
| 27 |
export default ChatMain
|
|
|
|
| 1 |
+
import React, { useContext } from 'react'
|
| 2 |
import Message from '../Message';
|
| 3 |
import "./ChatMain.css";
|
| 4 |
+
import { UserContext } from '../../UserProvider';
|
| 5 |
|
| 6 |
+
const ChatMain = React.forwardRef(({
|
| 7 |
messages,
|
| 8 |
+
},ref) => {
|
| 9 |
|
| 10 |
+
const { user } = useContext(UserContext);
|
| 11 |
+
|
| 12 |
+
const timeToReadable = (timestamp) => {
|
| 13 |
+
var d = new Date(timestamp * 1000);
|
| 14 |
return d.toLocaleTimeString();
|
| 15 |
// return timestamp;
|
| 16 |
}
|
| 17 |
|
| 18 |
+
const isReceived = (message) => {
|
| 19 |
+
// console.log("message",message.send_to);
|
| 20 |
+
// console.log("user",user.id);
|
| 21 |
+
// && user.id==message.from
|
| 22 |
+
return user && message.send_to == user.id;
|
| 23 |
+
};
|
| 24 |
+
|
| 25 |
return (
|
| 26 |
+
<div className='chat_main' ref={ref}>
|
| 27 |
{
|
| 28 |
|
| 29 |
+
messages.map((message, idx) => (
|
| 30 |
+
<Message
|
| 31 |
+
key={idx}
|
| 32 |
+
message={message.message}
|
| 33 |
+
time={timeToReadable(message.send_at)}
|
| 34 |
+
isReceived={isReceived(message)}
|
| 35 |
+
/>
|
| 36 |
))
|
| 37 |
}
|
| 38 |
</div>
|
| 39 |
)
|
| 40 |
+
})
|
| 41 |
|
| 42 |
export default ChatMain
|
client/src/index.css
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
margin:0;
|
| 3 |
padding:0;
|
| 4 |
box-sizing: border-box;
|
|
|
|
| 5 |
}
|
| 6 |
|
| 7 |
body {
|
|
|
|
| 2 |
margin:0;
|
| 3 |
padding:0;
|
| 4 |
box-sizing: border-box;
|
| 5 |
+
scroll-behavior: smooth;
|
| 6 |
}
|
| 7 |
|
| 8 |
body {
|
client/src/index.js
CHANGED
|
@@ -6,9 +6,9 @@ import reportWebVitals from './reportWebVitals';
|
|
| 6 |
|
| 7 |
const root = ReactDOM.createRoot(document.getElementById('root'));
|
| 8 |
root.render(
|
| 9 |
-
<React.StrictMode>
|
| 10 |
<App />
|
| 11 |
-
|
|
|
|
| 12 |
);
|
| 13 |
|
| 14 |
// If you want to start measuring performance in your app, pass a function
|
|
|
|
| 6 |
|
| 7 |
const root = ReactDOM.createRoot(document.getElementById('root'));
|
| 8 |
root.render(
|
|
|
|
| 9 |
<App />
|
| 10 |
+
// <React.StrictMode>
|
| 11 |
+
// </React.StrictMode>
|
| 12 |
);
|
| 13 |
|
| 14 |
// If you want to start measuring performance in your app, pass a function
|
server/index.ts
CHANGED
|
@@ -43,11 +43,6 @@ app.use(mutler().array(""));
|
|
| 43 |
// The entire build/web directory is statically served.
|
| 44 |
app.use(express.static(path.join(__dirname, "../client/build")));
|
| 45 |
|
| 46 |
-
// Catch all. If we want to add pages later, then we should probably change this.
|
| 47 |
-
app.get("/", (_req, res) => {
|
| 48 |
-
res.sendFile(path.join(__dirname + "/../client/build/index.html"));
|
| 49 |
-
});
|
| 50 |
-
|
| 51 |
// middleware
|
| 52 |
// app.use((req,res,next)=>{
|
| 53 |
// if(req.method==="GET"){
|
|
@@ -146,7 +141,7 @@ app.post("/signup", async (req, res) => {
|
|
| 146 |
})
|
| 147 |
}
|
| 148 |
|
| 149 |
-
// save to
|
| 150 |
const userId = generateId(15);
|
| 151 |
const hashedPassword = await bcrypt.hash(data.password, 10);
|
| 152 |
|
|
@@ -240,6 +235,11 @@ app.get("/messages/:userid", isAuthAPIMiddleware, async (req, res) => {
|
|
| 240 |
res.json({ "messages": messages });
|
| 241 |
})
|
| 242 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
|
| 244 |
const io = new Server(server, {
|
| 245 |
cors: {
|
|
@@ -297,7 +297,7 @@ io.on("connection", async (socket) => {
|
|
| 297 |
await stmt.run(
|
| 298 |
msg_id,
|
| 299 |
data.message,
|
| 300 |
-
data.
|
| 301 |
socket.data.user.id,
|
| 302 |
data.send_at,
|
| 303 |
);
|
|
@@ -308,9 +308,10 @@ io.on("connection", async (socket) => {
|
|
| 308 |
|
| 309 |
|
| 310 |
sockets.forEach((client)=>{
|
| 311 |
-
if(client.data.user.id==data.
|
| 312 |
{
|
| 313 |
-
|
|
|
|
| 314 |
...data,
|
| 315 |
"from":socket.data.user.id,
|
| 316 |
});
|
|
|
|
| 43 |
// The entire build/web directory is statically served.
|
| 44 |
app.use(express.static(path.join(__dirname, "../client/build")));
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
// middleware
|
| 47 |
// app.use((req,res,next)=>{
|
| 48 |
// if(req.method==="GET"){
|
|
|
|
| 141 |
})
|
| 142 |
}
|
| 143 |
|
| 144 |
+
// save to data se
|
| 145 |
const userId = generateId(15);
|
| 146 |
const hashedPassword = await bcrypt.hash(data.password, 10);
|
| 147 |
|
|
|
|
| 235 |
res.json({ "messages": messages });
|
| 236 |
})
|
| 237 |
|
| 238 |
+
// Catch all. If we want to add pages later, then we should probably change this.
|
| 239 |
+
app.get("*", (_req, res) => {
|
| 240 |
+
res.sendFile(path.join(__dirname + "/../client/build/index.html"));
|
| 241 |
+
});
|
| 242 |
+
|
| 243 |
|
| 244 |
const io = new Server(server, {
|
| 245 |
cors: {
|
|
|
|
| 297 |
await stmt.run(
|
| 298 |
msg_id,
|
| 299 |
data.message,
|
| 300 |
+
data.send_to,
|
| 301 |
socket.data.user.id,
|
| 302 |
data.send_at,
|
| 303 |
);
|
|
|
|
| 308 |
|
| 309 |
|
| 310 |
sockets.forEach((client)=>{
|
| 311 |
+
if(client.data.user.id==data.send_to)
|
| 312 |
{
|
| 313 |
+
// console.log("heyy");
|
| 314 |
+
return socket.to(client.id).emit("receive_message",{
|
| 315 |
...data,
|
| 316 |
"from":socket.data.user.id,
|
| 317 |
});
|