ivar_n 2 سال پیش
والد
کامیت
a6a2311e12
9فایلهای تغییر یافته به همراه150 افزوده شده و 44 حذف شده
  1. 59 0
      package-lock.json
  2. 2 0
      package.json
  3. 1 0
      public/index.html
  4. 10 1
      src/actions/authActions.js
  5. 2 0
      src/actions/index.js
  6. 3 3
      src/components/MainMenu.jsx
  7. 64 25
      src/components/SendingField.jsx
  8. 2 10
      src/reducers/index.js
  9. 7 5
      src/reducers/store.js

+ 59 - 0
package-lock.json

@@ -16,12 +16,14 @@
         "@testing-library/jest-dom": "^5.16.1",
         "@testing-library/react": "^12.1.2",
         "@testing-library/user-event": "^13.5.0",
+        "array-move": "^4.0.0",
         "react": "^17.0.2",
         "react-dom": "^17.0.2",
         "react-dropzone": "^11.5.1",
         "react-redux": "^7.2.6",
         "react-router-dom": "^5.3.0",
         "react-scripts": "5.0.0",
+        "react-sortable-hoc": "^2.0.0",
         "redux": "^4.1.2",
         "redux-thunk": "^2.4.1",
         "sass": "^1.45.1",
@@ -4642,6 +4644,17 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/array-move": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/array-move/-/array-move-4.0.0.tgz",
+      "integrity": "sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ==",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/array-union": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
@@ -8926,6 +8939,14 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/invariant": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+      "dependencies": {
+        "loose-envify": "^1.0.0"
+      }
+    },
     "node_modules/ip": {
       "version": "1.1.5",
       "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@@ -14009,6 +14030,21 @@
         }
       }
     },
+    "node_modules/react-sortable-hoc": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz",
+      "integrity": "sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==",
+      "dependencies": {
+        "@babel/runtime": "^7.2.0",
+        "invariant": "^2.2.4",
+        "prop-types": "^15.5.7"
+      },
+      "peerDependencies": {
+        "prop-types": "^15.5.7",
+        "react": "^16.3.0 || ^17.0.0",
+        "react-dom": "^16.3.0 || ^17.0.0"
+      }
+    },
     "node_modules/react-transition-group": {
       "version": "4.4.2",
       "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
@@ -20144,6 +20180,11 @@
         "is-string": "^1.0.7"
       }
     },
+    "array-move": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/array-move/-/array-move-4.0.0.tgz",
+      "integrity": "sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ=="
+    },
     "array-union": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
@@ -23302,6 +23343,14 @@
         "side-channel": "^1.0.4"
       }
     },
+    "invariant": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+      "requires": {
+        "loose-envify": "^1.0.0"
+      }
+    },
     "ip": {
       "version": "1.1.5",
       "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@@ -26826,6 +26875,16 @@
         "workbox-webpack-plugin": "^6.4.1"
       }
     },
