diff --git a/dump.rdb b/dump.rdb index 41c66a7..0833eb6 100644 Binary files a/dump.rdb and b/dump.rdb differ diff --git a/public/index.html b/public/index.html index aa069f2..f24465b 100644 --- a/public/index.html +++ b/public/index.html @@ -24,7 +24,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - React App + Lister diff --git a/src/components/AllUsers.js b/src/components/AllUsers.js index 452ed7b..0a15b9e 100644 --- a/src/components/AllUsers.js +++ b/src/components/AllUsers.js @@ -9,15 +9,17 @@ import axios from 'axios'; import { useDispatch, useSelector } from 'react-redux'; import { setMessages } from '../features/messages/messagesSlice.js'; +import { setUser, addUser, removeUser } from '../features/messages/usersSlice.js'; + const AllUsers = (params) => { - const [users, setUsers] = useState([]) + // const [users, setUsers] = useState([]) const [socket, setSocket] = useState([]) const [incomingRequest, setIncomingRequest] = useState([]) - const [selectedUser, setSelectedUser] = useState(null); - const [conversation, setConversation] = useState([]); - const [friend, setFriend] = useState(null) + // const [selectedUser, setSelectedUser] = useState(null); + // const [conversation, setConversation] = useState([]); + // const [friend, setFriend] = useState(null) const conv_name = 1 @@ -26,6 +28,9 @@ const AllUsers = (params) => { const dispatch = useDispatch(); const allMessages = useSelector((state) => state.messages); + const users = useSelector((state) => state.users); + console.log('users before', users) + // setUsers(usersList) useEffect(() => { console.log('allMessages', allMessages) @@ -48,6 +53,37 @@ const AllUsers = (params) => { } }, []) + + useEffect(() => { + + if ( users.length === 0 ) { + friendList() + } + + }, [user]) + + + const friendList = () => { + + axios + .get(`${ serverIP }/auth/friends/`, { + headers: { + Authorization: `Bearer ${user.token}`, + } + }) + .then((response) => { + dispatch(setUser(response.data.user_data)); + console.log('users after', users) + console.log('friends', response.data.user_data) + }) + .catch((error) => { + console.log('Error friendsList', error) + // navigate('/login') + }) + + } + + useEffect(() => { if ( user ) { @@ -64,8 +100,15 @@ const AllUsers = (params) => { if (data.type === 'allUsers') { console.log('allUsers', data, data.type); - console.log('friendReq', data.incomingFriendRequest); - setUsers(data.users) + console.log('friends_count', data.friends_count.count); + + if ( users.length < data.friends_count.count ) { + friendList() + console.log('count < '); + } + // dispatch(setUser(data.users)); + + // setUsers(data.users) setIncomingRequest(data.incomingFriendRequest) } else if (data.type === 'addFriend') { @@ -78,16 +121,17 @@ const AllUsers = (params) => { } else if (data.type === 'confirmRequest') { - console.log('confirmRequest data', data); - setUsers(data.users) + console.log('confirmRequest data', data.user); + // setUser(data.users) + dispatch(addUser(data.user)); // Remove the added user from request list - {data.users.map((user) => { + // {data.user.map((user) => { setIncomingRequest((prevUsers) => { // Use the filter method to remove the user with the specified id - const updatedUsers = prevUsers.filter((u) => u.id !== user.id); + const updatedUsers = prevUsers.filter((u) => u.id !== data.user.id); return updatedUsers; }); - })} + // })} } else if (data.type === 'blockResponse') { console.log('blockResponse', data) @@ -124,6 +168,11 @@ const AllUsers = (params) => { console.log('confirmRequest', 'socket OPEN', id) socket.send(JSON.stringify({type: 'confirmRequest', requestId: id, userId: user.id})); } + setIncomingRequest((prevUsers) => { + // Use the filter method to remove the user with the specified id + const updatedUsers = prevUsers.filter((u) => u.id !== id); + return updatedUsers; + }); } @@ -152,7 +201,7 @@ const AllUsers = (params) => { const searchUsers = (text) => { - setUsers((prevUsers) => { + setUser((prevUsers) => { const updatedUsers = prevUsers.filter((u) => u.username.toLowerCase().includes(text.toLowerCase())) return updatedUsers; }); @@ -165,12 +214,12 @@ const AllUsers = (params) => { {incomingRequest ? (
- {incomingRequest.map((user) => ( + {incomingRequest.map((userR) => ( blockUser(user.id)} - denyRequest={() => denyRequest(user.id)} - confirmRequest={() => confirmRequest(user.id)} + user={userR} + blockUser={() => blockUser(userR.id)} + denyRequest={() => denyRequest(userR.id)} + confirmRequest={() => confirmRequest(userR.id)} /> ))}
diff --git a/src/components/Chat.js b/src/components/Chat.js index a152962..c800268 100644 --- a/src/components/Chat.js +++ b/src/components/Chat.js @@ -8,14 +8,27 @@ import ConfirmationDialog from './ConfirmationDialog.js'; import { useUser } from '../context/userContext.js'; import AddUsersToChat from './AddUsersToChat.js'; +import { useDispatch, useSelector } from 'react-redux'; +import { addMessage, removeMessage } from '../features/messages/messagesSlice.js'; + const Chat = (props) => { const { user } = useUser(); const navigate = useNavigate(); + // Reseived props: + const location = useLocation() + const chat = location.state + const chatID = chat.id + + const dispatch = useDispatch(); + const allChatMessages = useSelector((state) => state.messages); + const messages = allChatMessages.filter((message) => message.chat_id === chat.id); + console.log('allChatMessages', allChatMessages) + const [data, setData] = useState(null) - const [messages, setMessages] = useState([]); + // const [messages, setMessages] = useState([]); const [usersPhotos, setUsersPhotos] = useState([]); const [newMessage, setNewMessage] = useState(''); const [socket, setSocket] = useState(null); @@ -33,11 +46,6 @@ const Chat = (props) => { // const to make a new message been on the bottom of the chat const chatContainerRef = useRef(null); - // Reseived props: - const location = useLocation() - const chat = location.state - const chatID = chat.id - const nav = useNavigate() @@ -62,43 +70,43 @@ const Chat = (props) => { // ################################################################################################### // - const allMessages = async () => { - - axios - .get( - `${serverIP}/allMessages/${chatID}/`, - { - headers: { - Authorization: `Bearer ${user.token}`, - }, - } - ) - .then((response) => { - console.log('response.data Chat', response.data) - for (const message of response.data.messages) { - const messageObject = { - id: message.id, - username: message.username, - message: message.content, - photo: message.photo, - }; - mess.push(messageObject); - } - setMessages(mess) - setChatUsers(response.data.chat.user) - setUsersPhotos(response.data.photos) - console.log('usersPhotos', usersPhotos) - }) - .catch((error) => { - console.error('Error fetching conversation data:', error); - if(error.response.status === 403){ - nav('/login') - }else if (error.response.status === 404){ - nav('/404') - } - }); - - } + // const allMessages = async () => { + + // axios + // .get( + // `${serverIP}/allMessages/${chatID}/`, + // { + // headers: { + // Authorization: `Bearer ${user.token}`, + // }, + // } + // ) + // .then((response) => { + // console.log('response.data Chat', response.data) + // for (const message of response.data.messages) { + // const messageObject = { + // id: message.id, + // username: message.username, + // message: message.content, + // photo: message.photo, + // }; + // mess.push(messageObject); + // } + // setMessages(mess) + // setChatUsers(response.data.chat.user) + // setUsersPhotos(response.data.photos) + // console.log('usersPhotos', usersPhotos) + // }) + // .catch((error) => { + // console.error('Error fetching conversation data:', error); + // if(error.response.status === 403){ + // nav('/login') + // }else if (error.response.status === 404){ + // nav('/404') + // } + // }); + + // } @@ -106,25 +114,25 @@ const Chat = (props) => { // ##################################### CONNECT TO THE SOCKET ####################################### // // ################################################################################################### // - + const ws = new WebSocket(`${wsIP}/ws/chat/${chatID}/?userId=${user.id}&token=${user.token}`); useEffect(() => { - const ws = new WebSocket(`${wsIP}/ws/chat/${chatID}/?userId=${user.id}&token=${user.token}`); - setSocket(ws); + // const ws = new WebSocket(`${wsIP}/ws/chat/${chatID}/?userId=${user.id}&token=${user.token}`); + // setSocket(ws); - if(ws) { - ws.onopen = () => { - console.log('WebSocket connection opened'); - allMessages() - }; - ws.onclose = () => { - console.log('WebSocket connection closed'); - nav('/login'); - }; - } else { - nav('/login') - } + // if(ws) { + // ws.onopen = () => { + // console.log('WebSocket connection opened'); + // // allMessages() + // }; + // ws.onclose = () => { + // console.log('WebSocket connection closed'); + // nav('/login'); + // }; + // } else { + // nav('/login') + // } // Receive data from server via socket @@ -134,13 +142,26 @@ const Chat = (props) => { if (message.type === 'message_deleted') { console.log('message_deleted', message.id); // Handle message deletion by filtering out the deleted message - setMessages((prevMessages) => { - const updatedMessages = prevMessages.filter((msg) => msg.id !== message.id); - console.log('updatedMessages', updatedMessages); - return updatedMessages; - }); + // setMessages((prevMessages) => { + // const updatedMessages = prevMessages.filter((msg) => msg.id !== message.id); + // console.log('updatedMessages', updatedMessages); + // return updatedMessages; + // }); } else if (message.type == 'added_message') { - setMessages((prevMessages) => [...prevMessages, { id: message.id, message: message.message, username: message.username, photo: message.photo }]); + + dispatch(addMessage({ + id: message.id, + message: message.message, + username: message.username, + user_id: message.user_id, + unread: message.unread, + photo: message.photo, + chat_id: message.chat_id, + })); + + console.log('addMessage'); + + // setMessages((prevMessages) => [...prevMessages, { id: message.id, message: message.message, username: message.username, photo: message.photo }]); // scrollToBottom(); } else if (message.type == 'added_users') { setSelectedUsers((prevMessages) => [...prevMessages, { users: message, }]); @@ -178,11 +199,11 @@ const Chat = (props) => { } } - // setSocket(ws); + setSocket(ws); return () => { - if (socket) { - socket.close(); + if (ws) { + ws.close(); } }; diff --git a/src/components/ChatWithUser.js b/src/components/ChatWithUser.js index b5098f8..059bb3b 100644 --- a/src/components/ChatWithUser.js +++ b/src/components/ChatWithUser.js @@ -1,31 +1,37 @@ import React from 'react' import { serverIP } from '../config' import { Link } from 'react-router-dom'; +import profile from '../media/profile.png' const ChatWithUser = ({ user }) => { + console.log('user', user) return (
+
+ {/* {user.username} */} {user.photo ? + {user.username} : - {user.username} + {user.username} }

{user.count}

+
- - {user.username} -
- {user.last_mess.unread ? -

- {user.last_mess.content}

- : -

+ {user.last_mess.content}

- } -
+ + {user.username} +
+ {user.last_mess.unread ? +

- {user.last_mess.content}

+ : +

+ {user.last_mess.content}

+ } +

{user.last_mess.timestamp}

diff --git a/src/components/Conversation.js b/src/components/Conversation.js index 81028f4..5f1c978 100644 --- a/src/components/Conversation.js +++ b/src/components/Conversation.js @@ -4,10 +4,10 @@ import { wsIP, serverIP } from '../config.js'; import { useLocation, useNavigate } from 'react-router-dom'; import ConfirmationDialog from './ConfirmationDialog.js'; import { useUser } from '../context/userContext.js'; -import Cookies from 'js-cookie'; import { useDispatch, useSelector } from 'react-redux'; -import { setMessages, addMessage, removeMessage } from '../features/messages/messagesSlice.js'; +import { addMessage, removeMessage } from '../features/messages/messagesSlice.js'; +import { lastMessage } from '../features/messages/usersSlice.js'; const Conversation = ( props ) => { @@ -21,6 +21,7 @@ const Conversation = ( props ) => { // User data from the useContext.js const { user } = useUser(); + console.log('user', user) const dispatch = useDispatch(); const allMessages = useSelector((state) => state.messages); @@ -46,12 +47,6 @@ const Conversation = ( props ) => { const ws = new WebSocket(`${wsIP}/ws/conversation/${conv_name}/?userId=${user.id}&receiverId=${receiver.id}&token=${user.token}`); const wsu = new WebSocket(`${wsIP}/ws/AllUsers/${conv_name}/?userId=${user.id}&token=${user.token}`); - // const messages = null - - // if (conversation) { - // messages = useSelector((state) => state.messages.filter((message) => message.conversation === conversation)); - // } - // Show the bottom message with open the chat; // chatContainerRef is a var of useRef; // current means the curren element(chat): '
'; @@ -59,101 +54,53 @@ const Conversation = ( props ) => { const [hasScrolled, setHasScrolled] = useState(false); + // useEffect(() => { + // if ( !hasScrolled ) { + // window.scrollTo({ top: chatContainerRef.current.scrollHeight}) + // } + // // console.log('window.scrollTo', chatContainerRef.current.scrollHeight) + // }, []) + useEffect(() => { - if ( !hasScrolled ) { - window.scrollTo({ top: chatContainerRef.current.scrollHeight}) - } - // console.log('window.scrollTo', chatContainerRef.current.scrollHeight) - }, []) + window.scrollTo({ top: chatContainerRef.current.scrollHeight}) + }) useEffect(() => { - // // Fetch conversation data for the selected user based on userId. - // axios - // .post( - // `${serverIP}/conversations/`, - // { - // user: [parseInt(user.id), receiver.id], - // }, - // { - // headers: { - // Authorization: `Bearer ${user.token}`, - // }, - // } - // ) - // .then((response) => { - // const conversationData = response.data.conversation; - // setConversation(conversationData.id); - - // // console.log('messages before axios', messages) - - // // Check if messages for this conversation already exist in the state - // // setMessages_(allMessages.filter((message) => message.conversation === conversationData.id)); - // // console.log('messages setMymessages', messages.filter((message) => message.user_id === receiver.id)) - - // console.log('conversation ID before', conversation, conversationData.id); - // // dispatch(setConvId(conversationData.id)); - // // if (messages.length === 0 || Cookies.get('userId') !== conversationData.id) { - // // console.log('conversation ID previouse', Cookies.get('userId'), conversationData.id); - // // getConvMess(conversationData.id) - // // } - // console.log('conversationData', conversationData); - // // checkConversation(conversationData.id) - - // }) - // .catch((error) => { - // console.error('Error fetching conversation data:', error); - // if(error.response.status === 403){ - // navigate('/login') - // }else if (error.response.status === 404){ - // navigate('/404') - // } - // }); - - - // wsu.onmessage = (event) => { - // const dataU = JSON.parse(event.data); - // console.log('dataU', dataU); - // if (dataU.type === 'mess_count'){ - // console.log('mess_count', dataU); - // } - // } - // message logic ws.onmessage = (event) => { const message = JSON.parse(event.data); console.log('message', message); if (message.type === 'message_deleted') { console.log('message_deleted', message.id); - // Handle message deletion by filtering out the deleted message - // setMessages((prevMessages) => { - // const updatedMessages = prevMessages.filter((msg) => msg.id !== message.id); - // console.log('updatedMessages', updatedMessages); - // return updatedMessages; - // }); dispatch(removeMessage(message.id)); - } else { + } else if ( message.type === 'resend_message' ) { + console.log('resend_message', message.id); + } + else { console.log('received new message', message); setHasScrolled(false) // Get you to the bottom of the page if is incoming data from the server - // setMessages((prevMessages) => [ - // ...prevMessages, - // { - // id: message.id, - // content: message.content, - // username: message.username, - // unread: message.unread, - // photo: message.photo - // } - // ]); + dispatch(addMessage({ id: message.id, content: message.content, username: message.username, + user_id: message.user_id, unread: message.unread, photo: message.photo, conversation_id: message.conversation_id, })); + + dispatch(lastMessage({ + user_id: receiver.id, + last_mess: { + content: message.content, + timestamp: '10', + unread: message.unread, + }, + })); + } } @@ -168,37 +115,8 @@ const Conversation = ( props ) => { }, []); - - // const checkConversation = (id) => { - // if (messages.length === 0 || conversation === id) { - // console.log('conversation ID previouse', conversation, id); - // getConvMess(id) - // } - // } - - -// const getConvMess = ((id) => { -// console.log('getConvMess', id) -// axios.get(`${serverIP}/getConversation/${id}/`,{ - -// headers: { -// Authorization: `Bearer ${user.token}`, -// userId: user.id, -// }, - -// }) -// .then((response) => { -// console.log('getConvMess-response', response.data.messages) -// // setMessages(response.data.messages) -// // Use the setMessages action to store messages in the Redux store -// // dispatch(setMessages(response.data.messages)); -// }) -// .catch((error) => { -// console.error('Error fetching getConvMess data:', error); -// }) -// }) - const sendMessage = () => { + if (socket && socket.readyState === WebSocket.OPEN) { if (newMessage) { // dispatch(addMessage(newMessage)); @@ -212,14 +130,9 @@ const Conversation = ( props ) => { } } - // if (socketU && socketU.readyState === WebSocket.OPEN) { - // console.log('socketU', 'ok'); - // console.log('socketU data', socketU); - // socketU.send(JSON.stringify({type: 'new_message_count', count: 1 })); - // } - }; + const handleChange = (e) => { setNewMessage(e.target.value); }; @@ -317,7 +230,7 @@ const delQuest = () => { { receiver.photo ? {receiver.username} : - {receiver.username} + {receiver.username} }
{showConfirmation ? @@ -355,13 +268,13 @@ const delQuest = () => { {messages.map((message, index) => (
- {message.username === user.username ? + {message.user_id === user.id ?
{message.photo ? {message.username} : - {message.username} + {message.username} }

confirmDelete(message.id)}>{message.content}{' '}

@@ -379,7 +292,7 @@ const delQuest = () => { {message.photo ? {message.username} : - {message.username} + {message.username} }

{message.content}

@@ -391,7 +304,7 @@ const delQuest = () => { {message.photo ? {message.username} : - {message.username} + {message.username} }

{message.content}

diff --git a/src/components/Header.js b/src/components/Header.js index 35daa03..e57ec85 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -7,14 +7,23 @@ import {serverIP} from '../config.js'; import '../styles/Header.css'; import { useUser } from '../context/userContext.js'; +import { useDispatch, useSelector } from 'react-redux'; +import { setUser, clearUsers } from '../features/messages/usersSlice.js' +import { setMessages, clearMessages } from '../features/messages/messagesSlice.js'; + const Header = () => { + const dispatch = useDispatch(); + const navigate = useNavigate(); const { user } = useUser(); // Handle user logout const handleLogout = async () => { + dispatch(clearUsers()) + dispatch(clearMessages()) + try { const userId = user.id; // Send a POST request with user ID and token diff --git a/src/components/auth/Logout.js b/src/components/auth/Logout.js index 58b091c..2b64a3e 100644 --- a/src/components/auth/Logout.js +++ b/src/components/auth/Logout.js @@ -4,8 +4,14 @@ import axios from 'axios'; import {serverIP} from '../../config'; import {useUser} from '../../context/userContext.js' +import { useDispatch, useSelector } from 'react-redux'; +import { setUser } from '../../features/messages/usersSlice.js' +import { setMessages } from '../../features/messages/messagesSlice.js'; + const Logout = () => { + const dispatch = useDispatch(); + const { user } = useUser(); const navigate = useNavigate(); const userId = user.id; @@ -14,6 +20,9 @@ const Logout = () => { // Handle user logout const handleLogout = async () => { + dispatch(setUser(null)) + dispatch(setMessages(null)) + try { console.log('token', user.token) diff --git a/src/features/messages/messagesSlice.js b/src/features/messages/messagesSlice.js index fd00c12..8cc610b 100644 --- a/src/features/messages/messagesSlice.js +++ b/src/features/messages/messagesSlice.js @@ -1,7 +1,4 @@ -import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; -import axios from 'axios'; -import { serverIP } from '../../config'; -import { useUser } from '../../context/userContext'; +import { createSlice } from '@reduxjs/toolkit'; // Users slice const messagesSlice = createSlice({ @@ -17,60 +14,11 @@ const messagesSlice = createSlice({ removeMessage: (state, action) => { return state.filter((message) => message.id !== action.payload); }, + clearMessages: (state) => { + return []; + }, }, }); - export const { setConvId, setMessages, addMessage, removeMessage } = messagesSlice.actions; - export default messagesSlice.reducer; - - -// import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; -// import axios from 'axios'; -// import { serverIP } from '../../config'; -// import { useUser } from '../../context/userContext'; - -// // Async thunk for fetching messages for a specific conversation -// export const fetchMessages = createAsyncThunk('messages/fetchMessages', async (conversationId) => { -// const { user } = useUser(); -// // const existingMessages = getState().messages[conversationId]; // Check if messages for this conversation already exist in the state - -// // If messages are already in the state, return them -// // if (existingMessages) { -// // return existingMessages; -// // } - -// const response = await axios.get(`${serverIP}/getConversation/${conversationId}/`, { -// headers: { -// Authorization: `Bearer ${user.token}`, -// userId: user.id, -// }, -// }); - -// return response.data.messages; -// }); - -// // Messages slice -// const messagesSlice = createSlice({ -// name: 'messages', -// initialState: {}, -// reducers: { -// setMessages: (state, action) => { -// const { conversationId, messages } = action.payload; -// state[conversationId] = messages; -// }, -// addMessage: (state, action) => { -// const { conversationId, message } = action.payload; -// state[conversationId].push(message); -// }, -// removeMessage: (state, action) => { -// const { conversationId, messageId } = action.payload; -// state[conversationId] = state[conversationId].filter((message) => message.id !== messageId); -// }, -// }, -// }); - -// // Export action creators -// export const { setMessages, addMessage, removeMessage } = messagesSlice.actions; - -// // Export the reducer -// export default messagesSlice.reducer; + export const { setConvId, setMessages, addMessage, removeMessage, clearMessages } = messagesSlice.actions; + export default messagesSlice.reducer; \ No newline at end of file diff --git a/src/features/messages/usersSlice.js b/src/features/messages/usersSlice.js new file mode 100644 index 0000000..49c6b6e --- /dev/null +++ b/src/features/messages/usersSlice.js @@ -0,0 +1,38 @@ +import { createSlice } from '@reduxjs/toolkit'; + +// Users slice +const usersSlice = createSlice({ + name: 'users', + initialState: [], + reducers: { + setUser: (state, action) => { + return action.payload; + }, + addUser: (state, action) => { + state.push(action.payload); + }, + lastMessage: (state, action) => { + console.log('lastMessage reducer called with action:', action); + // Find the user by user_id + const userToUpdate = state.find((user) => user.id === action.payload.user_id); + + // If the user is found, update their lastMessage + if (userToUpdate) { + userToUpdate.last_mess = { + content: action.payload.last_mess.content, + timestamp: action.payload.last_mess.timestamp, + unread: action.payload.last_mess.unread, + }; + } + }, + removeUser: (state, action) => { + return state.filter((user) => user.id !== action.payload); + }, + clearUsers: (state) => { + return []; + }, + }, + }); + + export const { setUser, addUser, lastMessage, removeUser, clearUsers } = usersSlice.actions; + export default usersSlice.reducer; \ No newline at end of file diff --git a/src/media/profile.png b/src/media/profile.png new file mode 100644 index 0000000..e1a28b3 Binary files /dev/null and b/src/media/profile.png differ diff --git a/src/store.js b/src/store.js index 531d182..6eae197 100644 --- a/src/store.js +++ b/src/store.js @@ -1,9 +1,11 @@ import { configureStore } from '@reduxjs/toolkit'; import messagesReducer from '../src/features/messages/messagesSlice.js'; +import usersReducer from '../src/features/messages/usersSlice.js'; const store = configureStore({ reducer: { messages: messagesReducer, + users: usersReducer, }, });