Spaces:
Runtime error
Runtime error
Commit
·
bf65a6c
1
Parent(s):
d1f9048
worked on making messages work
Browse files- client/src/App.js +5 -0
- client/src/PrivateRoutes.jsx +10 -6
- client/src/UserProvider.jsx +39 -0
- client/src/components/Chat/Chat.jsx +24 -3
- client/src/components/ChatFooter/ChatFooter.jsx +37 -7
- client/src/components/ChatMain/ChatMain.jsx +12 -12
- server/index.ts +52 -0
client/src/App.js
CHANGED
|
@@ -4,6 +4,7 @@ import ChatPage from './ChatPage';
|
|
| 4 |
import LoginPage from './LoginPage';
|
| 5 |
import SignupPage from './SignupPage';
|
| 6 |
import PrivateRoutes from './PrivateRoutes';
|
|
|
|
| 7 |
|
| 8 |
function App() {
|
| 9 |
|
|
@@ -11,18 +12,22 @@ function App() {
|
|
| 11 |
|
| 12 |
return (
|
| 13 |
<div className="App">
|
|
|
|
| 14 |
<Router>
|
| 15 |
<Routes>
|
| 16 |
<Route element={<LoginPage />} path="/login" />
|
| 17 |
<Route element={<SignupPage />} path="/signup" />
|
| 18 |
{/* <Route element={<ChatPage />} path="/" /> */}
|
| 19 |
|
|
|
|
| 20 |
<Route element={<PrivateRoutes />}>
|
| 21 |
<Route element={<ChatPage />} path="/" />
|
| 22 |
</Route>
|
|
|
|
| 23 |
|
| 24 |
</Routes>
|
| 25 |
</Router>
|
|
|
|
| 26 |
|
| 27 |
|
| 28 |
|
|
|
|
| 4 |
import LoginPage from './LoginPage';
|
| 5 |
import SignupPage from './SignupPage';
|
| 6 |
import PrivateRoutes from './PrivateRoutes';
|
| 7 |
+
import { UserProvider } from './UserProvider';
|
| 8 |
|
| 9 |
function App() {
|
| 10 |
|
|
|
|
| 12 |
|
| 13 |
return (
|
| 14 |
<div className="App">
|
| 15 |
+
|
| 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 |
|
| 32 |
|
| 33 |
|
client/src/PrivateRoutes.jsx
CHANGED
|
@@ -1,8 +1,12 @@
|
|
| 1 |
-
import React, { useState,useEffect } from 'react'
|
| 2 |
import { Outlet, Navigate } from "react-router-dom";
|
| 3 |
|
| 4 |
function PrivateRoutes() {
|
|
|
|
| 5 |
const [isAuth, setIsAuth] = useState(undefined);
|
|
|
|
|
|
|
|
|
|
| 6 |
const getAuthStatus = async () => {
|
| 7 |
|
| 8 |
const res = await fetch(process.env.REACT_APP_BACKEND_URL + "/session", {
|
|
@@ -12,12 +16,12 @@ function PrivateRoutes() {
|
|
| 12 |
return res.ok;
|
| 13 |
}
|
| 14 |
|
| 15 |
-
useEffect(()=>{
|
| 16 |
-
getAuthStatus().then((status)=>{
|
| 17 |
console.log(status);
|
| 18 |
setIsAuth(status);
|
| 19 |
})
|
| 20 |
-
},[])
|
| 21 |
|
| 22 |
|
| 23 |
if (isAuth === undefined) {
|
|
@@ -25,12 +29,12 @@ function PrivateRoutes() {
|
|
| 25 |
Loading ...
|
| 26 |
</p>
|
| 27 |
}
|
| 28 |
-
else{
|
| 29 |
|
| 30 |
return (
|
| 31 |
isAuth ? <Outlet /> : <Navigate to="/login" />
|
| 32 |
);
|
| 33 |
-
|
| 34 |
}
|
| 35 |
|
| 36 |
}
|
|
|
|
| 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 |
+
// const [user, setUser] = useState(null);
|
| 8 |
+
|
| 9 |
+
|
| 10 |
const getAuthStatus = async () => {
|
| 11 |
|
| 12 |
const res = await fetch(process.env.REACT_APP_BACKEND_URL + "/session", {
|
|
|
|
| 16 |
return res.ok;
|
| 17 |
}
|
| 18 |
|
| 19 |
+
useEffect(() => {
|
| 20 |
+
getAuthStatus().then((status) => {
|
| 21 |
console.log(status);
|
| 22 |
setIsAuth(status);
|
| 23 |
})
|
| 24 |
+
}, [])
|
| 25 |
|
| 26 |
|
| 27 |
if (isAuth === undefined) {
|
|
|
|
| 29 |
Loading ...
|
| 30 |
</p>
|
| 31 |
}
|
| 32 |
+
else {
|
| 33 |
|
| 34 |
return (
|
| 35 |
isAuth ? <Outlet /> : <Navigate to="/login" />
|
| 36 |
);
|
| 37 |
+
|
| 38 |
}
|
| 39 |
|
| 40 |
}
|
client/src/UserProvider.jsx
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { createContext, useEffect, useState } from "react";
|
| 2 |
+
|
| 3 |
+
export const UserContext = createContext();
|
| 4 |
+
|
| 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 |
+
);
|
| 39 |
+
}
|
client/src/components/Chat/Chat.jsx
CHANGED
|
@@ -1,12 +1,33 @@
|
|
| 1 |
-
import React from 'react';
|
| 2 |
import "./Chat.css";
|
| 3 |
import ChatHeader from '../ChatHeader';
|
| 4 |
import ChatMain from '../ChatMain';
|
| 5 |
import ChatFooter from '../ChatFooter';
|
|
|
|
| 6 |
|
| 7 |
function Chat({
|
| 8 |
currentContact,
|
| 9 |
}) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
if (Object.keys(currentContact).length === 0) {
|
| 12 |
|
|
@@ -19,8 +40,8 @@ function Chat({
|
|
| 19 |
return (
|
| 20 |
<div className='chat'>
|
| 21 |
<ChatHeader name={currentContact.username} />
|
| 22 |
-
<ChatMain />
|
| 23 |
-
<ChatFooter />
|
| 24 |
</div>
|
| 25 |
)
|
| 26 |
}
|
|
|
|
| 1 |
+
import React, { useContext, useEffect, useState } from 'react';
|
| 2 |
import "./Chat.css";
|
| 3 |
import ChatHeader from '../ChatHeader';
|
| 4 |
import ChatMain from '../ChatMain';
|
| 5 |
import ChatFooter from '../ChatFooter';
|
| 6 |
+
import { SocketContext } from '../../SocketProvider';
|
| 7 |
|
| 8 |
function Chat({
|
| 9 |
currentContact,
|
| 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 |
+
useEffect(() => {
|
| 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 |
|
|
|
|
| 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 |
)
|
| 47 |
}
|
client/src/components/ChatFooter/ChatFooter.jsx
CHANGED
|
@@ -1,16 +1,46 @@
|
|
| 1 |
-
import React, { useContext, useEffect } from 'react'
|
| 2 |
import "./ChatFooter.css";
|
| 3 |
import { PaperAirplaneIcon, PhotoIcon, PaperClipIcon, FaceSmileIcon } from "@heroicons/react/24/solid";
|
| 4 |
import { SocketContext } from '../../SocketProvider';
|
| 5 |
|
| 6 |
-
function ChatFooter(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
const socket = useContext(SocketContext);
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
| 10 |
console.log(socket);
|
| 11 |
socket.emit("foobar");
|
| 12 |
}
|
| 13 |
-
},[socket])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
return (
|
| 16 |
<footer className='chat_footer'>
|
|
@@ -20,8 +50,8 @@ function ChatFooter() {
|
|
| 20 |
<PhotoIcon className='send_options_btn' />
|
| 21 |
<PaperClipIcon className='send_options_btn' />
|
| 22 |
</div>
|
| 23 |
-
<input className="text_box" type="text" placeholder='Message' />
|
| 24 |
-
<PaperAirplaneIcon className='send_icon' />
|
| 25 |
</footer>
|
| 26 |
)
|
| 27 |
}
|
|
|
|
| 1 |
+
import React, { useContext, useEffect, useRef } from 'react'
|
| 2 |
import "./ChatFooter.css";
|
| 3 |
import { PaperAirplaneIcon, PhotoIcon, PaperClipIcon, FaceSmileIcon } from "@heroicons/react/24/solid";
|
| 4 |
import { SocketContext } from '../../SocketProvider';
|
| 5 |
|
| 6 |
+
function ChatFooter({
|
| 7 |
+
setMessages,
|
| 8 |
+
currentContactId,
|
| 9 |
+
}) {
|
| 10 |
+
|
| 11 |
const socket = useContext(SocketContext);
|
| 12 |
+
const messageRef = useRef();
|
| 13 |
+
|
| 14 |
+
useEffect(() => {
|
| 15 |
+
if (socket) {
|
| 16 |
console.log(socket);
|
| 17 |
socket.emit("foobar");
|
| 18 |
}
|
| 19 |
+
}, [socket])
|
| 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 |
+
to:currentContactId,
|
| 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) => {
|
| 40 |
+
if (e.key == "Enter") {
|
| 41 |
+
sendMessage(messageRef.current.value);
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
|
| 45 |
return (
|
| 46 |
<footer className='chat_footer'>
|
|
|
|
| 50 |
<PhotoIcon className='send_options_btn' />
|
| 51 |
<PaperClipIcon className='send_options_btn' />
|
| 52 |
</div>
|
| 53 |
+
<input className="text_box" type="text" placeholder='Message' onKeyUp={handleEnter} ref={messageRef} />
|
| 54 |
+
<PaperAirplaneIcon onClick={() => { sendMessage(messageRef.current.value) }} className='send_icon' />
|
| 55 |
</footer>
|
| 56 |
)
|
| 57 |
}
|
client/src/components/ChatMain/ChatMain.jsx
CHANGED
|
@@ -2,24 +2,24 @@ import React from 'react'
|
|
| 2 |
import Message from '../Message';
|
| 3 |
import "./ChatMain.css";
|
| 4 |
|
| 5 |
-
function ChatMain(
|
|
|
|
|
|
|
| 6 |
|
| 7 |
-
const
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
messages.push(<Message key={i} message={"Hello"} time="10AM" isReceived={false} />);
|
| 12 |
-
}
|
| 13 |
-
return messages;
|
| 14 |
}
|
|
|
|
| 15 |
return (
|
| 16 |
<div className='chat_main'>
|
| 17 |
{
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
| 19 |
}
|
| 20 |
-
<Message message={"Hi"} time="11AM" isReceived={true} />
|
| 21 |
-
<Message message={"How are you"} time="11:10AM" isReceived={false} />
|
| 22 |
-
<Message message={"I am good what about you..."} time="11AM" isReceived={true} />
|
| 23 |
</div>
|
| 24 |
)
|
| 25 |
}
|
|
|
|
| 2 |
import Message from '../Message';
|
| 3 |
import "./ChatMain.css";
|
| 4 |
|
| 5 |
+
function ChatMain({
|
| 6 |
+
messages,
|
| 7 |
+
}) {
|
| 8 |
|
| 9 |
+
const timeToReadable = (timestamp)=>{
|
| 10 |
+
var d = new Date(timestamp*1000);
|
| 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 key={idx} message={message.message} time={timeToReadable(message.send_at)} isReceived={message.isReceived} />
|
| 21 |
+
))
|
| 22 |
}
|
|
|
|
|
|
|
|
|
|
| 23 |
</div>
|
| 24 |
)
|
| 25 |
}
|
server/index.ts
CHANGED
|
@@ -220,6 +220,27 @@ app.get("/contacts", isAuthAPIMiddleware, async (req, res) => {
|
|
| 220 |
res.json({ "contacts": contacts });
|
| 221 |
})
|
| 222 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
const io = new Server(server, {
|
| 224 |
cors: {
|
| 225 |
origin: allowed_origins,
|
|
@@ -266,6 +287,37 @@ io.on("connection", async (socket) => {
|
|
| 266 |
// an event was received from the client
|
| 267 |
console.log("received:" + data, socket.data.user);
|
| 268 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
|
| 270 |
// upon disconnection
|
| 271 |
socket.on("disconnect", (reason) => {
|
|
|
|
| 220 |
res.json({ "contacts": contacts });
|
| 221 |
})
|
| 222 |
|
| 223 |
+
// get currentContact's messsages route
|
| 224 |
+
app.get("/messages/:userid", isAuthAPIMiddleware, async (req, res) => {
|
| 225 |
+
|
| 226 |
+
// req.query.id
|
| 227 |
+
|
| 228 |
+
var messages = await db.prepare(`select * from messages
|
| 229 |
+
where (send_to=? and send_from=?)
|
| 230 |
+
or
|
| 231 |
+
(send_to=? and send_from=?)`).all(
|
| 232 |
+
res.locals.user.id,
|
| 233 |
+
req.params.userid,
|
| 234 |
+
|
| 235 |
+
req.params.userid,
|
| 236 |
+
res.locals.user.id,
|
| 237 |
+
);
|
| 238 |
+
// const messages = await db.prepare("select * from messages").all();
|
| 239 |
+
|
| 240 |
+
res.json({ "messages": messages });
|
| 241 |
+
})
|
| 242 |
+
|
| 243 |
+
|
| 244 |
const io = new Server(server, {
|
| 245 |
cors: {
|
| 246 |
origin: allowed_origins,
|
|
|
|
| 287 |
// an event was received from the client
|
| 288 |
console.log("received:" + data, socket.data.user);
|
| 289 |
});
|
| 290 |
+
socket.on("send_message", async (data) => {
|
| 291 |
+
// an event was received from the client
|
| 292 |
+
|
| 293 |
+
const msg_id = generateId(15);
|
| 294 |
+
|
| 295 |
+
const stmt = db.prepare("insert into messages(id,message,send_to,send_from,send_at) values(?,?,?,?,?)");
|
| 296 |
+
|
| 297 |
+
await stmt.run(
|
| 298 |
+
msg_id,
|
| 299 |
+
data.message,
|
| 300 |
+
data.to,
|
| 301 |
+
socket.data.user.id,
|
| 302 |
+
data.send_at,
|
| 303 |
+
);
|
| 304 |
+
|
| 305 |
+
console.log("saved message:" , data.message);
|
| 306 |
+
|
| 307 |
+
const sockets = await io.fetchSockets();
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
sockets.forEach((client)=>{
|
| 311 |
+
if(client.data.user.id==data.to)
|
| 312 |
+
{
|
| 313 |
+
return socket.to(client.data.user.id).emit("receive_message",{
|
| 314 |
+
...data,
|
| 315 |
+
"from":socket.data.user.id,
|
| 316 |
+
});
|
| 317 |
+
}
|
| 318 |
+
})
|
| 319 |
+
|
| 320 |
+
});
|
| 321 |
|
| 322 |
// upon disconnection
|
| 323 |
socket.on("disconnect", (reason) => {
|