7 Commits 1b3c63804a ... ba78f41db6

Author SHA1 Message Date
  serg1557733 ba78f41db6 fix send message and add admins funktions, fix bugs 1 year ago
  serg1557733 7b6c18029f fix send message 1 year ago
  serg1557733 aa713244c1 send message reserved variant not updateble chat 1 year ago
  serg1557733 afeb4e23f0 send message need to add update chatl 1 year ago
  serg1557733 1e9a03465f socket connect and get users , some send functional 1 year ago
  serg1557733 1dd1be27eb fix request message 1 year ago
  serg1557733 acce68919d fix token saving in localstorage and add request message 1 year ago

+ 10 - 12
backend/app.js

@@ -26,11 +26,6 @@ const PORT = process.env.PORT || 5000;
 const TOKEN_KEY = process.env.TOKEN_KEY || 'rGH4r@3DKOg06hgj'; 
 const HASH_KEY = 7;
 
-// //main test page
-// app.get('/', (req, res) => {
-//     res.send('here will be login page')
-// })
-
 const generateToken = (id, userName, isAdmin) => {
     const payload = {
         id,
@@ -57,7 +52,6 @@ const getOneUser = async (userName) => {
 app.post('/login', async (req, res) => {
     try {
         const {userName,password} = req.body;
-        console.log(userName,password)
         if (!isValidUserName(userName)){
             return res.status(400).json({message: 'Invalid username'})
         }
@@ -89,9 +83,8 @@ app.post('/login', async (req, res) => {
         }
         res.json({
             token:  generateToken(dbUser.id, dbUser.userName, dbUser.isAdmin),
-            message: 'Login success!!!'
-
         })
+        
 
     } catch (e) {
         console.log(e);
@@ -101,10 +94,11 @@ app.post('/login', async (req, res) => {
 
 
 io.use( async (socket, next) => {
-    const token = socket.handshake.auth.token;
+    const token = socket.handshake.auth.token; 
     const sockets = await io.fetchSockets();
-    
+   
     if(!token) {
+        console.log('socket')
         socket.disconnect();
         return;
     }
@@ -143,7 +137,7 @@ io.on("connection", async (socket) => {
     const userName = socket.user.userName;
     const sockets = await io.fetchSockets();
     const dbUser = await getOneUser(userName);
-
+    
     io.emit('usersOnline', sockets.map((sock) => sock.user)); // send array online users  
     socket.emit('connected', dbUser); //socket.user
    
@@ -176,10 +170,13 @@ io.on("connection", async (socket) => {
             });
             try {
                 await message.save(); 
+                
+
             } catch (error) {
                 console.log('Message save to db error', error);   
             }
-            io.emit('message', message);
+           // const newMessagesToShow = await Message.find({}).sort({ 'createDate': -1 }).limit(20);
+            io.emit('newmessage', message);
         // }
         // } 
     });
@@ -223,6 +220,7 @@ io.on("connection", async (socket) => {
                 const exist = sockets.find( current => current.user.userName == user)
                 
                 if(exist){
+                    exist.emit('ban', "dbUser")
                     exist.disconnect();  
                 }
             // }

+ 45 - 0
frontend/package-lock.json

@@ -11,6 +11,7 @@
         "@emotion/react": "^11.7.1",
         "@emotion/styled": "^11.6.0",
         "@mui/material": "^5.2.7",
+        "@reduxjs/toolkit": "^1.8.3",
         "@testing-library/jest-dom": "^5.16.1",
         "@testing-library/react": "^12.1.2",
         "@testing-library/user-event": "^13.5.0",
@@ -3069,6 +3070,29 @@
         "url": "https://opencollective.com/popperjs"
       }
     },
+    "node_modules/@reduxjs/toolkit": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.3.tgz",
+      "integrity": "sha512-lU/LDIfORmjBbyDLaqFN2JB9YmAT1BElET9y0ZszwhSBa5Ef3t6o5CrHupw5J1iOXwd+o92QfQZ8OJpwXvsssg==",
+      "dependencies": {
+        "immer": "^9.0.7",
+        "redux": "^4.1.2",
+        "redux-thunk": "^2.4.1",
+        "reselect": "^4.1.5"
+      },
+      "peerDependencies": {
+        "react": "^16.9.0 || ^17.0.0 || ^18",
+        "react-redux": "^7.2.1 || ^8.0.2"
+      },
+      "peerDependenciesMeta": {
+        "react": {
+          "optional": true
+        },
+        "react-redux": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@rollup/plugin-babel": {
       "version": "5.3.0",
       "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",
@@ -14029,6 +14053,11 @@
       "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
       "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
     },
+    "node_modules/reselect": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.6.tgz",
+      "integrity": "sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ=="
+    },
     "node_modules/resolve": {
       "version": "1.21.0",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz",
@@ -18816,6 +18845,17 @@
       "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz",
       "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA=="
     },
+    "@reduxjs/toolkit": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.3.tgz",
+      "integrity": "sha512-lU/LDIfORmjBbyDLaqFN2JB9YmAT1BElET9y0ZszwhSBa5Ef3t6o5CrHupw5J1iOXwd+o92QfQZ8OJpwXvsssg==",
+      "requires": {
+        "immer": "^9.0.7",
+        "redux": "^4.1.2",
+        "redux-thunk": "^2.4.1",
+        "reselect": "^4.1.5"
+      }
+    },
     "@rollup/plugin-babel": {
       "version": "5.3.0",
       "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",
@@ -26727,6 +26767,11 @@
       "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
       "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
     },
+    "reselect": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.6.tgz",
+      "integrity": "sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ=="
+    },
     "resolve": {
       "version": "1.21.0",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz",

+ 1 - 0
frontend/package.json

@@ -6,6 +6,7 @@
     "@emotion/react": "^11.7.1",
     "@emotion/styled": "^11.6.0",
     "@mui/material": "^5.2.7",
+    "@reduxjs/toolkit": "^1.8.3",
     "@testing-library/jest-dom": "^5.16.1",
     "@testing-library/react": "^12.1.2",
     "@testing-library/user-event": "^13.5.0",

+ 1 - 3
frontend/src/App.js

@@ -1,12 +1,10 @@
 import { LoginForm } from './components/loginForm/LoginForm';
 import { ChatPage } from './components/chatPage/ChatPage';
-import { useState } from 'react';
 import { useSelector } from 'react-redux';
-import { store } from './store';
 
 export default function App() {
 
-    const token = useSelector(state => state.userDataReducer.token) 
+    const token = useSelector(state => localStorage.getItem('token') || state.userDataReducer.token);
     
     return token ? <ChatPage /> : <LoginForm/>
 };

+ 121 - 111
frontend/src/components/chatPage/ChatPage.js

@@ -1,96 +1,52 @@
-import { useEffect, useState, useMemo, useRef, Fragment} from 'react';
-import { MessageForm } from './messageForm/MessageForm';
+import { useEffect, useMemo, useRef, Fragment} from 'react';
 import {Button,Avatar, Box} from '@mui/material';
 import { UserInfo } from './userInfo/UserInfo';
 import { dateFormat } from './utils/dateFormat';
-import {io} from 'socket.io-client';
 import './chatPage.scss';
 import { scrollToBottom } from './utils/scrollToBottom';
 import { banUser } from './service/banUser';
 import { muteUser } from './service/muteUser';
-import {sendMessage} from './service/sendMessage';
 import { store } from '../../store';
 import { removeToken} from '../../reducers/userDataReducer'
+import { useDispatch, useSelector } from 'react-redux';
+import {getSocket} from'../../reducers/socketReducer';
+import { useState } from 'react';
+import TextareaAutosize from '@mui/material/TextareaAutosize';
+import { sendMessage, storeMessage } from '../../reducers/messageReducer';
 
+export const ChatPage = () => {
 
-import { useDispatch } from 'react-redux';
 
-export const ChatPage = () => {
-    
-    const [socket, setSocket] = useState(null);
-    const [messages, setMessages] = useState([])
-    const [user, setUser] = useState({})
-    const [usersOnline, setUsersOnline] = useState([])
-    const [allUsers, setAllUsers] = useState([])
-    const randomColor = require('randomcolor'); 
-    const endMessages = useRef(null);
-    const token = localStorage.getItem('token');
+    const SOCKET_EVENTS = process.env.REACT_APP_SERVER_URL || ['allmessages', 'usersOnline', 'allDbUsers']
+
     const dispatch = useDispatch();
+    const token = useSelector(state => localStorage.getItem('token') || state.userDataReducer.token);
 
+    const startMessages = useSelector(state => state.getUserSocketReducer.startMessages)
+    const user = useSelector(state => state.getUserSocketReducer.socketUserData)
+    const usersOnline = useSelector(state => state.getUserSocketReducer.usersOnline)
+    const allUsers = useSelector(state => state.getUserSocketReducer.allUsers)
+    const socket = useSelector(state => state.getUserSocketReducer.socket)
+
+    const [message, setMessage] = useState({message: ''});
+
+
+    const randomColor = require('randomcolor'); 
+    const endMessages = useRef(null);
+    
     useEffect(() => {
         if(token){
-            
-            try {
-                setSocket(io.connect( 
-                        process.env.REACT_APP_SERVER_URL || 'http://localhost:5000', 
-                        {auth: {token}})
-                        )
-            } catch (error) {
-                console.log(error)
-            } 
+            SOCKET_EVENTS.map(event => dispatch(getSocket(event)))       
         }
     }, [token])
 
-    useEffect(() => {
-
-        if(socket){
-            socket.on('connected', (data) => {
-                setUser(data);
-                }).on('error', (e) => {
-                console.log('On connected', e)
-            }); 
-            socket.on('allmessages', (data) => {
-                    setMessages(data)
-                    }).on('error', (e) => {
-                    console.log('allmessages', e)
-            }); 
-            socket.on('usersOnline', (data) => {
-                setUsersOnline(data)
-                }).on('error', (e) => {
-                console.log(e)
-            });  
-            socket.on('allDbUsers', (data) => {
-                setAllUsers(data);
-                }).on('error', (e) => {
-                console.log(e)
-            }); 
-            socket.on('disconnect', (data) => {
-                if(data === 'io server disconnect') {
-                   socket.disconnect();
-                   store.dispatch(removeToken()); 
-                } 
-                }).on('error', (e) => {
-                console.log('error token', e)
-            });  
-            socket.on('message', (data) => {
-                setMessages((messages) => [...messages, data] )
-                }).on('error', (e) => {
-                console.log(e)
-            }); 
-             
-        }
-
-        // return () => {
-        //     socket.off('connected');
-        //     socket.off('allmessages');
-        // }
-    }, [socket])
 
     useEffect(() => {
+        console.log('useEffect chat page')
         scrollToBottom(endMessages)
-      }, [messages]);
+      }, [startMessages]);
 
-    let userColor = useMemo(() => randomColor(),[]);//color for myavatar
+    const userColor = useMemo(() => randomColor(),[]);//color for myavatar
 
     return (
         <div className='rootContainer'>
@@ -108,7 +64,7 @@ export const ChatPage = () => {
                 }}>
                     <Box className='messageBox'>                     
                         {
-                        messages.map((item, i) =>
+                        startMessages.map((item, i) =>
                         <Fragment key={i} >
                             <Avatar 
                                 sx={
@@ -125,7 +81,7 @@ export const ChatPage = () => {
                                     :
                                     {
                                         backgroundColor:  (usersOnline.map(current => {
-                                            if(item.userName == current.userName ) {
+                                            if(item.userName === current.userName ) {
                                                 return current.color
                                             }
                                           
@@ -148,7 +104,7 @@ export const ChatPage = () => {
                                 }}
 
                                 className={ 
-                                (item.userName ==user.userName)
+                                (item.userName === user.userName)
                                 ? 
                                 'message myMessage' 
                                 :
@@ -174,10 +130,58 @@ export const ChatPage = () => {
                         <div ref={endMessages}></div>
     
                         </Box>
-                            <MessageForm 
-                                    data = {user} 
-                                    sendMessage = {data => sendMessage(data, socket)}>
-                            </MessageForm>
+                        <Box 
+            component="form" 
+            onSubmit = {e  =>
+                {
+                    e.preventDefault()
+                     dispatch(sendMessage({user, socket}))
+                     dispatch(getSocket('allmessages'))
+                     setMessage({message: ''})
+                }}
+                
+                sx={{
+                    display: 'flex',
+                    margin: '20px 5px'
+                }}>
+        
+                    <TextareaAutosize
+                        id="outlined-basic" 
+                        label="Type a message..." 
+                        variant="outlined" 
+                        value={message.message}
+                        placeholder='type you message...'
+                        minRows={3}
+                        maxRows={4}
+                        onKeyPress={(e) => {
+                            if (e.key === "Enter")   {
+                                e.preventDefault();
+                                dispatch(sendMessage({user, socket}))
+                                dispatch(getSocket('allmessages'))
+                                setMessage({message: ''})
+                            }
+                        }}
+                        onChange={e => { 
+                            dispatch(storeMessage({message: e.target.value}))
+                            setMessage({message: e.target.value})}
+                        } 
+                        style={{
+                            width: '80%',
+                            resize: 'none',
+                            borderRadius: '4px',
+                        }}
+                        /> 
+                    <Button 
+                        variant="contained" 
+                        type='submit'
+                        disabled={user?.isMutted}
+                        style={{
+                            width: '20%',
+                        }}
+                    >
+                        Send
+                    </Button>
+        </Box>            
                         </Box>
 
                         <Box
@@ -188,10 +192,10 @@ export const ChatPage = () => {
                         <Button 
                                 sx={{margin:'10px 5px'}}
                                 variant="outlined"
-                                onClick={(e)=> {
-                                            socket.disconnect();
-                                            localStorage.removeItem('token');
-                                            dispatch(removeToken());
+                                onClick={()=> {
+                                        localStorage.removeItem('token');
+                                        socket.disconnect(); 
+                                        dispatch(removeToken());
                                             }}>
                                 Logout
                         </Button>
@@ -207,46 +211,52 @@ export const ChatPage = () => {
                                     key={item._id}
                                     className='online'>
                                     <div style={
-                                        {color: (usersOnline.map(current =>{
+                                        {color: (usersOnline.map(current => {
                                                 if(item.userName == current.userName ) {
                                                     return current.color
                                                 }
                                             
                                             }))}}>{item.userName}</div>
                                         <div>
-                                            <Button
-                                                variant="contained"
-                                                onClick={()=>{
-                                                    muteUser(item.userName, item.isMutted, socket)
-                                                    }}
-                                                sx={{
-                                                    margin:'3px',
-                                                    height: '25px'
-                                                }}>
-                                                    {item.isMutted
-                                                    ? 
-                                                    'unmute'
-                                                    : 'mute'}
-                                            </Button>
+                                           { (user.userName === item.userName )? 
+                                           'admin'
+                                           :   
+                                           <>      
+                                                <Button
+                                                    variant="contained"
+                                                    onClick={()=>{
+                                                    muteUser(item.userName, item?.isMutted, socket)
+                                                        }}
+                                                    sx={{
+                                                        margin:'3px',
+                                                        height: '25px'
+                                                    }}>
+                                                        {item.isMutted
+                                                        ? 
+                                                        'unmute'
+                                                        : 'mute'}
+                                                </Button>
 
-                                            <Button
-                                                variant="contained"
-                                                onClick={()=>{
+                                                <Button
+                                                    variant="contained"
+                                                    onClick={()=>{
                                                     banUser(item.userName, item.isBanned, socket)
-                                                }}
-                                                sx={{
-                                                    margin:'3px',
-                                                    height: '25px'
-                                                }}>
-                                                    {item.isBanned
-                                                ? 'unban'
-                                                : 'ban'}
-                                            </Button>
-
+                                                    }}
+                                                    sx={{
+                                                        margin:'3px',
+                                                        height: '25px'
+                                                    }}>
+                                                        {item?.isBanned
+                                                    ? 'unban'
+                                                    : 'ban'}
+                                                </Button> 
+                                            </> 
+                                             }
+                                   
                                         </div>
                                 {
-                                usersOnline.map((user, i) =>{
-                                                    if(item.userName == user.userName){
+                                usersOnline.map((user, i) => {
+                                                    if(item.userName === user.userName){
                                                     return <span key={i} style={{color: 'green'}}>online</span>
                                                     }
                                                 })

+ 25 - 14
frontend/src/components/chatPage/messageForm/MessageForm.js

@@ -1,20 +1,28 @@
 import TextareaAutosize from '@mui/material/TextareaAutosize';
 import Button from '@mui/material/Button';
-import { useState } from 'react';
 import Box from '@mui/material/Box';
+import { useDispatch, useSelector } from 'react-redux';
+import { sendMessage, storeMessage } from '../../../reducers/messageReducer';
+import { useState } from 'react';
+
 
-export const MessageForm = ({sendMessage, data}) => {
+export const MessageForm = () => {
 
+    const dispatch = useDispatch();  
+      
+    const user = useSelector(state => state.getUserSocketReducer.socketUserData)
+    const socket = useSelector(state => state.getUserSocketReducer.socket)
     const [message, setMessage] = useState({message: ''});
 
+
     return (
         <Box 
             component="form" 
-            onSubmit = {(e) =>
+            onSubmit = {e  =>
                 {
                     e.preventDefault()
-                    sendMessage(message);
-                    setMessage({message: ''});
+                     dispatch(sendMessage({user, socket}))
+                     setMessage({message: ''})
                 }}
                 
                 sx={{
@@ -30,14 +38,17 @@ export const MessageForm = ({sendMessage, data}) => {
                         placeholder='type you message...'
                         minRows={3}
                         maxRows={4}
-                        onKeyPress={(e) => {
-                            if (e.key === "Enter")   {
-                                e.preventDefault();
-                                sendMessage(message);
-                                setMessage({message: ''});
-                            }
-                        }}
-                        onChange={e => setMessage({...message, message: e.target.value})}
+                        // onKeyPress={(e) => {
+                        //     if (e.key === "Enter")   {
+                        //         e.preventDefault();
+                        //         dispatch(sendStoreMessage())
+                        //         dispatch(setMessage({message: ''}));// add localstorage save message later
+                        //     }
+                        // }}
+                        onChange={e => { 
+                            dispatch(storeMessage({message: e.target.value}))
+                            setMessage({message: e.target.value})}
+                        } 
                         style={{
                             width: '80%',
                             resize: 'none',
@@ -47,7 +58,7 @@ export const MessageForm = ({sendMessage, data}) => {
                     <Button 
                         variant="contained" 
                         type='submit'
-                        disabled={data.isMutted}
+                        disabled={user?.isMutted}
                         style={{
                             width: '20%',
                         }}

+ 0 - 5
frontend/src/components/chatPage/service/sendMessage.js

@@ -1,5 +0,0 @@
-export const sendMessage = (data, socket) => {
-    if (data.message && data.message.length < 200) {
-        socket.emit('message', data); 
-    } 
-};

+ 11 - 24
frontend/src/components/loginForm/LoginForm.js

@@ -1,35 +1,30 @@
-
 import Button from '@mui/material/Button';
 import TextField from '@mui/material/TextField';
 import Container from '@mui/material/Container';
 import Box from '@mui/material/Box';
 import { Modal } from '../modalMessage/Modal';
 import { useDispatch, useSelector } from 'react-redux';
-import { useEffect } from 'react';
-import  {setUserName, setUserPassword, getUserData} from '../../reducers/userDataReducer'
+import  {setUserName, setUserPassword, getUserData} from '../../reducers/userDataReducer';
 
 export const LoginForm = () => {
 
     const dispatch = useDispatch();
     const userName = useSelector(state => state.userName);
     const password = useSelector(state => state.password);
-   console.log('logform', userName)
+
     return (
         <Container maxWidth="xs">
             <Box
                 component="form" 
                 onSubmit={(e) => {
                     e.preventDefault();
-                    console.log('submit', userName)
-                   dispatch(getUserData())
-            }
-                }
+                    dispatch(getUserData())
+                }}
                 sx={{
                     marginTop: 40,
                     display: 'flex',
                     flexDirection: 'column',
-                }}
-                >
+                }}>
                 <TextField
                         margin="normal"
                         required
@@ -40,8 +35,7 @@ export const LoginForm = () => {
                         autoComplete="email"
                         autoFocus
                         value={userName}
-                        onChange={e => dispatch(setUserName({userName: e.target.value}))}
-                />
+                        onChange={e => dispatch(setUserName({userName: e.target.value}))}/>
                 <TextField
                         margin="normal"
                         required
@@ -53,23 +47,16 @@ export const LoginForm = () => {
                         autoComplete="current-password"
                         value={password}
                         onChange={e => 
-                            
                             dispatch(setUserPassword({password: e.target.value}))
-                        }
-                />
-                <Modal 
-                    //text={textModal}
-                    //propDisplay = {display}
-                
-                ></Modal>
+                        }/>
+                <Modal/>
                 <Button 
                     type="submit"
                     variant="contained"
-                    fullWidth
-                   >Login
-                    
+                    fullWidth>
+                        Login
                 </Button>
             </Box>
         </Container>
     )
-}
+};

+ 0 - 27
frontend/src/components/loginForm/utils/handleSubmit.js

@@ -1,27 +0,0 @@
-// import { isValidPayload } from './validations/isValidPayload';
-// import { isValidUserName } from './validations/isValidUserName';
-// import { sendForm } from './sendForm';
-
-
-// export const handleSubmit = async (e, userName , password) => {
-//     console.log(userName,password)
-//     const POST_URL =  process.env.REACT_APP_POST_URL || 'http://localhost:5000/login';//add on handlesubmit
-//     e.preventDefault();
-
-//     if(isValidPayload(userName,password) && isValidUserName(userName)){
-//         const data = await sendForm(POST_URL, userName, password);
-//         const token = data.token;
-//        if(token){
-//            localStorage.setItem('token', token); 
-//           // dispatch(storeToken(token))
-//        }
-//         // setTextModal(data.message)
-//         // setDisplay('block')
-       
-//         console.log(userName,password)
-//    } 
-// //    else {
-// //       //  setTextModal('too short or using special symbols')
-// //        // setDisplay('block')
-// //     }   
-// }

+ 9 - 9
frontend/src/components/loginForm/utils/sendForm.js

@@ -1,12 +1,12 @@
-export const sendForm = async (POST_URL, userData) => {
-    console.log('sendform', userData)
-        const response = await fetch(POST_URL, {
-            method: 'POST',
-            body: JSON.stringify(userData),
-            headers: {
-                'Content-Type': 'application/json'
-            }
-});
+export const sendForm = async (POST_URL, userData) => { 
+        const response = await fetch(POST_URL, 
+                        {
+                            method: 'POST',
+                            body: JSON.stringify(userData),
+                            headers: {
+                                'Content-Type': 'application/json'
+                        }
+                    });
         const json = await response.json();
         return json;
 }

+ 15 - 5
frontend/src/components/modalMessage/Modal.js

@@ -1,10 +1,20 @@
 import Alert from '@mui/material/Alert';
+import { useSelector, useDispatch } from 'react-redux';
+import { deleteResponseMessage } from '../../reducers/userDataReducer';
 
-export const Modal = ({text, propDisplay}) => {
+export const Modal = () => {
+    
+    let text = useSelector(state=> state.userDataReducer.responseMessage)
+    const dispatch =useDispatch();
+    if (text){
+            setTimeout(() => {
+               dispatch(deleteResponseMessage())
+                }, 2500)
+    }
+    
     return <Alert 
                 severity="error"
-                sx={{display: propDisplay}}
-            >
-            {text}
+                sx={{display: (text ? 'block':'none' )}}>
+                {text}
             </Alert>
-}
+};

+ 0 - 0
frontend/src/reducers/index.js


+ 35 - 0
frontend/src/reducers/messageReducer.js

@@ -0,0 +1,35 @@
+import { createSlice} from '@reduxjs/toolkit';
+
+const initialState = {
+    startMessages: [],
+    message:''
+}
+
+export const sendMessageToSocket = (state, data) => {
+             if (state.message && state.message.length < 200) {    //remove to other file
+                data.socket.emit('message', {...data.user, message: state.message}); 
+                   
+            } 
+    };
+
+const messageReducerSlice = createSlice({
+    name: 'messageReducer',
+    initialState,
+    reducers: {
+        storeMessage: (state, action) => {state.message = action.payload.message},
+        sendMessage: (state, action) => sendMessageToSocket(state, action.payload),
+        clearMessage: (state) => {state.message = ''}
+        
+    },
+});
+
+const {actions, reducer} = messageReducerSlice;
+const messageReducer = reducer;
+
+export default messageReducer;
+
+export const {
+    storeMessage, 
+    sendMessage,
+    clearMessage
+    } = actions;

+ 97 - 19
frontend/src/reducers/socketReducer.js

@@ -1,22 +1,100 @@
+import {createSlice } from '@reduxjs/toolkit';
+import {io} from 'socket.io-client';
+import { store } from '../store';
+import { removeToken } from './userDataReducer';
+
+
 const initialState = {
-    socket: null
-  }
-  
-const socketReducer = (state = initialState, action) => {
-    switch (action.type){
-    case 'SET_SOCKET':  
-        return {...state, 
-            socket:
-              action.socket
-            };
-    case 'REMOVE_SOCKET':  
-        return {...state, 
-            socket: 
-              initialState.socket
-            };
-    default:
-      return state
+    socketStatus: 'idle',
+    socket: null,
+    socketUserData: {},
+    usersOnline: [],
+    startMessages: [],
+    allUsers: []
+}
+
+const SOCKET_URL =  process.env.REACT_APP_SERVER_URL || 'http://localhost:5000'; 
+
+const connectToSocket = (event) => {
+        try {
+            const token = localStorage.getItem('token');
+            if(token){
+                const socket = io.connect( 
+                    SOCKET_URL, 
+                    {auth: {token}})
+                    socket.on('connected', data => {
+                                store.dispatch(getUser(data));
+                            })
+                            .on(event, (data) => {
+                                   switch (event){
+                                    case 'allmessages':
+                                        store.dispatch(getAllMessages(data));
+                                        break;
+                            
+                                    case 'usersOnline':
+                                        store.dispatch(getUsersOnline(data));
+                                        break;
+
+                                    case 'allDbUsers':
+                                        store.dispatch(getAllUsers(data));
+                                        break;
+                                    default: 
+                                        break;
+                                    }
+                                })
+                            .on('newmessage', (data) => {
+                                store.dispatch(addNewMessage(data))
+                                })
+                            .on('ban', (data) => {
+                                store.dispatch(removeToken()); 
+                                localStorage.removeItem('token');
+                                })
+                            .on('disconnect', (data) => {
+                                if( data === 'io server disconnect') {
+                                    socket.disconnect();
+                                    store.dispatch(removeToken()); 
+                                }
+                            })
+                            .on('error', e => {console.log('On connected', e)}); 
+                return socket;       
+            }   
+        } catch (error) {
+            console.log('error connecting to socket ', error)
+        } 
+    };
+
+    
+export const getUserSocketSlice = createSlice({
+    name: 'userSocket',
+    initialState,
+    reducers: {
+        removeSocket: state => {
+            state.socket = null
+            state.socketStatus = 'disconnected'},
+        getSocket: (state, action) => {
+            state.socket = connectToSocket(action.payload);
+            state.socketStatus = 'connected';
+        },
+        getUser: (state, action) => {state.socketUserData = action.payload},
+        getAllMessages: (state, action) => {state.startMessages = action.payload},
+        getUsersOnline: (state, action) => {state.usersOnline = action.payload},
+        getAllUsers: (state, action) => {state.allUsers = action.payload},
+        addNewMessage: (state, action) => {state.startMessages.push(action.payload)}
+        }
     }
-  };
+);
+
+
+const {actions, reducer} = getUserSocketSlice;
+const getUserSocketReducer = reducer;
 
- export default socketReducer;
+export default getUserSocketReducer;
+export const {
+    removeSocket,
+    getSocket, 
+    getUser,
+    getAllMessages,
+    getUsersOnline,
+    addNewMessage,
+    getAllUsers
+} = actions;

+ 0 - 22
frontend/src/reducers/tokenReducer.js

@@ -1,22 +0,0 @@
-const initialState = {
-    token: ''
-  }
-  
-const tokenReducer = (state = initialState, action) => {
-    switch (action.type){
-    case 'SET_TOKEN':  
-        return {...state, 
-            token:
-              action.token
-            };
-    case 'REMOVE_TOKEN':  
-        return {...state, 
-            token: 
-              initialState.token
-            };
-    default:
-      return state
-    }
-  };
-
- export default tokenReducer;

+ 35 - 20
frontend/src/reducers/userDataReducer.js

@@ -8,28 +8,29 @@ const initialState = {
     password: '',
     userLoadingStatus: 'idle',
     token: '',
-    socket: null,
+    socket: null, 
     responseMessage: ''
 }
 
-
 const POST_URL =  process.env.REACT_APP_POST_URL || 'http://localhost:5000/login';
 
 export const getUserData = createAsyncThunk(
     'userData/getUser',
      ( t , thunkAPI) => {
       const userData = thunkAPI.getState().userDataReducer;
-          if(userData.userName){
-        if(isValidPayload({...userData}) && isValidUserName({...userData}))
-            console.log('getUserData', userData)
-            try {
-                const response =  sendForm(POST_URL, userData);
-                return response;
-            }catch (err) {
-                return err?.message
+        if(userData.userName){
+            if(isValidPayload({...userData}) && isValidUserName({...userData}))
+                try {
+                    const response =  sendForm(POST_URL, userData);
+                    return response;
+                }catch (err) {
+                    console.log('error getUserData thunk', err)
+                    return err?.message
+                }
             }
-        }
+        
     });
+                    
 
 const getUserDataSlice = createSlice({
     name: 'userData',
@@ -37,21 +38,34 @@ const getUserDataSlice = createSlice({
     reducers: {
         setUserName: (state, action) => {state.userName = action.payload.userName},
         setUserPassword: (state, action) => {state.password = action.payload.password},
-        removeToken: (state, action) => {state.token = ''}
+            
+        removeToken: state => {
+            state.token = ''
+        },
+        deleteResponseMessage: state => {state.responseMessage = ''},
     },
     extraReducers: (builder) => { 
        builder
           .addCase(getUserData.fulfilled, (state, action) => {
-            state.userLoadingStatus = 'fulfilled'
-            state.token = action.payload
-            localStorage.setItem('token', action.payload.token)
-            console.log('fulfilled',action.payload.token)
+            if(action.payload?.token){
+                state.token = action.payload.token
+                state.userLoadingStatus = 'success'
+                localStorage.setItem('token', action.payload.token)
+            }
+            if(action.payload?.message){
+                state.responseMessage = action.payload.message
+                state.userLoadingStatus = 'error';
+            }
           })
-           .addCase(getUserData.rejected, (state) => {
+           .addCase(getUserData.rejected, (state, action) => {
                state.userLoadingStatus = 'error';
-               console.log('rejected', state)
+               if(action.payload?.message){
+                state.responseMessage = action.payload.message
+            }
+            state.responseMessage = 'Something went wrong...'
           })
-        }
+        },
+        
 });
 
 const {actions, reducer} = getUserDataSlice;
@@ -61,5 +75,6 @@ export default userDataReducer;
 export const {
     setUserName,
     setUserPassword,
-    removeToken
+    removeToken,
+    deleteResponseMessage,
 } = actions;

+ 8 - 5
frontend/src/store.js

@@ -1,13 +1,16 @@
-import tokenReducer from './reducers/tokenReducer';
-import socketReducer from './reducers/socketReducer';
+//import getUserSocketReducer from './reducers/socketReducer'
 import userDataReducer from './reducers/userDataReducer';
+import getUserSocketReducer from './reducers/socketReducer';
+import messageReducer from './reducers/messageReducer';
+import { configureStore } from '@reduxjs/toolkit';
 
-import { configureStore } from '@reduxjs/toolkit'
 
 
 export  const store = configureStore({
-    reducer: {tokenReducer, socketReducer, userDataReducer},
-    middleware: getDefaultMiddleware => getDefaultMiddleware(),
+    reducer: {userDataReducer, getUserSocketReducer, messageReducer},
+    middleware: getDefaultMiddleware => getDefaultMiddleware({
+        serializableCheck: false
+      }),
     devTools: process.env.NODE_ENV !== 'production',
 })