+    "react-sortable-hoc": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz",
+      "integrity": "sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==",
+      "requires": {
+        "@babel/runtime": "^7.2.0",
+        "invariant": "^2.2.4",
+        "prop-types": "^15.5.7"
+      }
+    },
     "react-transition-group": {
       "version": "4.4.2",
       "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",

+ 2 - 0
package.json

@@ -11,12 +11,14 @@
     "@testing-library/jest-dom": "^5.16.1",
     "@testing-library/react": "^12.1.2",
     "@testing-library/user-event": "^13.5.0",
+    "array-move": "^4.0.0",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
     "react-dropzone": "^11.5.1",
     "react-redux": "^7.2.6",
     "react-router-dom": "^5.3.0",
     "react-scripts": "5.0.0",
+    "react-sortable-hoc": "^2.0.0",
     "redux": "^4.1.2",
     "redux-thunk": "^2.4.1",
     "sass": "^1.45.1",

+ 1 - 0
public/index.html

@@ -24,6 +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`.
     -->
+    <script src='https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js'></script>
     <title>TopChat229</title>
   </head>
   <body>

+ 10 - 1
src/actions/authActions.js

@@ -2,13 +2,21 @@ import {gql} from '../helpers'
 import {   
    actionPromise, 
    actionAuthLogin, 
-   actionAboutMe
+   actionAuthLogout,
+   actionAboutMe,
+   socket
 } from '../reducers'
 import {   
    actionUploadFile
 } from './mediaActions'
 
 
+   export const actionFullLogout = () => {
+      async (dispatch) => {
+         await dispatch(actionAuthLogout())
+         socket.disconnect()
+      }
+   }
  
    const actionLogin = (login, password) => (
       actionPromise('login', gql(`query log($login: String, $password: String) {
@@ -22,6 +30,7 @@ import {
          if (token) {
             await dispatch(actionAuthLogin(token))
             await dispatch(actionAboutMe())
+            socket.emit('jwt', token)
          }
       }
    )

+ 2 - 0
src/actions/index.js

@@ -1,5 +1,6 @@
 // далекие от редьюсеров экшены 
 import {
+   actionFullLogout,
    actionFullLogin, 
    actionFullRegister, 
    actionSetUserInfo,
@@ -30,6 +31,7 @@ import {
 
 
 export {
+   actionFullLogout,
    actionFullLogin, 
    actionFullRegister, 
    actionSetUserInfo,

+ 3 - 3
src/components/MainMenu.jsx

@@ -12,7 +12,7 @@ import Divider from '@mui/material/Divider';
 import ListItem from '@mui/material/ListItem';
 import ListItemText from '@mui/material/ListItemText';
 
-import {actionAuthLogout} from "../reducers"
+import {actionFullLogout} from "../actions"
 import {connect}  from 'react-redux';
 
 import {CProfileModal, CUserAvatar} from '.'
@@ -20,12 +20,12 @@ import {CProfileModal, CUserAvatar} from '.'
 
 const LogoutBtn = ({onLogout}) => (
    <>
-      <ListItem button onClick={onLogout} >
+      <ListItem button onClick={() => onLogout()} >
          <ListItemText primary={'Выйти'} />
       </ListItem>
    </>
 )
-const CLogoutBtn = connect(null, {onLogout: actionAuthLogout})(LogoutBtn)
+const CLogoutBtn = connect(null, {onLogout: actionFullLogout})(LogoutBtn)
 
 export const MainMenu = () => {
    const [anchorEl, setAnchorEl] = useState(null)

+ 64 - 25
src/components/SendingField.jsx

@@ -7,6 +7,9 @@ import AttachFileIcon from '@mui/icons-material/AttachFile';
 import CloseIcon from '@mui/icons-material/Close';
 
 import {useDropzone} from 'react-dropzone';
+import {render} from 'react-dom';
+import {sortableContainer, sortableElement} from 'react-sortable-hoc';
+import {arrayMoveImmutable} from 'array-move';
 
 import {connect}  from 'react-redux'
 import {
@@ -66,6 +69,41 @@ const thumbsContainer = {
    height: '100%'
  };
 
+
+
+
+ const SortableItem = sortableElement(({onDelete, index, file}) => (
+   <div style={thumb}>         
+      <div style={closeBtn}>
+         <CloseIcon 
+            fontSize="small" 
+            sx={{ cursor: 'pointer' }}
+            onClick={onDelete} 
+         />
+      </div>         
+      <div style={thumbInner}>           
+         <img
+         src={file.preview}
+         style={img}
+         /> 
+      </div>     
+   </div>
+ ));
+
+ const SortableContainer = sortableContainer(({children}) => {
+
+   return (
+      <div style={thumbsContainer}>
+         {children}
+      </div>
+   )
+ });
+
+
+
+
+
+
 const MsgDropZone = ({setText, setFiles, files}) => {
 
    const {
@@ -89,30 +127,15 @@ const MsgDropZone = ({setText, setFiles, files}) => {
 
    // console.log(files)
 
-
-
-
    const onDelete = (i) => {
-      setFiles( files.filter((el, index) => index !== i) )
+      setFiles( (files) => files.filter((el, index) => index !== i) )
    }
 
-   const thumbs = files.map((file, i) => (
-      <div style={thumb} key={file.preview}>         
-         <div style={closeBtn}>
-            <CloseIcon 
-               fontSize="small" 
-               sx={{ cursor: 'pointer' }}
-               onClick={() => onDelete(i)} 
-            />
-         </div>         
-         <div style={thumbInner}>           
-            <img
-            src={file.preview}
-            style={img}
-            /> 
-         </div>     
-      </div>
-   ));
+   const onSortEnd = ({oldIndex, newIndex}) => {
+      setFiles( (files) => (
+        arrayMoveImmutable(files, oldIndex, newIndex)
+      ));
+    }
    
    // useEffect(() => {
    // // Make sure to revoke the data uris to avoid memory leaks
@@ -124,9 +147,22 @@ const MsgDropZone = ({setText, setFiles, files}) => {
       <form action="/upload" method="post" encType="multipart/form-data" id='form'> 
          <section style={containerWrapp}>
 
-            <div style={thumbsContainer}>
-               {thumbs}
-            </div>
+
+            <SortableContainer 
+               onSortEnd={onSortEnd}
+               axis={'xy'}
+               pressDelay={100}
+            >
+               {files.map((file, i) => (
+                  <SortableItem 
+                     key={file.preview} 
+                     index={i} 
+                     file={file}
+                     onDelete={() => onDelete(i)}
+                     axis={'xy'}
+                  />
+               ))}
+            </SortableContainer>
         
 
             <div {...getRootProps({className: 'dropzone'})} style={{ display: 'flex', alignItems: 'center'}}>
@@ -156,10 +192,13 @@ const SendingField = ({onSend}) => {
    const [files, setFiles] = useState([])
 
    return (
-      <Box sx={{ display: 'flex', alignItems: 'center', height: '100%', width: '100%'}} >
+      <Box sx={{ display: 'flex', alignItems: 'center', /* flexDirection: 'column', */
+            height: '100%', width: '100%'}} >
       
          <Box sx={{ flexGrow: 1, flexShrink: 1, flexBasis: '80%', overflow: 'auto', height: '100%', backgroundColor: '#fff' }}>
+            
             <MsgDropZone setText={setText} setFiles={setFiles} files={files} />
+         
          </Box>         
          <Box sx={{ flexGrow: 1, flexShrink: 0, flexBasis: '50px'}}>
             <Button 

+ 2 - 10
src/reducers/index.js

@@ -18,14 +18,7 @@ import {
     actionUserFindOne,
     actionAboutMe
 } from './findUserActions'
-
-import {store} from './store'
-
-
-
-
-
-
+import {store, socket} from './store'
 
 
 export {
@@ -46,8 +39,7 @@ export {
     actionUserFindOne,
     actionAboutMe
 } 
-
-export {store} 
+export {store, socket} 
 
 
 

+ 7 - 5
src/reducers/store.js

@@ -12,7 +12,7 @@ import {
    actionChatLeft 
 } from './chatsReducer'
 
-const { io } = require("socket.io-client");
+// const { io } = require("socket.io-client");
 
 export const store =  createStore (  combineReducers({ 
                                     // promise: localStoredReducer(promiseReducer, 'promise'),
@@ -30,11 +30,13 @@ store.dispatch(actionAboutMe())
 store.subscribe(() => console.log(store.getState()))
 
 
-const socket = io("ws://chat.fs.a-level.com.ua")
 
-if (localStorage.authToken) {
-   socket.emit('jwt', localStorage.authToken)
-} 
+export const socket = window.io("ws://chat.fs.a-level.com.ua")
+
+// if (localStorage.authToken) {
+//    socket.emit('jwt', localStorage.authToken)
+// }
+// socket.disconnect()
 
 socket.on('jwt_ok',   (data) => console.log(data))
 socket.on('jwt_fail', (error) => console.log(error))