فهرست منبع

add file sending to chat on backend save and emit event

serg1557733 1 سال پیش
والد
کامیت
58701fbefc

+ 67 - 0
backend/app.js

@@ -125,6 +125,73 @@ app.post('/avatar', async (req, res) =>  {
     }
 })
 
+app.post('/files', async (req, res) =>  {
+    if (!req.files || Object.keys(req.files).length === 0) {
+        return res.status(400).json('No files were uploaded.');
+    }  
+    
+    const user = jwt.verify(req.body.token, TOKEN_KEY);
+    const oneUser = await getOneUser(user.userName);
+    if(oneUser.isMutted){  
+        return;
+     }
+    const files = req.files.files;
+    if (files.length) {
+        for (let i = 0; i < files.length; i++) {
+        let file = files[i]
+        file.mv(STATIC_PATH + '\/files/' + file.name) 
+
+        const message = new Message({
+            text: 'data.message',
+            userName: user.userName,
+            createDate: Date.now(),
+            user: oneUser.id, //add link to other collection by id
+            file: file.name
+        });
+
+        try {
+            await message.save();
+            if(!oneUser.messages){
+                await oneUser.update({ $set: {'messages': []}});
+            }
+            await oneUser.messages.push(message)
+            await oneUser.save()
+        } 
+        catch (error) {
+            console.log('Message save to db error', error);   
+        }
+        const newMessages = await message.populate( {path:'user'})   
+        io.emit('newmessage', newMessages);        
+
+   }}     
+    else {
+            files.mv(STATIC_PATH + '\/files/' + files.name);      //for one file       
+            const message = new Message({
+                text: 'File sent',
+                userName: user.userName,
+                createDate: Date.now(),
+                user: oneUser.id, //add link to other collection by id
+                file: files.name
+            });
+    
+            try {
+                await message.save();
+                if(!oneUser.messages){
+                    await oneUser.update({ $set: {'messages': []}});
+                }
+                await oneUser.messages.push(message)
+                await oneUser.save()
+            } 
+            catch (error) {
+                console.log('Message save to db error', error);   
+            }
+            const newMessages = await message.populate( {path:'user'})   
+            console.log(newMessages)
+            io.emit('newmessage', newMessages); 
+    } 
+
+    return res.json({ message:'File was uploud succesfully...'})
+  })
 
 
 

+ 3 - 1
backend/db/models/Message.js

@@ -4,7 +4,9 @@ const Message = new Schema({
     text: {type: String, required: true},
     userName : {type: String, required: true},
     createDate: {type: Date, required: true},
-    user: { type: Schema.Types.ObjectId, ref: 'User' } //not using 
+    user: { type: Schema.Types.ObjectId, ref: 'User' },//not using 
+    file: {type: String} 
+
 })
 
 module.exports = model('Message', Message)

+ 31 - 1
frontend/src/components/chatPage/ChatPage.jsx

@@ -7,10 +7,11 @@ import { store } from '../../store';
 import { removeToken} from '../../reducers/userDataReducer'
 import { useDispatch, useSelector } from 'react-redux';
 import {getSocket} from'../../reducers/socketReducer';
-import { sendMessage, storeMessage } from '../../reducers/messageReducer';
+import { sendMessage, storeMessage, fileMessage } from '../../reducers/messageReducer';
 import { editMessage } from '../../reducers/messageReducer';
 import { SwitchButton } from './SwitchButton';
 import { MessageEditorMenu } from './MessageEditorMenu.jsx';
+import imgBtn from '../../assets/img/gg.png'
 import './chatPage.scss';
 
 
@@ -47,10 +48,14 @@ export const ChatPage = () => {
             SOCKET_EVENTS.map(event => dispatch(getSocket(event)))   
         }
     }, [token, editOldMessage, showUserInfoBox])
