Ivar пре 3 година
родитељ
комит
0078fb66c9

+ 216 - 0
package-lock.json

@@ -25,6 +25,7 @@
         "redux": "^4.1.2",
         "redux-thunk": "^2.4.1",
         "sass": "^1.45.1",
+        "socket.io-client": "^4.4.1",
         "web-vitals": "^2.1.2"
       }
     },
@@ -3203,6 +3204,19 @@
         "@sinonjs/commons": "^1.7.0"
       }
     },
+    "node_modules/@socket.io/base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/@socket.io/component-emitter": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
+      "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
+    },
     "node_modules/@surma/rollup-plugin-off-main-thread": {
       "version": "2.2.3",
       "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
@@ -5069,6 +5083,11 @@
         "babel-plugin-transform-react-remove-prop-types": "^0.4.24"
       }
     },
+    "node_modules/backo2": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+      "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -6607,6 +6626,53 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/engine.io-client": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
+      "integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.0.0",
+        "debug": "~4.3.1",
+        "engine.io-parser": "~5.0.0",
+        "has-cors": "1.1.0",
+        "parseqs": "0.0.6",
+        "parseuri": "0.0.6",
+        "ws": "~8.2.3",
+        "xmlhttprequest-ssl": "~2.0.0",
+        "yeast": "0.1.2"
+      }
+    },
+    "node_modules/engine.io-client/node_modules/ws": {
+      "version": "8.2.3",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
+      "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/engine.io-parser": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
+      "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
+      "dependencies": {
+        "@socket.io/base64-arraybuffer": "~1.0.2"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
     "node_modules/enhanced-resolve": {
       "version": "5.8.3",
       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
@@ -8353,6 +8419,11 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/has-cors": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+      "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+    },
     "node_modules/has-flag": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -12034,6 +12105,16 @@
       "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
       "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
     },
+    "node_modules/parseqs": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
+      "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
+    },
+    "node_modules/parseuri": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
+      "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
+    },
     "node_modules/parseurl": {
       "version": "1.3.3",
       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -14678,6 +14759,34 @@
         "node": ">=8"
       }
     },
+    "node_modules/socket.io-client": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
+      "integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.0.0",
+        "backo2": "~1.0.2",
+        "debug": "~4.3.2",
+        "engine.io-client": "~6.1.1",
+        "parseuri": "0.0.6",
+        "socket.io-parser": "~4.1.1"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/socket.io-parser": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.1.tgz",
+      "integrity": "sha512-USQVLSkDWE5nbcY760ExdKaJxCE65kcsG/8k5FDGZVVxpD1pA7hABYXYkCUvxUuYYh/+uQw0N/fvBzfT8o07KA==",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.0.0",
+        "debug": "~4.3.1"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
     "node_modules/sockjs": {
       "version": "0.3.24",
       "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
@@ -16714,6 +16823,14 @@
       "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
       "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
     },
+    "node_modules/xmlhttprequest-ssl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
+      "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/xtend": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@@ -16768,6 +16885,11 @@
         "node": ">=10"
       }
     },
+    "node_modules/yeast": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+      "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+    },
     "node_modules/yocto-queue": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -18918,6 +19040,16 @@
         "@sinonjs/commons": "^1.7.0"
       }
     },
+    "@socket.io/base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ=="
+    },
+    "@socket.io/component-emitter": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
+      "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
+    },
     "@surma/rollup-plugin-off-main-thread": {
       "version": "2.2.3",
       "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
@@ -20336,6 +20468,11 @@
         "babel-plugin-transform-react-remove-prop-types": "^0.4.24"
       }
     },
+    "backo2": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+      "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+    },
     "balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -21487,6 +21624,38 @@
       "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
       "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
     },
+    "engine.io-client": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
+      "integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
+      "requires": {
+        "@socket.io/component-emitter": "~3.0.0",
+        "debug": "~4.3.1",
+        "engine.io-parser": "~5.0.0",
+        "has-cors": "1.1.0",
+        "parseqs": "0.0.6",
+        "parseuri": "0.0.6",
+        "ws": "~8.2.3",
+        "xmlhttprequest-ssl": "~2.0.0",
+        "yeast": "0.1.2"
+      },
+      "dependencies": {
+        "ws": {
+          "version": "8.2.3",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
+          "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
+          "requires": {}
+        }
+      }
+    },
+    "engine.io-parser": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
+      "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
+      "requires": {
+        "@socket.io/base64-arraybuffer": "~1.0.2"
+      }
+    },
     "enhanced-resolve": {
       "version": "5.8.3",
       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
@@ -22749,6 +22918,11 @@
       "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
       "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA=="
     },
+    "has-cors": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+      "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+    },
     "has-flag": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -25406,6 +25580,16 @@
       "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
       "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
     },
+    "parseqs": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
+      "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
+    },
+    "parseuri": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
+      "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
+    },
     "parseurl": {
       "version": "1.3.3",
       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -27201,6 +27385,28 @@
       "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
       "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
     },
+    "socket.io-client": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
+      "integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
+      "requires": {
+        "@socket.io/component-emitter": "~3.0.0",
+        "backo2": "~1.0.2",
+        "debug": "~4.3.2",
+        "engine.io-client": "~6.1.1",
+        "parseuri": "0.0.6",
+        "socket.io-parser": "~4.1.1"
+      }
+    },
+    "socket.io-parser": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.1.tgz",
+      "integrity": "sha512-USQVLSkDWE5nbcY760ExdKaJxCE65kcsG/8k5FDGZVVxpD1pA7hABYXYkCUvxUuYYh/+uQw0N/fvBzfT8o07KA==",
+      "requires": {
+        "@socket.io/component-emitter": "~3.0.0",
+        "debug": "~4.3.1"
+      }
+    },
     "sockjs": {
       "version": "0.3.24",
       "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
@@ -28753,6 +28959,11 @@
       "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
       "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
     },
+    "xmlhttprequest-ssl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
+      "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="
+    },
     "xtend": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@@ -28792,6 +29003,11 @@
       "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
       "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
     },
+    "yeast": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+      "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+    },
     "yocto-queue": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

+ 1 - 0
package.json

@@ -20,6 +20,7 @@
     "redux": "^4.1.2",
     "redux-thunk": "^2.4.1",
     "sass": "^1.45.1",
+    "socket.io-client": "^4.4.1",
     "web-vitals": "^2.1.2"
   },
   "scripts": {

+ 9 - 14
src/App.js

@@ -30,20 +30,16 @@ const Page404 = () => (
 
 
 
-const RRoute = ({ action, component:Component, ...routeProps}) => {
-  const WrapperComponent = (componentProps) => {
-    action(componentProps.match)
-    return <Component {...componentProps} />
-  }
-  return <Route {...routeProps} component={WrapperComponent} />
-}
-const CRRoute = connect(null, {action: match => ({type: 'ROUTE', match})})(RRoute)
+// const RRoute = ({ action, component:Component, ...routeProps}) => {
+//   const WrapperComponent = (componentProps) => {
+//     action(componentProps.match)
+//     return <Component {...componentProps} />
+//   }
+//   return <Route {...routeProps} component={WrapperComponent} />
+// }
+// const CRRoute = connect(null, {action: match => ({type: 'ROUTE', match})})(RRoute)
+
 
-// const ProtectedRoute = ({ fallback='/',
-//                           roles=["admin"],
-//                           auth,
-                          
-// })
 
 
 
@@ -56,7 +52,6 @@ const AuthSwitch = ({token}) => {
       <>      
       <Switch> 
         <Route path="/main" component={Main} exact/>
-        <Redirect from="/login" to="/main" />
         <Route path="*" component={Main} /> 
       </Switch>
       </>

+ 4 - 1
src/actions/authActions.js

@@ -70,6 +70,8 @@ import {
       }`, { user: {_id: userId, login: newLogin, nick: newNick } }))
    )
 
+
+
    export const actionSetUserInfo = (name, file, newLogin, newNick) => (
       async (dispatch, getState) => {
          let {auth} = getState()
@@ -83,6 +85,7 @@ import {
       }
    )
 
+   
 
    const actionChangePass = (_id, password) => (
       actionPromise('register', gql(`mutation reg($user:UserInput) {
@@ -109,7 +112,7 @@ import {
 
    // так же нужен setUserChats
       // дописать
-   // происходит когда юзер уходит сам, иначе в чат добавляются юзерю, а не наоборот
+   // происходит когда юзер уходит сам, иначе в чат добавляются юзер, а не наоборот
    const actionUpdateUserCahts = (userId, newChats) => (
       actionPromise('updateUser', gql(`mutation updateUser($user:UserInput) {
          UserUpsert(user:$user) {

+ 18 - 32
src/actions/chatsActions.js

@@ -1,31 +1,14 @@
 import {gql} from '../helpers'
 import {   
    actionPromise, 
-   actionAddListInChats,
-   actionAddChatInChats, 
-   actionUpdateChatInChats,
-   actionRemoveChatInChats,
+   actionChatList,
+   actionChatOne,
+   actionChatLeft
 } from '../reducers'
 
 
-
-export const actionAddChat = (title) => (
-   actionPromise('addChat', gql(`mutation addChat($chat:ChatInput) {
-      ChatUpsert(chat:$chat) {
-         _id     
-         owner {_id login}       
-         title
-         members {
-            _id
-            login
-         }      
-      }
-   }`, { chat: { title } }
-   ))
-)
-
 // в массив newMemders передавать объекты только с полем _id
-export const actionUpdateChat = (chatId, newTitle, newMemders) => (
+export const actionUpdateChat = (title, members, chatId) => (
    actionPromise('updateChat', gql(`mutation updateChat($chat:ChatInput) {
       ChatUpsert(chat:$chat) {
          _id     
@@ -36,17 +19,10 @@ export const actionUpdateChat = (chatId, newTitle, newMemders) => (
             login
          }      
       }
-   }`, { chat: {_id: chatId, title: newTitle, members: newMemders} }))
+   }`, { chat: {_id: chatId, title, members} }))
 )
 
-// не работает
-// export const actionRemoveChat = (chatId) => (
-//    actionPromise('removeChat', gql(`mutation removeChat($chat:ChatInput) {
-//       ChatDelete(chat:$chat) {
-//          _id     
-//       }
-//    }`, { chat: {_id: chatId} }))
-// )
+
 
 
 // поиск по значению в массиве объектов - { 'members._id': userId }
@@ -98,7 +74,7 @@ export const actionFullChatList = (userId, currentCount, limitCount=50) => (
    async (dispatch) => {
       let payload = await dispatch(actionGetChatsByUser(userId, currentCount, limitCount))
       if (payload) {
-         dispatch(actionAddListInChats(payload))
+         await dispatch(actionChatList(payload))
       }
    }
 )
@@ -147,4 +123,14 @@ export const actionGetAllChats = (userId) => (
          q: JSON.stringify([ { 'members._id': userId }, { skip: [0], limit: [100] } ])
          }
    ))
-)
+)
+
+
+// не работает
+// export const actionRemoveChat = (chatId) => (
+//    actionPromise('removeChat', gql(`mutation removeChat($chat:ChatInput) {
+//       ChatDelete(chat:$chat) {
+//          _id     
+//       }
+//    }`, { chat: {_id: chatId} }))
+// )

+ 34 - 7
src/actions/index.js

@@ -1,12 +1,11 @@
 // далекие от редьюсеров экшены 
-export {
+import {
    actionFullLogin, 
    actionFullRegister, 
    actionSetUserInfo,
    actionSetUserPass,
 } from './authActions'
-export {
-   actionAddChat, 
+import {
    actionUpdateChat,
    actionGetChatsByUser,
    actionGetAllChats,
@@ -14,22 +13,50 @@ export {
    actionFullChatList
 } from './chatsActions'
 
-export {
+import {
    actionGetMsgsByChat,
-   actionMsgsCount
+   actionMsgsCount,
+   actionSendMsg,
 } from './msgActions'
 
-export {
+import {
    actionUploadFile,
 } from './mediaActions'
 
-export {
+import {
    actionFindUsers
 } from './findActions'
 
 
 
 
+export {
+   actionFullLogin, 
+   actionFullRegister, 
+   actionSetUserInfo,
+   actionSetUserPass,
+} 
+export {
+   actionUpdateChat,
+   actionGetChatsByUser,
+   actionGetAllChats,
+
+   actionFullChatList
+} 
+
+export {
+   actionGetMsgsByChat,
+   actionMsgsCount,
+   actionSendMsg,
+} 
+
+export {
+   actionUploadFile,
+} 
+
+export {
+   actionFindUsers
+} 
 
 
 

+ 29 - 59
src/actions/msgActions.js

@@ -1,9 +1,8 @@
 import {gql} from '../helpers'
 import {   
    actionPromise,
-   actionMsgListInChat, 
-   actionAddMsgInChat,
-   actionUpdateMsgInChat,
+   actionMsgList,
+   actionMsgOne
 } from '../reducers'
 import {   
    actionUploadFile
@@ -52,6 +51,8 @@ export const actionGetMsgsByChat = (chatId, skipCount=0, limitCount=50) => (
 )
 
 
+
+
 export const actionMsgsCount = (chatId) => (
    actionPromise('msgsCount', gql(`query msgsCount($q: String) {
       MessageCount (query: $q)  
@@ -63,9 +64,8 @@ export const actionMsgsCount = (chatId) => (
 
 
 
-
-export const actionAddMsg = (text) => (
-   actionPromise('addMsg', gql(`mutation addMsg($msg: MessageInput) {
+const actionUpdateMsg = (chatId, text, media, msgId) => (
+   actionPromise('updateMsg', gql(`mutation updateMsg($msg: MessageInput) {
       MessageUpsert(message: $msg) {
          _id
          createdAt
@@ -92,71 +92,41 @@ export const actionAddMsg = (text) => (
             _id
          }
       }
-   }`, { msg: { text } }
+   }`, { msg: { _id: msgId, text, chat: {_id: chatId}, media } }
    ))
 )
 
-export const actionUpdateMsg = (msgId, text) => (
-   actionPromise('updateMsg', gql(`mutation updateMsg($msg: MessageInput) {
-      MessageUpsert(message: $msg) {
-         _id
-         createdAt
-         owner {
-            _id
-            login
-         }
-         text
 
-         media {
-            url
-         }
-         forwardWith {
-            _id
-         }
-         replies {
-            _id
-         }
+// медиа - массив объектов с ид медиа
+export const actionSendMsg = (chatId, text, inputName, files, msgId) => (
+   async (dispatch, getState) => {
 
-         replyTo {
-            _id
-         }
-         forwarded {
-            _id
+// тут нужно отделить уже залитые файлы от тех которые лежат локально
+// локальные залить и получить ид, с залитых просто получить ид
+      const media = []
+      for (const file of files) {
+         if (file.preview.match(/blob/)) {
+            let fileObj = await dispatch(actionUploadFile(inputName, file))
+            media.push({_id: fileObj?._id})
+         } else {
+            media.push({})
          }
       }
-   }`, { msg: { _id: msgId, text } }
-   ))
-)
 
-export const actionRemoveMsg = (msgId) => (
-   actionPromise('removeMsg', gql(`mutation removeMsg($msg: MessageInput) {
-      MessageDelete(message: $msg) {
-         _id 
-      }
-   }`, { msg: { _id: msgId } }
-   ))
+      await dispatch(actionUpdateMsg(chatId, text, media, msgId))
+   }
 )
 
 
 
-
-
-
-
-
-
    
-      // !!!! ПОДХОД ВЕРНЫЙ ТОЛЬКО ДЛЯ ЗАГРУЗКИ МНОГИХ ФАЙЛОВ В СООБЩЕНИЕ - перенести в сообщения
-   // позволяет загрузить файл и взамен получить url  
-   // нужен для предпросмотра, когда юзер еще не определился нужно ли добавлять этот url в стейт
-   export const actionGroupFiles = (name, file) => (
-      async (dispatch, getState) => {
-         let fileObj = await dispatch(actionUploadFile(name, file))
-         let URL = fileObj.avatar.url
-         console.log(fileObj._id)
-         let {auth} = getState()
-         let userId = auth.payload?.sub?.id  
-      }
-   )
+   // const actionRemoveMsg = (msgId) => (
+   //    actionPromise('removeMsg', gql(`mutation removeMsg($msg: MessageInput) {
+   //       MessageDelete(message: $msg) {
+   //          _id 
+   //       }
+   //    }`, { msg: { _id: msgId } }
+   //    ))
+   // )
 
 

+ 113 - 0
src/components/Avatar.jsx

@@ -0,0 +1,113 @@
+import React from 'react';
+import Avatar from '@mui/material/Avatar';
+
+import {backURL}  from '../helpers'
+import {connect}  from 'react-redux'
+
+
+const small = {
+  height: '40px', 
+  width: '40px'
+}
+
+const big = {
+  height: '70px', 
+  width: '70px'
+}
+
+
+function stringToColor(string) {
+  if (!string) {
+    return '#' + Math.floor(Math.random()*16777215).toString(16);
+  }
+  let hash = 0;    
+  for (let i = 0; i < string?.length; i += 1) {
+    hash = string.charCodeAt(i) + ((hash << 5) - hash);
+  }    
+  let color = '#'; 
+  for (let i = 0; i < 3; i += 1) {
+    const value = (hash >> (i * 8)) & 0xff;
+    color += `00${value.toString(16)}`.substr(-2);
+  }   
+  return color;
+}
+
+function stringSplit(str) {
+  if (!str) {
+    return 
+  }
+  let titleStr = ''
+  let letterAmount = 2
+  for (const word of str.split(' ')) {
+    if (letterAmount <= 0) {
+      break
+    }
+    letterAmount--
+    if (word) {
+      titleStr += word[0].toUpperCase()
+    }  
+  }
+  return titleStr
+}
+
+function stringAvatar(name) { 
+  return {
+    sx: {
+      bgcolor: stringToColor(name),
+    },
+    children: stringSplit(name)
+  };
+}
+
+
+export const UserAvatar = ({  profile, bigSize=false }) => {
+  //  console.log(profile)
+   
+    function getUrl() {
+      if (profile.localUrl) {
+        return profile.localUrl
+      } else if (profile.avatar?.url) {
+        return backURL + profile.avatar?.url
+      } else {
+        return false
+      }
+    }
+
+   return (
+      <>
+      {
+         getUrl() ?
+         <Avatar  sx={ bigSize ? big : small } 
+                  alt={profile.nick || profile.login } src={getUrl()} /> :
+         <Avatar  sx={ bigSize ? big : small }
+                  {...stringAvatar(profile.nick || profile.login)} /> 
+      }        
+      </>
+   ) 
+}
+export const CUserAvatar = connect( state => ({profile: state.promise.myProfile?.payload || {}}))(UserAvatar)
+
+
+
+export const ChatAvatar = ({ chat, bigSize=false }) => {
+   
+    function getUrl() {
+       if (chat.avatar?.url) {
+        return backURL + chat.avatar?.url
+      } else {
+        return false
+      }
+    }
+
+   return (
+      <>
+      {
+         getUrl() ?
+         <Avatar  sx={ bigSize ? big : small } 
+                  alt={chat.title } src={getUrl()} /> :
+         <Avatar  sx={ bigSize ? big : small }
+                  {...stringAvatar(chat.title)} /> 
+      }        
+      </>
+   ) 
+}

+ 9 - 9
src/components/ChatList.js

@@ -4,25 +4,25 @@ import List from '@mui/material/List';
 import ListItem from '@mui/material/ListItem';
 import ListItemText from '@mui/material/ListItemText';
 import ListItemAvatar from '@mui/material/ListItemAvatar';
-import Avatar from '@mui/material/Avatar';
 import Box from '@mui/material/Box';
 
-import {FloatBtn} from "../components"
+import {FloatBtn, ChatAvatar} from "../components"
 import {connect}  from 'react-redux'
 import {
   actionFullChatList
 } from "../actions"
  
-const Chat = ({chat:{_id, title, owner, members, messages, avatar}}) => {
+const Chat = ({chat}) => {
+
+  let {_id, title, owner, members} = chat
+
   return (
-    <Link href={`/main/chat/${_id}`}>
+    <Link href={`/main/${_id}`}>
       <ListItem >
         <ListItemAvatar>
-          <Avatar>
-
-          </Avatar>
+          <ChatAvatar chat={chat} />
         </ListItemAvatar>
-        <ListItemText primary={title} secondary={messages && messages[messages.length-1]?.text} />
+        <ListItemText primary={title} secondary={'затычка'} />
       </ListItem>
     </Link>
   )
@@ -34,7 +34,7 @@ const ChatList = ({chats, userId, getData}) => {
 
   useEffect(() => {
     getData(userId, chatBlock, 20)
-  },[userId])
+  },[])
 
   return (
     <List sx={{ width: '100%', bgcolor: 'background.paper', position: 'relative', zIndex: 2 }}>

src/components/FloatBtn.js → src/components/FloatBtn.jsx


src/components/Header.js → src/components/Header.jsx


+ 1 - 1
src/components/MainMenu.js

@@ -15,7 +15,7 @@ import ListItemText from '@mui/material/ListItemText';
 import {actionAuthLogout} from "../reducers"
 import {connect}  from 'react-redux';
 
-import {CProfileModal, CUserAvatar} from '../components'
+import {CProfileModal, CUserAvatar} from '.'
 
 
 const LogoutBtn = ({onLogout}) => (

+ 0 - 8
src/components/MsgList.js

@@ -1,8 +0,0 @@
-
-
-
-export const Msg = () => {
-   return (
-      <></>
-   )
-}

+ 10 - 0
src/components/MsgList.jsx

@@ -0,0 +1,10 @@
+import React, {useState, useEffect, useRef} from 'react';
+import {connect}  from 'react-redux'
+
+
+const MsgList = ({}) => {
+   return (
+      <></>
+   )
+}
+export const CMsgList = connect(state => ({chats: state.chats}))(MsgList)

src/components/Preload.js → src/components/Preload.jsx


+ 9 - 44
src/components/ProfileModal.js

@@ -11,9 +11,9 @@ import CloseIcon from '@mui/icons-material/Close';
 
 import {useDropzone} from 'react-dropzone';
 
-import {UserAvatar} from '../components'
+import {UserAvatar} from '.'
 
-import { printStrReq } from "../helpers";
+import { printStrReq, passReq } from "../helpers";
 import {connect}  from 'react-redux'
 import {actionSetUserInfo, actionSetUserPass} from '../actions'
 
@@ -35,9 +35,8 @@ const styleModalParrent = {
 }
 
 
-const PassModal = ({ onСonfirm, minPass='2', 
-                     char=true, bigChar=false, 
-                     number=false, regError}) => {
+const PassModal = ({ onСonfirm, regError}) => {
+   
    const [open, setOpen] = useState(false);
    const handleOpen = () => {
      setOpen(true);
@@ -64,40 +63,6 @@ const PassModal = ({ onСonfirm, minPass='2',
    },[regError])
 
 
-   const checkPass = (password) => {
-      if (  password.length >= minPass && 
-            !password.match(/\s/) && 
-            (char ? password.match(/[a-zа-яё]/) : true) && 
-            (bigChar ? password.match(/[A-ZА-ЯЁ]/) : true) && 
-            (number ? password.match(/[\d]/) : true) ) {
-      return true
-      } else {
-         return false
-      }
-   }
-
-   const printPassReq = (password) => {
-      let str = ''
-      if (checkPass(password)) {
-         str += ' '
-      } else if (password.match(/\s/)) {
-         str += 'Пароль не должен содержать пробелы '
-      } else {
-         str += 'Пароль должен содержать: '
-         str += (printStrReq(password, minPass) ? printStrReq(password, minPass) + ',' : '')
-         if (!(char ? password.match(/[a-zа-яё]/) : true)) {
-            str += ' строчные буквы,'
-         }
-         if (!(bigChar ? password.match(/[A-ZА-ЯЁ]/) : true)) {
-            str += ' прописные буквы,'
-         }
-         if  (!(number ? password.match(/[\d]/) : true)) {
-            str += ' цифры,'
-         }
-      }
-      return str.slice(0, -1)
-   }
- 
    return (
      <>
       <Button variant="text" color="error" onClick={handleOpen} >
@@ -137,8 +102,8 @@ const PassModal = ({ onСonfirm, minPass='2',
                         setPassBlur(false)              
                      }
                   }
-                  error={passBlur ? (checkPass(pass) ? false : true) : false}               
-                  helperText={printPassReq(pass)}            
+                  error={passBlur ? (passReq.checkPass(pass) ? false : true) : false}               
+                  helperText={passReq.printPassReq(pass)}            
    
                   inputProps={{
                      maxLength: 100
@@ -173,8 +138,8 @@ const PassModal = ({ onСonfirm, minPass='2',
                         setPass2Blur(false)              
                      }
                   }
-                  error={pass2Blur ? (checkPass(pass2) ? false : true) : false}               
-                  helperText={printPassReq(pass2)}            
+                  error={pass2Blur ? (passReq.checkPass(pass2) ? false : true) : false}               
+                  helperText={passReq.printPassReq(pass2)}            
    
                   inputProps={{
                      maxLength: 100
@@ -202,7 +167,7 @@ const PassModal = ({ onСonfirm, minPass='2',
 
             <Box sx={{ display: 'flex', justifyContent: 'end', mt: 2}}>
                <Button  variant="contained" 
-                        disabled={( checkPass(pass) && 
+                        disabled={( passReq.checkPass(pass) && 
                                     pass === pass2 ) ? false : true}
 
                         onClick={() => onСonfirm(pass)}>

src/components/SearchBlock.js → src/components/SearchBlock.jsx


+ 24 - 23
src/components/SendingField.js

@@ -10,7 +10,7 @@ import {useDropzone} from 'react-dropzone';
 
 import {connect}  from 'react-redux'
 import {
-    actionUploadFile,
+   actionSendMsg
 } from "../actions"
 
 const containerWrapp = {
@@ -66,9 +66,7 @@ const thumbsContainer = {
    height: '100%'
  };
 
-const MsgDropZone = ({onText, setFiles}) => {
-
-   const [localFiles, setLocalFiles] = useState([]);
+const MsgDropZone = ({setText, setFiles, files}) => {
 
    const {
       acceptedFiles,
@@ -80,28 +78,31 @@ const MsgDropZone = ({onText, setFiles}) => {
 
             console.log(acceptedFiles)
 
-         await setLocalFiles([...localFiles, 
-            ...acceptedFiles.map(file => Object.assign(file, {
-               preview: URL.createObjectURL(file)
-            }))
-         ]);   
+            await setFiles([...files, 
+               ...acceptedFiles.map(file => Object.assign(file, {
+                  preview: URL.createObjectURL(file)
+               }))
+            ])
 
-         await acceptedFiles.map(file => {
-            setFiles(file)
-         })
-     
          }
       });
 
-      console.log(localFiles)
+   // console.log(files)
+
+
+
+
+   const onDelete = (i) => {
+      setFiles( files.filter((el, index) => index !== i) )
+   }
 
-   const thumbs = localFiles.map((file, index) => (
+   const thumbs = files.map((file, i) => (
       <div style={thumb} key={file.preview}>         
          <div style={closeBtn}>
             <CloseIcon 
                fontSize="small" 
                sx={{ cursor: 'pointer' }}
-               onClick={() => {}} 
+               onClick={() => onDelete(i)} 
             />
          </div>         
          <div style={thumbInner}>           
@@ -111,12 +112,12 @@ const MsgDropZone = ({onText, setFiles}) => {
             /> 
          </div>     
       </div>
-      ));
+   ));
    
    // useEffect(() => {
    // // Make sure to revoke the data uris to avoid memory leaks
-   // localFiles.forEach(file => URL.revokeObjectURL(file.preview));
-   // }, [localFiles]);
+   // files.forEach(file => URL.revokeObjectURL(file.preview));
+   // }, [files]);
 
   return (
    <>
@@ -139,7 +140,7 @@ const MsgDropZone = ({onText, setFiles}) => {
                      placeholder="Сообщение"
                      style={{ width: '100%' }}
                      onClick={(e) => e.stopPropagation()}
-                     onChange={() => onText()}
+                     onChange={(e) => setText(e.target.value)}
                   />
             </div>
 
@@ -158,13 +159,13 @@ const SendingField = ({onSend}) => {
       <Box sx={{ display: 'flex', alignItems: 'center', height: '100%', width: '100%'}} >
       
          <Box sx={{ flexGrow: 1, flexShrink: 1, flexBasis: '80%', overflow: 'auto', height: '100%', backgroundColor: '#fff' }}>
-            <MsgDropZone onText={setText} onFile={setFiles} />
+            <MsgDropZone setText={setText} setFiles={setFiles} files={files} />
          </Box>         
          <Box sx={{ flexGrow: 1, flexShrink: 0, flexBasis: '50px'}}>
             <Button 
                variant="contained" 
                endIcon={<SendIcon />} 
-               onClick={() => onSend(text, files)}
+               onClick={() => onSend(null, text, "media", files)}
             >
                Отправить
             </Button>
@@ -173,4 +174,4 @@ const SendingField = ({onSend}) => {
       </Box>
    )
 }
-export const CSendingField= connect( null, {onSend: actionUploadFile})(SendingField)
+export const CSendingField= connect( null, {onSend: actionSendMsg})(SendingField)

+ 0 - 85
src/components/UserAvatar.js

@@ -1,85 +0,0 @@
-import React from 'react';
-import Avatar from '@mui/material/Avatar';
-
-import {backURL}  from '../helpers'
-import {connect}  from 'react-redux'
-
-
-export const UserAvatar = ({  profile, bigSize=false }) => {
-  //  console.log(profile)
-   
-   function stringToColor(string) {
-      if (!string) {
-        return '#' + Math.floor(Math.random()*16777215).toString(16);
-      }
-      let hash = 0;
-    
-      /* eslint-disable no-bitwise */
-      for (let i = 0; i < string?.length; i += 1) {
-        hash = string.charCodeAt(i) + ((hash << 5) - hash);
-      }
-    
-      let color = '#';
-    
-      for (let i = 0; i < 3; i += 1) {
-        const value = (hash >> (i * 8)) & 0xff;
-        color += `00${value.toString(16)}`.substr(-2);
-      }
-      /* eslint-enable no-bitwise */
-    
-      return color;
-    }
-
-    function stringSplit(str) {
-      if (!str) {
-        return 
-      }
-      let titleStr = ''
-      let letterAmount = 2
-      for (const word of str.split(' ')) {
-        if (letterAmount <= 0) {
-          break
-        }
-        letterAmount--
-        if (word) {
-          titleStr += word[0].toUpperCase()
-        }  
-      }
-      return titleStr
-    }
-    
-    function stringAvatar(name) { 
-      return {
-        sx: {
-          bgcolor: stringToColor(name),
-        },
-        children: stringSplit(name)
-      };
-    }
-
-
-    function getUrl() {
-      if (profile.localUrl) {
-        return profile.localUrl
-      } else if (profile.avatar?.url) {
-        return backURL + profile.avatar?.url
-      } else {
-        return false
-      }
-    }
-
-
-   return (
-      <>
-      {
-         getUrl() ?
-         <Avatar  sx={ bigSize ? { height: '70px', width: '70px' } : { height: '40px', width: '40px' } } 
-                  alt={profile.nick || profile.login } src={getUrl()} /> :
-         <Avatar  sx={ bigSize ? { height: '70px', width: '70px' } : { height: '40px', width: '40px' } }
-                  {...stringAvatar(profile.nick || profile.login)} /> 
-      }        
-      </>
-   ) 
-}
-export const CUserAvatar = connect( state => ({profile: state.promise.myProfile?.payload || {}}))(UserAvatar)
-

+ 20 - 10
src/components/index.js

@@ -1,16 +1,26 @@
 // универсальные компоненты, повторяющиеся на многих страницах
-export {CChatList} from './ChatList'
-export {Msg} from './MsgList'
-export {CPreloaded} from './Preload'
-export {MainMenu, MenuDrawer} from './MainMenu'
-export {Header} from './Header'
-export {SearchBlock} from './SearchBlock'
-export {FloatBtn} from './FloatBtn'
-export {UserAvatar, CUserAvatar} from './UserAvatar'
-export {CProfileModal} from './ProfileModal'
+import {CChatList} from './ChatList'
+import {} from './MsgList'
+import {CPreloaded} from './Preload'
+import {MainMenu, MenuDrawer} from './MainMenu'
+import {Header} from './Header'
+import {SearchBlock} from './SearchBlock'
+import {FloatBtn} from './FloatBtn'
+import {ChatAvatar, UserAvatar, CUserAvatar} from './Avatar'
+import {CProfileModal} from './ProfileModal'
 
-export {CSendingField} from './SendingField'
+import {CSendingField} from './SendingField'
 
 
 
+export {CChatList}
+export {} 
+export {CPreloaded} 
+export {MainMenu, MenuDrawer} 
+export {Header}  
+export {SearchBlock}  
+export {FloatBtn}  
+export {ChatAvatar, UserAvatar, CUserAvatar} 
+export {CProfileModal} 
 
+export {CSendingField}  

+ 9 - 2
src/helpers/index.js

@@ -1,3 +1,10 @@
 // вспомогательные функции
-export {backURL, gql} from './getGql'
-export {printStrReq} from './printStrReq'
+import {backURL, gql} from './getGql'
+import {printStrReq} from './printStrReq'
+import {passReq} from './passReq'
+
+
+
+export {backURL, gql} 
+export {printStrReq} 
+export {passReq} 

+ 46 - 0
src/helpers/passReq.js

@@ -0,0 +1,46 @@
+
+import { printStrReq } from '.'
+
+export const passReq = {
+
+   minPass:'2', 
+   char:false, 
+   bigChar:false, 
+   number:false,
+
+   checkPass(password) {
+      if (  password.length >= this.minPass && 
+            !password.match(/\s/) && 
+            (this.char ? password.match(/[a-zа-яё]/) : true) && 
+            (this.bigChar ? password.match(/[A-ZА-ЯЁ]/) : true) && 
+            (this.number ? password.match(/[\d]/) : true) ) {
+      return true
+      } else {
+         return false
+      }
+   },
+   
+   printPassReq(password) {
+      let str = ''
+      if (this.checkPass(password)) {
+         str += 'Пароль подходит '
+      } else if (password.match(/\s/)) {
+         str += 'Пароль не должен содержать пробелы '
+      } else {
+         str += 'Пароль должен содержать: '
+         str += printStrReq(password, this.minPass) + ','
+         if (!(this.char ? password.match(/[a-zа-яё]/) : true)) {
+            str += ' строчные буквы,'
+         }
+         if (!(this.bigChar ? password.match(/[A-ZА-ЯЁ]/) : true)) {
+            str += ' прописные буквы,'
+         }
+         if  (!(this.number ? password.match(/[\d]/) : true)) {
+            str += ' цифры,'
+         }
+      }
+      return str.slice(0, -1)
+   }
+
+}
+

src/pages/Login.js → src/pages/Login.jsx


+ 21 - 3
src/pages/Main.js

@@ -12,13 +12,25 @@ import { createTheme, ThemeProvider } from '@mui/material/styles';
 
 
 
-import {MenuDrawer, SearchBlock, CChatList, Header, CUserAvatar, CSendingField} from "../components"
+import {
+   MenuDrawer, 
+   SearchBlock, 
+   CChatList, 
+   Header, 
+   CUserAvatar, 
+   CSendingField
+} from "../components"
 import {
    actionChangePass,
    actionAddChat
  } from "../actions"
  import {store} from "../reducers"
 
+
+ const Page404 = () => (
+   <h1>Тут ведутся работы</h1>
+   )
+
 const theme = createTheme()
 export const Main = ({}) => {
 
@@ -68,8 +80,14 @@ export const Main = ({}) => {
                      </Grid>
 
                      <Grid item sx={{ flexGrow: 1 }} >
-                        сообщения
-                        
+
+
+                           
+             {/*            <Switch> 
+                           <Route path="/main/:_id" component={} />
+                           <Route path="*" component={} /> 
+                        </Switch> */}
+
                      </Grid>
 
                      <Grid item  sx={{ flexGrow: 0 }} height="max-content" > 

+ 7 - 41
src/pages/Register.js

@@ -15,10 +15,10 @@ import { createTheme, ThemeProvider } from '@mui/material/styles';
 
 import {connect}  from 'react-redux'
 import { actionFullRegister } from "../actions";
-import { printStrReq } from "../helpers";
+import { printStrReq, passReq } from "../helpers";
 
 
-const RegisterForm = ({minLog='2', minPass='2', char=false, bigChar=false, number=false, onRegister, regError}) => {
+const RegisterForm = ({minLog='2', onRegister, regError}) => {
    const [login, setLogin] = useState('')
    const [logBlur, setLogBlur] = useState(false)
 
@@ -49,40 +49,6 @@ const RegisterForm = ({minLog='2', minPass='2', char=false, bigChar=false, numbe
       onRegister(login, pass, nick)
    }
 
-  const checkPass = (password) => {
-      if (  password.length >= minPass && 
-            !password.match(/\s/) && 
-            (char ? password.match(/[a-zа-яё]/) : true) && 
-            (bigChar ? password.match(/[A-ZА-ЯЁ]/) : true) && 
-            (number ? password.match(/[\d]/) : true) ) {
-      return true
-      } else {
-         return false
-      }
-   }
-
-   const printPassReq = (password) => {
-      let str = ''
-      if (checkPass(password)) {
-         str += 'Пароль подходит '
-      } else if (password.match(/\s/)) {
-         str += 'Пароль не должен содержать пробелы '
-      } else {
-         str += 'Пароль должен содержать: '
-         str += printStrReq(password, minPass) + ','
-         if (!(char ? password.match(/[a-zа-яё]/) : true)) {
-            str += ' строчные буквы,'
-         }
-         if (!(bigChar ? password.match(/[A-ZА-ЯЁ]/) : true)) {
-            str += ' прописные буквы,'
-         }
-         if  (!(number ? password.match(/[\d]/) : true)) {
-            str += ' цифры,'
-         }
-      }
-      return str.slice(0, -1)
-   }
-
    return (
       <Box component="form" noValidate onSubmit={handleSubmit} sx={{ mt: 3 }}>
 
@@ -144,8 +110,8 @@ const RegisterForm = ({minLog='2', minPass='2', char=false, bigChar=false, numbe
                            setPassBlur(false)              
                         }
                      }
-                     error={passBlur ? (checkPass(pass) ? false : true) : false}               
-                     helperText={printPassReq(pass)}            
+                     error={passBlur ? (passReq.checkPass(pass) ? false : true) : false}               
+                     helperText={passReq.printPassReq(pass)}            
       
                      inputProps={{
                         maxLength: 100
@@ -175,8 +141,8 @@ const RegisterForm = ({minLog='2', minPass='2', char=false, bigChar=false, numbe
                            setPass2Blur(false)              
                         }
                      }
-                     error={pass2Blur ? (checkPass(pass2) ? false : true) : false}               
-                     helperText={printPassReq(pass2)}            
+                     error={pass2Blur ? (passReq.checkPass(pass2) ? false : true) : false}               
+                     helperText={passReq.printPassReq(pass2)}            
       
                      inputProps={{
                         maxLength: 100
@@ -209,7 +175,7 @@ const RegisterForm = ({minLog='2', minPass='2', char=false, bigChar=false, numbe
 
             <Button
                disabled={( login.length >= minLog && 
-                           checkPass(pass) && 
+                           passReq.checkPass(pass) && 
                            pass === pass2) ? false : true}
 
               type="submit"

+ 6 - 3
src/pages/index.js

@@ -1,5 +1,8 @@
 // уникальные страницы приложения
-export {Login} from './Login'
-export {Register} from './Register'
-export {Main} from './Main'
+import {Login} from './Login'
+import {Register} from './Register'
+import {Main} from './Main'
 
+export {Login} 
+export {Register} 
+export {Main} 

+ 86 - 78
src/reducers/chatsReducer.js

@@ -1,95 +1,103 @@
 
-
+// const chats = {
+//   chats: [ 
+//     {_id: '333', title: 'moiChat2', avatar: null, lastModified: 'segodnya', owner: {login: 'ya'}, members:[]},
+//     {_id: '222', title: 'moiChat', avatar: null, lastModified: 'vchera', owner: {login: 'ya'}, members:[]}
+//   ],
+//   messages: {
+//     '222': [
+//       {text: 'uuu'}, {text: 'uuuuuu'}
+//     ],
+//     '333': [
+//       {text: 'UUU'}, {text: 'U'}
+//     ]
+//   }
+// }
+
+// payload - или массив чатов или массив сообщений или 1 чат
  export function chatsReducer (state={}, {type, payload}) {
-   if (!state) {
-       return {}
-   }
-
-   const types = {
-      CHATS_LIST() {
-          let newPayload = {} 
-          for (const obj of payload) {
-            newPayload[obj._id] = obj
-          }
-        return {
-            ...state,
-            ...newPayload
-        }
-      },
-      CHAT_ADD() {
-        return {
-            ...state,
-            [payload._id]: payload
-        }
-      },
-      CHAT_CHANGE() {
-        return {
-            ...state,
-            [payload._id]: payload
-        }
-      },
-//       CHAT_REMOVE() {   
-//         let { [payload._id]: removed, ...newState }  = state        
-//         return newState
-//       },
-
-      MSG_LIST() {
-
-      },
-      MSG_ADD() {
-
-      },
-      MSG_CHANGE() {
-
-      },
+    if (!state) {
+        return {}
+    }
 
+    const types = {
 
-      SOCKET_CHAT_ADD() {
-
-      },
-      SOCKET_CHAT_CHANGE() {
+      CHATS() {
+        const newChats = {}        
+        for (const chat of payload) {
+          newChats[chat._id] = chat
+        }
 
+        const newState = Object.fromEntries(
+          Object.entries({
+            ...state,
+            ...newChats
+          }).sort((a, b) => {
+            if (a[1].lastModified > b[1].lastModified) {
+              return 1
+            }
+            if (a[1].lastModified < b[1].lastModified) {
+              return -1
+            }
+            return 0
+          })
+        )
+
+        return newState
       },
-      SOCKET_CHAT_REMOVE() {
 
+      CHAT_LEFT() {   
+        const { [payload._id]: removed, ...newState }  = state        
+        return newState
       },
 
-      SOCKET_MSG_ADD() {
+      MSGS() {
+        const chatId = payload[0]?.chat?._id
 
-      },
-      SOCKET_MSG_CHANGE() {
+        const msgState = [...state[chatId]?.messages, ...payload].sort((a, b) => {
+          if (a._id > b._id) {
+            return 1
+          }
+          if (a._id < b._id) {
+            return -1
+          }
+          return 0
+        })
 
-      },
-   }
-   if (type in types) {        
-      return types[type]()
-   }
-   return state
+        const newState = {
+          ...state,
+          [chatId]: {
+            messages: msgState
+          }
+        }
+        return newState
+      }
+
+    }
+    if (type in types) {        
+        return types[type]()
+    }
+    return state
  }
 
- export const actionAddListInChats = (payload) => 
-         ({type: 'CHATS_LIST', payload})
-
- export const actionAddChatInChats = (payload) => 
-         ({type: 'CHAT_ADD', payload})
-
- export const actionUpdateChatInChats = (payload) => 
-         ({type: 'CHAT_CHANGE', payload})
-
-//  export const actionRemoveChatInChats = (_id) => 
-//          ({type: 'CHAT_REMOVE', payload: {_id}})
-
-
-
-
- export const actionMsgListInChat = (payload) => 
-         ({type: 'MSG_LIST', payload})
-
- export const actionAddMsgInChat = (payload) => 
-         ({type: 'MSG_ADD', payload})
+  export const actionChatList = (chats) => (
+        {type: 'CHATS', payload: chats}
+  )     
+  export const actionChatOne = (chat) => (
+        {type: 'CHATS', payload: [chat]}
+  )
+  export const actionChatLeft = (chat) => (
+        {type: 'CHAT_LEFT', payload: chat}
+  )
+
+  export const actionMsgList = (msgs) => (
+        {type: 'MSGS', payload: msgs}
+  )
+  export const actionMsgOne = (msg) => (
+        {type: 'MSGS', payload: [msg]}
+  )
+      
 
- export const actionUpdateMsgInChat = (payload) => 
-         ({type: 'MSG_CHANGE', payload})
 
 
 

+ 35 - 17
src/reducers/index.js

@@ -1,35 +1,53 @@
 // работа со стейтом, подключение редьюсеров, близкие к редьюсерам экшны
-export {
+
+import {
     actionPromise
 } from './promiseReducer'
-export {
+import {
     actionAuthLogin, 
     actionAuthLogout
 } from './authReducer'
-export {
-    actionAddListInChats,
-    actionAddChatInChats, 
-    actionUpdateChatInChats, 
-    
-    actionMsgListInChat, 
-    actionAddMsgInChat,
-    actionUpdateMsgInChat,
+import {
+    actionChatList,
+    actionChatOne,
+    actionChatLeft,
+    actionMsgList,
+    actionMsgOne
 } from './chatsReducer'
-export {
+import {
     actionUserFindOne,
     actionAboutMe
 } from './findUserActions'
 
-export {store} from './store'
+import {store} from './store'
+
+
+
+
+
 
 
 
-// const socket = io("ws://chat.fs.a-level.com.ua")
+export {
+    actionPromise
+} 
+export {
+    actionAuthLogin, 
+    actionAuthLogout
+} 
+export {
+    actionChatList,
+    actionChatOne,
+    actionChatLeft,
+    actionMsgList,
+    actionMsgOne
+}
+export {
+    actionUserFindOne,
+    actionAboutMe
+} 
 
-// socket.emit('msg', {}) только при отправке токена 
-// socket.on('msg', msg => { 
-//  диспатч месседжей в чат редьюсер   
-// })
+export {store} 
 
 
 

+ 32 - 6
src/reducers/store.js

@@ -1,12 +1,18 @@
-import {createStore, combineReducers, applyMiddleware} from 'redux'
+import { createStore, combineReducers, applyMiddleware } from 'redux'
 import thunk from 'redux-thunk'
-import {promiseReducer} from './promiseReducer'
-import {authReducer} from './authReducer'
-import {chatsReducer} from './chatsReducer'
-import {localStoredReducer} from './localStoredReducer'
+import { promiseReducer } from './promiseReducer'
+import { authReducer } from './authReducer'
+import { chatsReducer } from './chatsReducer'
+import { localStoredReducer } from './localStoredReducer'
 
-import {actionAboutMe} from './findUserActions'
+import { actionAboutMe } from './findUserActions'
+import { 
+   actionMsgOne, 
+   actionChatOne, 
+   actionChatLeft 
+} from './chatsReducer'
 
+// const { io } = require("socket.io-client");
 
 export const store =  createStore (  combineReducers({ 
                                     // promise: localStoredReducer(promiseReducer, 'promise'),
@@ -24,6 +30,26 @@ 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)
+// } 
+
+// socket.on('jwt_ok',   (data) => console.log(data))
+// socket.on('jwt_fail', (error) => console.log(error))
+
+// socket.on('msg', (msg) => { 
+//     store.dispatch(actionMsgOne(msg)) 
+// })
+// socket.on('chat', (chat) => { 
+//     store.dispatch(actionChatOne(chat)) 
+// })
+// socket.on('chat_left', (chat) => { 
+//     store.dispatch(actionChatLeft(chat)) 
+// })
+
+
 // combineReducers({cart: localStoredReducer(cartReducer, 'cart'),
 //                  promise: localStoredReducer(promiseReducer, 'promise') })
 //для пользы при работе с промисами надо бы пока PENDING не делать payload undefined