+
+  
  
     return (
         <div className='rootContainer'>
 
+    
+
             <Box className = 'rootBox'>
 
                 { isTabletorMobile ? <SwitchButton/> : null}
@@ -80,6 +85,31 @@ export const ChatPage = () => {
                            
                         }>
 
+                        <Button
+                            variant="contained" 
+                            component="label"
+                            sx = {{
+                                minWidth: 'auto',
+                                backgroundImage:'url(' + imgBtn + ')' ,
+                                backgroundPosition: 'center', 
+                                backgroundRepeat: "no-repeat", 
+                                backgroundSize: '20px 40px'
+
+                            }}
+                        >
+                        <input
+                            onChange={e =>{
+                                dispatch(fileMessage(e.target.files))
+                            }}
+
+                            type="file"
+                            multiple
+                            hidden
+                        />
+
+                       
+                        </Button>            
+
                         <TextareaAutosize
                             id="outlined-basic" 
                             label="Type a message..." 

+ 1 - 1
frontend/src/components/chatPage/MessageEditorMenu.jsx.jsx

@@ -1,7 +1,7 @@
 import { useDispatch } from 'react-redux';
 import { editMessage } from '../../reducers/messageReducer';
 
-
+//test
 export const MessageEditorMenu = () => {
 
     const dispatch = useDispatch();

+ 5 - 4
frontend/src/components/chatPage/messageForm/MessegaForm.jsx

@@ -21,14 +21,15 @@ export const MessageForm = () => {
     const storeMessageId = useSelector(state => state.messageReducer.messageId)
 
 
-    const endMessages =useRef(null);
+    let endMessages =useRef(null);
 
     const regYoutube = /http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?‌​[\w\?‌​=]*)?/; //for youtube video
 
 
     useEffect(() => {
         scrollToBottom(endMessages)
-      }, [startMessages, usersOnline]);
+        console.log('useEffect', endMessages)
+      }, [startMessages]);
                   
     return (             
             <Box className='messageBox'>  
@@ -39,7 +40,7 @@ export const MessageForm = () => {
                         onClick = {(e) => {
                             if(e.target.className.includes('myMessage') && (item.userName === user.userName) && (item.text === e.target.textContent)){
                                 e.currentTarget.className += ' editMessage'  
-                                dispatch(editMessage({editMessage: e.target.textContent, messageId: item._id}))   
+                                dispatch(editMessage({editMessage: e.target.textContent, messageId: item._id}))  
                                 }
                         }}
                         > 
@@ -72,7 +73,7 @@ export const MessageForm = () => {
                         
                         </StyledAvatar>
                         <div 
-                            key={i/10}
+                            key={i}
                         
                             className={ 
                                 (item.userName === user.userName)? 'message myMessage' :'message'}>

+ 45 - 1
frontend/src/reducers/messageReducer.js

@@ -1,12 +1,20 @@
 import { createSlice} from '@reduxjs/toolkit';
+import axios from 'axios';
+import { createAsyncThunk } from '@reduxjs/toolkit';
+
 
 const initialState = {
     startMessages: [],
     message:'',
     editMessage: '',
     messageId: '', 
+    files: [],
+    ref: null
 }
 
+const POST_FILES_URL = 'http://localhost:5000/files';
+
+
 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}); 
@@ -21,6 +29,32 @@ export const editMessageToSocket = (state, data) => {
        } 
 };
 
+export const fileMessage = createAsyncThunk(
+    'messageReducer/fileMessage',
+    async (files) => {
+        const token = localStorage.getItem('token')
+        try {
+            const formData = new FormData();
+            for (let i = 0; i < files.length; i++) {
+                formData.append('files', files[i])
+            }
+            formData.append('token', token)
+            const response = await axios.post(POST_FILES_URL, formData,
+                {
+                    headers: {
+                      "Content-type": "multipart/form-data",
+                    }
+                  });
+            return await response;
+            
+        } catch (err) {
+            console.log('error messageReducer thunk', err)
+                return err?.message
+            
+            }
+        })
+
+
 
 
 
@@ -32,9 +66,19 @@ const messageReducerSlice = createSlice({
         editMessage: (state, action) => {
             state.editMessage = action.payload.editMessage;
             state.messageId = action.payload.messageId;
+            state.ref = action.payload.ref;
             },
         sendMessage: (state, action) => sendMessageToSocket(state, action.payload),
-        clearMessage: (state) => {state.message = ''}
+        clearMessage: (state) => {state.message = ''},
+        extraReducers: (bilder) => 
+            bilder
+            .addCase(fileMessage.fulfilled, (state, action) => {
+                state.files = action.payload.data?.files
+                
+        })
+            .addCase(fileMessage.rejected, (state, action) => {
+                console.log('post file rejected', action.payload)
+        })
         
     },
 });

+ 1 - 0
frontend/src/reducers/socketReducer.js

@@ -42,6 +42,7 @@ const connectToSocket = (event) => {
                                 })
                             .on('newmessage', (data) => {
                                 store.dispatch(addNewMessage(data))
+                                console.log(data)
                                 })
                             .on('ban', (data) => {
                                 store.dispatch(removeToken());