Forráskód Böngészése

FINITA LA COMMEDIA

sheva77 3 éve
szülő
commit
ab781f827f

+ 3 - 1
README.md

@@ -7,4 +7,6 @@
 	- при создании чата: копию поля "createdAt"
 	- при добавлении в чат нового сообщения: дату создания этого сообщения
 
-2) При создании нового чата в "members" автоматически добавлять owner-а этого чата
+2) При создании нового чата в "members" автоматически добавлять owner-а этого чата
+
+3) В сообщения socket-а должна быть полная информация - достаточная для отображения сообщения на экране (что бы не дергать back лишними запросами)

+ 6 - 0
chat_final_bootstrap/.htaccess

@@ -0,0 +1,6 @@
+RewriteEngine on
+# If a directory or a file exists, use the request directly
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+# Otherwise forward the request to index.html
+RewriteRule . index.html [L]

+ 123 - 0
chat_final_bootstrap/package-lock.json

@@ -2192,6 +2192,11 @@
       "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz",
       "integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw=="
     },
+    "@types/component-emitter": {
+      "version": "1.2.10",
+      "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz",
+      "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg=="
+    },
     "@types/eslint": {
       "version": "7.2.6",
       "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz",
@@ -3473,6 +3478,11 @@
       "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
       "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
     },
+    "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.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -3528,6 +3538,11 @@
         }
       }
     },
+    "base64-arraybuffer": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
+      "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
+    },
     "base64-js": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -4416,6 +4431,14 @@
         "sha.js": "^2.4.8"
       }
     },
+    "cross-fetch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.1.tgz",
+      "integrity": "sha512-eIF+IHQpRzoGd/0zPrwQmHwDC90mdvjk+hcbYhKoaRrEk4GEIDqdjs/MljmdPPoHTQudbmWS+f0hZsEpFaEvWw==",
+      "requires": {
+        "node-fetch": "2.6.1"
+      }
+    },
     "cross-spawn": {
       "version": "6.0.5",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -5307,6 +5330,30 @@
         "once": "^1.4.0"
       }
     },
+    "engine.io-client": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.0.0.tgz",
+      "integrity": "sha512-e6GK0Fqvq45Nu/j7YdIVqXtDPvlsggAcfml3QiEiGdJ1qeh7IQU6knxSN3+yy9BmbnXtIfjo1hK4MFyHKdc9mQ==",
+      "requires": {
+        "base64-arraybuffer": "0.1.4",
+        "component-emitter": "~1.3.0",
+        "debug": "~4.3.1",
+        "engine.io-parser": "~4.0.1",
+        "has-cors": "1.1.0",
+        "parseqs": "0.0.6",
+        "parseuri": "0.0.6",
+        "ws": "~7.4.2",
+        "yeast": "0.1.2"
+      }
+    },
+    "engine.io-parser": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz",
+      "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==",
+      "requires": {
+        "base64-arraybuffer": "0.1.4"
+      }
+    },
     "enhanced-resolve": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
@@ -6353,6 +6400,11 @@
         }
       }
     },
+    "extract-files": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz",
+      "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ=="
+    },
     "extsprintf": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
@@ -6952,6 +7004,28 @@
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.5.tgz",
       "integrity": "sha512-kBBSQbz2K0Nyn+31j/w36fUfxkBW9/gfwRWdUY1ULReH3iokVJgddZAFcD1D0xlgTmFxJCbUkUclAlc6/IDJkw=="
     },
+    "graphql-request": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-3.4.0.tgz",
+      "integrity": "sha512-acrTzidSlwAj8wBNO7Q/UQHS8T+z5qRGquCQRv9J1InwR01BBWV9ObnoE+JS5nCCEj8wSGS0yrDXVDoRiKZuOg==",
+      "requires": {
+        "cross-fetch": "^3.0.6",
+        "extract-files": "^9.0.0",
+        "form-data": "^3.0.0"
+      },
+      "dependencies": {
+        "form-data": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+          "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.8",
+            "mime-types": "^2.1.12"
+          }
+        }
+      }
+    },
     "growly": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
@@ -6999,6 +7073,11 @@
         "function-bind": "^1.1.1"
       }
     },
+    "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",
@@ -10252,6 +10331,11 @@
         }
       }
     },
+    "node-fetch": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+      "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
+    },
     "node-forge": {
       "version": "0.10.0",
       "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
@@ -10781,6 +10865,16 @@
       "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
       "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="
     },
+    "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",
@@ -13853,6 +13947,30 @@
         }
       }
     },
+    "socket.io-client": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.0.0.tgz",
+      "integrity": "sha512-27yQxmXJAEYF19Ygyl8FPJ0if0wegpSmkIIbrWJeI7n7ST1JyH8bbD5v3fjjGY5cfCanACJ3dARUAyiVFNrlTQ==",
+      "requires": {
+        "@types/component-emitter": "^1.2.10",
+        "backo2": "~1.0.2",
+        "component-emitter": "~1.3.0",
+        "debug": "~4.3.1",
+        "engine.io-client": "~5.0.0",
+        "parseuri": "0.0.6",
+        "socket.io-parser": "~4.0.4"
+      }
+    },
+    "socket.io-parser": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
+      "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
+      "requires": {
+        "@types/component-emitter": "^1.2.10",
+        "component-emitter": "~1.3.0",
+        "debug": "~4.3.1"
+      }
+    },
     "sockjs": {
       "version": "0.3.20",
       "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz",
@@ -16709,6 +16827,11 @@
         }
       }
     },
+    "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",

+ 51 - 49
chat_final_bootstrap/package.json

@@ -1,51 +1,53 @@
 {
-  "name": "final_olx",
-  "version": "0.1.0",
-  "private": true,
-  "dependencies": {
-    "@testing-library/jest-dom": "^5.11.9",
-    "@testing-library/react": "^11.2.5",
-    "@testing-library/user-event": "^12.6.3",
-    "bootstrap": "^5.0.0-beta2",
-    "bootstrap-icons": "^1.4.0",
-    "jquery": "^3.5.1",
-    "jwt-decode": "^3.1.2",
-    "popper.js": "^1.16.1",
-    "react": "^17.0.1",
-    "react-bootstrap": "^1.5.1",
-    "react-dom": "^17.0.1",
-    "react-dropzone": "^11.3.1",
-    "react-redux": "^7.2.2",
-    "react-router-dom": "^5.2.0",
-    "react-scripts": "4.0.2",
-    "react-scrollable-feed": "^1.3.0",
-    "redux": "^4.0.5",
-    "redux-thunk": "^2.3.0",
-    "sass": "^1.32.8",
-    "web-vitals": "^1.1.0"
-  },
-  "scripts": {
-    "start": "react-scripts start",
-    "build": "react-scripts build",
-    "test": "react-scripts test",
-    "eject": "react-scripts eject"
-  },
-  "eslintConfig": {
-    "extends": [
-      "react-app",
-      "react-app/jest"
-    ]
-  },
-  "browserslist": {
-    "production": [
-      ">0.2%",
-      "not dead",
-      "not op_mini all"
-    ],
-    "development": [
-      "last 1 chrome version",
-      "last 1 firefox version",
-      "last 1 safari version"
-    ]
-  }
+    "name": "final_olx",
+    "version": "0.1.0",
+    "private": true,
+    "dependencies": {
+        "@testing-library/jest-dom": "^5.11.9",
+        "@testing-library/react": "^11.2.5",
+        "@testing-library/user-event": "^12.6.3",
+        "bootstrap": "^5.0.0-beta2",
+        "bootstrap-icons": "^1.4.0",
+        "graphql-request": "^3.4.0",
+        "jquery": "^3.5.1",
+        "jwt-decode": "^3.1.2",
+        "popper.js": "^1.16.1",
+        "react": "^17.0.1",
+        "react-bootstrap": "^1.5.1",
+        "react-dom": "^17.0.1",
+        "react-dropzone": "^11.3.1",
+        "react-redux": "^7.2.2",
+        "react-router-dom": "^5.2.0",
+        "react-scripts": "4.0.2",
+        "react-scrollable-feed": "^1.3.0",
+        "redux": "^4.0.5",
+        "redux-thunk": "^2.3.0",
+        "sass": "^1.32.8",
+        "socket.io-client": "^4.0.0",
+        "web-vitals": "^1.1.0"
+    },
+    "scripts": {
+        "start": "react-scripts start",
+        "build": "react-scripts build",
+        "test": "react-scripts test",
+        "eject": "react-scripts eject"
+    },
+    "eslintConfig": {
+        "extends": [
+            "react-app",
+            "react-app/jest"
+        ]
+    },
+    "browserslist": {
+        "production": [
+            ">0.2%",
+            "not dead",
+            "not op_mini all"
+        ],
+        "development": [
+            "last 1 chrome version",
+            "last 1 firefox version",
+            "last 1 safari version"
+        ]
+    }
 }

+ 1 - 0
chat_final_bootstrap/public/index.html

@@ -22,6 +22,7 @@
       Learn how to configure a non-root public URL by running `npm run build`.
     -->
         <title>Chat. FEA-23.</title>
+        <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
     </head>
     <body>
         <noscript>You need to enable JavaScript to run this app.</noscript>

+ 2 - 2
chat_final_bootstrap/src/Actions/ActionLogin.js

@@ -45,8 +45,8 @@ export const actionUserInfo = (userId) => async (dispatch) => {
 
     // console.log("UserFindOne - ##########", userData.data.UserFindOne);
 
-    if (userData && userData.data.UserFindOne) {
-        dispatch(actionAuthInfo(userData.data.UserFindOne)); // получить доп инфу о юзере
+    if (userData && userData.data && userData.data.UserFindOne) {
+        dispatch(actionAuthInfo(userData.data.UserFindOne)); // занести доп инфу Redax
         if (userData.data.UserFindOne.chats) {
             userData.data.UserFindOne.chats.forEach(async (chat) => {
                 // получить по 10 первых сообщений из каждого из моих чатов

+ 27 - 58
chat_final_bootstrap/src/Actions/ActionsGql.js

@@ -75,12 +75,12 @@ export const actionSearchMessagesByChatId = (_chatId, skip = 0, searchStr = "",
                             avatar{url}
                         }
                         text
-                        createdAt
                         chat {
                             _id
                             title
                             avatar{url}
                         }
+                        media {url text _id}
                     }
                 }`,
                 { searchMsgStr: JSON.stringify([searchObj, { sort: [{ _id: -1 }], skip: [skip], limit: [limit] }]) }
@@ -104,7 +104,7 @@ export const actionSearchMessagesByChatId = (_chatId, skip = 0, searchStr = "",
             // из поля createdAt последнего прибывшего сообщения
             // теперь store.auth.chats...createdAt будет говорить о дате последнего изменения в этом чате
             // надо для сортировки списка чатов
-            // а ыообще надо "попросить" backend-ера внести в сущность Chat поле "lastModified"
+            // а вообще надо "попросить" backend-ера внести в сущность Chat поле "lastModified"
             // либо "lastMessageCreatedAt" - снимет кучу проблем
 
             // console.log("MessageFind", _chatId, messages.data.MessageFind[messages.data.MessageFind.length - 1].createdAt);
@@ -117,29 +117,33 @@ export const actionSearchMessagesByChatId = (_chatId, skip = 0, searchStr = "",
             );
 
             // подсчет количества сообщений в чате с таким-то id
-            let count = await gql(
-                `query MessageCountByChatId ($chatId:String){
+            countMsgInChat(_chatId);
+        }
+    } else {
+        dispatch({
+            type: "NEW_COUNT",
+            count: {
+                [_chatId]: 0,
+            },
+        });
+    }
+};
+
+export const countMsgInChat = async (_chatId) => {
+    let count = await gql(
+        `query MessageCountByChatId ($chatId:String){
                     MessageCount(query: $chatId)
                 }`,
-                { chatId: JSON.stringify([{ "chat._id": _chatId }]) }
-            );
+        { chatId: JSON.stringify([{ "chat._id": _chatId }]) }
+    );
 
-            if (!count.data.errors) {
-                dispatch({
-                    type: "NEW_COUNT",
-                    count: {
-                        [_chatId]: count.data.MessageCount,
-                    },
-                });
-            }
-        } else {
-            dispatch({
-                type: "NEW_COUNT",
-                count: {
-                    [_chatId]: 0,
-                },
-            });
-        }
+    if (!count.data.errors) {
+        store.dispatch({
+            type: "NEW_COUNT",
+            count: {
+                [_chatId]: count.data.MessageCount,
+            },
+        });
     }
 };
 
@@ -201,15 +205,12 @@ export const actionSearchChat = (_userId = "", str = "") => async (dispatch) =>
             )
         )
     );
-    console.log("actionSearchChat - searchData:", searchData);
+    // console.log("actionSearchChat - searchData:", searchData);
 };
 
 export const actionAllUsersFind = (skip = 0, str = "") => async (dispatch) => {
     str = toQuery(str);
 
-    //FIXME:
-    // console.log(str);
-
     let users = await dispatch(
         actionPromise(
             "UserFind",
@@ -232,35 +233,3 @@ export const actionAllUsersFind = (skip = 0, str = "") => async (dispatch) => {
         // console.log("actionUserFind - UserFind:", users);
     }
 };
-
-// export const actionUsersConcat = (userArr) => {
-//     // console.log("actionUsersConcat - ", JSON.stringify(userArr, null, 4));
-//     // console.log("actionUsersConcat - ", userArr);
-//     return { type: "NEW_USER_PART", userArr };
-// };
-
-// это НИЗЗЯ!! это - затянуть всех пользователей сети к себе на Front!!
-
-// export const actionGetAllUsers = () => async (dispatch) => {
-//     let userCount = await dispatch(
-//         actionPromise(
-//             "userCount",
-//             gql(
-//                 `query UsersCount{
-//                     UserCount(query:"[{}]")
-//                 }`
-//             )
-//         )
-//     );
-//     // console.log("actionUserCount - userCount:", userCount.data.UserCount);
-//     if (!userCount.errors) {
-//         userCount = userCount.data.UserCount;
-//         // console.log("actionUserCount - userCount:", userCount);
-
-//         for (let i = 0; i < userCount; i += 100) {
-//             // console.log("++++++++", i);
-//             await actionAllUsersFind(i)(dispatch);
-//             dispatch(actionUsersConcat(store.getState().promise.UserFind.payload.data.UserFind));
-//         }
-//     }
-// };

+ 28 - 7
chat_final_bootstrap/src/Actions/ActionsGqlUpsert.js

@@ -4,9 +4,24 @@ import { urlUploadConst } from "../const";
 import { actionPromise } from "../Reducers";
 import { gql, actionSearchMessagesByChatId, actionUserInfo, actionAuthLogout } from "../Actions";
 import history from "../history";
-
-export const actionMessageUpsert = ({ text, chatId }) => async (dispatch) => {
-    // console.log("actionMessageUpsert --- ", text, chatId);
+import { store } from "../Reducers";
+
+export const actionMessageUpsert = ({ text, chatId, imgArray = [] }) => async (dispatch) => {
+    // console.log("actionMessageUpsert --- ", text, chatId, imgArray.current);
+
+    let messageData = {
+        text,
+        chat: {
+            _id: chatId,
+        },
+    };
+
+    if (imgArray.current[0]) {
+        messageData.media = imgArray.current.map((el) => ({
+            _id: el._id,
+        }));
+    }
+    // console.log(messageData);
 
     let msgData = await dispatch(
         actionPromise(
@@ -15,20 +30,27 @@ export const actionMessageUpsert = ({ text, chatId }) => async (dispatch) => {
                 `mutation MessageUpsert($messageData:MessageInput){
                     MessageUpsert(message: $messageData){
                         _id
+                        media{
+                            _id
+                            url
+                        }
                     }
                 }`,
-                { messageData: { text, chat: { _id: chatId } } }
+                { messageData }
             )
         )
     );
 
     // console.log("MessageUpsert - ", msgData);
 
-    if (msgData && msgData.data && msgData.data.MessageUpsert && msgData.data.MessageUpsert._id) {
-        dispatch(actionSearchMessagesByChatId(chatId));
+    if (msgData.errors && msgData.errors[0] && msgData.errors[0].message) {
+        // console.log(`Ошибка отправки сообщения: \nСкорее всего Вас удалили из чата...\n ${msgData.errors[0].message}`);
+        dispatch(actionUserInfo(store.getState().auth.payloadId));
+        history.push("/");
     }
 };
 
+// пока что это только привязка аватарки к чату
 const actionMediaUpsert = ({ chatId, mediaId }) => async (dispatch) => {
     let mediaData = await dispatch(
         actionPromise(
@@ -158,7 +180,6 @@ export const actionUserUpdate = ({ _id, login, nick, password, avaFile, isNeedLo
     if (!userUpserResult.errors) {
         if (isNeedLogout) {
             await dispatch(actionAuthLogout());
-            console.log("upsert success after actionLogin");
         } else {
             await dispatch(actionUserInfo(_id));
         }

+ 2 - 0
chat_final_bootstrap/src/Actions/index.js

@@ -5,6 +5,7 @@ import {
     actionGetMessagesByChatId,
     actionSearchChat,
     actionAllUsersFind,
+    countMsgInChat,
 } from "./ActionsGql";
 import { actionMsgNewChat, actionCurChatId, actionMsgInsertInHead, actionUpdateChatCreatedAt } from "./ActionsMsg";
 import { actionAddUserToChatList, actionDelUserFromChatList, actionNewChatList } from "./ActionsChatUsers";
@@ -31,4 +32,5 @@ export {
     actionMessageUpsert,
     actionUserUpdate,
     actionUpdateChatCreatedAt,
+    countMsgInChat,
 };

+ 24 - 22
chat_final_bootstrap/src/App.js

@@ -16,7 +16,7 @@ import { Provider, connect } from "react-redux";
 import { Header, Footer } from "./Layout";
 import { CPageMain, PageLogin, CPageNewChat, PageUpload, PageAbout, CPageSearch } from "./Pages";
 import { actionFindChatsByUserId } from "./Actions";
-
+import { urlUploadConst } from "./const";
 import { store } from "./Reducers";
 
 const PageNotFound = () => {
@@ -30,29 +30,31 @@ const PageNotFound = () => {
     );
 };
 
-const App = () => (
-    <Provider store={store}>
-        <Router history={history}>
-            <div className="mainWrapper">
-                <Header />
+const App = () => {
+    return (
+        <Provider store={store}>
+            <Router history={history}>
+                <div className="mainWrapper">
+                    <Header />
 
-                <Switch>
-                    <Route path="/about" component={PageAbout} exact />
-                    <Route path="/newchat" component={CPageNewChat} exact />
-                    <Route path="/newchat/:_chatId" component={CPageNewChat} exact />
-                    <Route path="/upload" component={PageUpload} exact />
-                    <Route path="/" component={PageLogin} exact />
-                    <Route path="/main/:_chatId" component={CPageMain} exact />
-                    <Route path="/main/" component={CPageMain} exact />
-                    <Route path="/search/" component={CPageSearch} exact />
+                    <Switch>
+                        <Route path="/about" component={PageAbout} exact />
+                        <Route path="/newchat" component={CPageNewChat} exact />
+                        <Route path="/newchat/:_chatId" component={CPageNewChat} exact />
+                        <Route path="/upload" component={PageUpload} exact />
+                        <Route path="/" component={PageLogin} exact />
+                        <Route path="/main/" component={CPageMain} exact />
+                        <Route path="/main/:_chatId" component={CPageMain} exact />
+                        {/* <Route path="/search/" component={CPageSearch} exact /> */}
 
-                    <Route component={PageNotFound} exact />
-                </Switch>
+                        <Route component={PageNotFound} exact />
+                    </Switch>
 
-                {/* <Footer /> */}
-            </div>
-        </Router>
-    </Provider>
-);
+                    {/* <Footer /> */}
+                </div>
+            </Router>
+        </Provider>
+    );
+};
 
 export default App;

+ 33 - 10
chat_final_bootstrap/src/App.scss

@@ -5,14 +5,6 @@ $position-values: (
     20: 20%,
 );
 
-// // Create your own map
-// $custom-colors: (
-//     "custom-color": #900,
-// );
-
-// // Merge the maps
-// $theme-colors: map-merge($theme-colors, $custom-colors);
-
 div {
     border-radius: 7px;
     // border: 1px solid red;
@@ -40,7 +32,7 @@ div {
 }
 
 body {
-    overflow: hidden;
+    // overflow: hidden;
     background: url("./images/romashki_1920x1080.jpg");
 }
 
@@ -164,10 +156,41 @@ body {
     z-index: 10;
 }
 
+.MediaItem {
+    img {
+        max-width: 150px;
+        max-height: 150px;
+    }
+}
+
+.modalImg {
+    width: 470px;
+    height: 470px;
+    img {
+        max-width: 100%;
+        height: auto;
+    }
+}
+.modalImgSend {
+    width: 470px;
+    height: 470px;
+
+    > div {
+        width: 300px;
+        height: 300px;
+        margin: 2px;
+    }
+    img {
+        width: 100%;
+        height: auto;
+        margin: 2px;
+    }
+}
+
 @import "/node_modules/bootstrap/scss/bootstrap";
 @import "./login.scss";
 // @media (prefers-reduced-motion: reduce);
 
 * {
-    transition: all 0.3s linear;
+    transition: all 0.2s linear;
 }

+ 0 - 3
chat_final_bootstrap/src/Components/AdditionalTools.js

@@ -35,9 +35,6 @@ export const AdditionalTools = ({ _userId, onSearch = null }) => {
                 <i className="bi bi-chat-left-text"></i>
                 <span className="ms-2">New Chat</span>
             </Button>
-            {/* <Button className="gradient" variant="secondary btn-sm  m-2">
-                Join to another chat
-            </Button> */}
         </div>
     );
 };

+ 59 - 5
chat_final_bootstrap/src/Components/ChatMessages.js

@@ -5,11 +5,61 @@ import logo from "../images//logo23.jpg";
 import { actionGetMessagesByChatId, actionSearchMessagesByChatId } from "../Actions";
 import ScrollableFeed from "react-scrollable-feed";
 import { urlUploadConst } from "../const";
+import history from "../history";
+import { Button, Form, Modal } from "react-bootstrap";
+
+const MediaItem = ({ mediaItem }) => {
+    const [show, setShow] = useState(false);
+
+    const handleClose = () => setShow(false);
+
+    const handleShow = () => setShow(true);
+
+    let mediaUrl, mediaText;
+    if (mediaItem && mediaItem.url) mediaUrl = mediaItem.url;
+    if (mediaItem && mediaItem.text) mediaText = mediaItem.text;
+    return (
+        <>
+            <div className="MediaItem m-1">
+                <img src={`${urlUploadConst}/${mediaUrl}`} onClick={handleShow} />
+                {mediaText && <div> {`${mediaText}`}</div>}
+            </div>
+            <Modal show={show} onHide={handleClose}>
+                <Modal.Header closeButton>
+                    <Modal.Title></Modal.Title>
+                </Modal.Header>
+                <Modal.Body>
+                    <div className="modalImg border border-warning d-flex">
+                        <img src={`${urlUploadConst}/${mediaUrl}`} />
+                    </div>
+                </Modal.Body>
+            </Modal>
+        </>
+    );
+};
+
+const Media = ({ media = null }) => {
+    return (
+        <>
+            {media && media.length && (
+                <>
+                    <div className="d-flex flex-wrap ">
+                        {/* {JSON.stringify(media, null, 4)} */}
+                        {media.map((mediaItem) => (
+                            <MediaItem key={mediaItem._id} mediaItem={mediaItem} />
+                        ))}
+                    </div>
+                </>
+            )}
+        </>
+    );
+};
 
 const MessageItem = ({
     _id,
     createdAt = 0,
     text = "",
+    media,
     owner: { _id: ownerId, login = "", nick = "", avatar },
     myId,
 }) => {
@@ -63,8 +113,8 @@ const MessageItem = ({
                 {/* <p>{`Login: ${login}, Nick: ${nick}, ownerId: ${ownerId}`}</p> */}
 
                 <div className="lh-sm mb-2 text-success fw-bolder">{`${nick}`}</div>
-                {/* <div className="text-dark fs-6 lh-sm mb-3">{text.replace(/\r?\n|\r/g, "\n")}</div> */}
-                <div className="text-dark fs-6 lh-sm mb-3">{text}</div>
+                <Media media={media} />
+                <div className="text-dark fs-6 lh-sm mb-3 text-wrap text-break">{text}</div>
 
                 {/* <span className="text-success">{`message id: ${_id}`}</span> */}
                 <span className="position-absolute bottom-0 end-0  badge rounded-pill bg-secondary">
@@ -84,7 +134,7 @@ const MessagesList = ({ messages, myId, chatId, getMoreMessages = null }) => {
     const isNeedMoreMessages = useRef(false);
 
     let arrayOfMessages = messages[chatId];
-    // console.log(isNeedMoreMessages.current, arrayOfMessages && arrayOfMessages.length);
+    // console.log("рисую компоненту- ", messages);
 
     // скролл в самый низ при приходе новых сообщений, scrollIntoView({ behavior: "smooth" }) - плавно
     // от плавности прийдется отказаться, так как успевает сработатьт запрос на чтение новой порции сообщений
@@ -129,6 +179,7 @@ const MessagesList = ({ messages, myId, chatId, getMoreMessages = null }) => {
     );
 };
 
+// другой вариант скрола
 // скролл вниз при новых сообщениях только если мы на этот момент уже были внизу внизу
 // const MessagesList = ({ arrayOfMessages }) => {
 //     return (
@@ -150,7 +201,11 @@ const CMessagesList = connect(
 )(MessagesList);
 
 const Messages = ({ _id = "", chatInfo, messages, getMsg }) => {
-    if (chatInfo) chatInfo = chatInfo.filter((chat) => chat._id === _id);
+    // _id чата
+
+    if (chatInfo && _id) chatInfo = chatInfo.filter((chat) => chat._id === _id);
+
+    if (!chatInfo || !chatInfo[0]) history.push("/");
 
     let avatar = chatInfo && chatInfo[0] && chatInfo[0].avatar && chatInfo[0].avatar.url;
     let title = chatInfo && chatInfo[0] && chatInfo[0].title && chatInfo[0].title.trim();
@@ -195,7 +250,6 @@ const Messages = ({ _id = "", chatInfo, messages, getMsg }) => {
                         </Link>
                         <div className="fs-5 fw-bolder ms-2">{`${title}`}</div>
                     </div>
-                    {/* <span className="position-absolute bottom-0 end-0  badge rounded-pill bg-secondary"> */}
                     <span className="position-absolute bottom-0 end-0 rounded-pill bg-success">
                         <Link to={`/newchat/${_id}`} className="noUnderLine text-light">
                             {/* {` _chatId: ${_id}`} */}

+ 2 - 0
chat_final_bootstrap/src/Layout/AllUsersList.js

@@ -122,6 +122,8 @@ const AllUsersList = ({
     }
 
     // выкидываем пользователей, у которых (ник из одних пробелов) или (нет ника но логин из одних пробелов)
+    // с ними не интересно общаться ))
+    // это глюки базы - таких пользователей быть не должно
 
     searchUserResultArr = tempResult
         .filter((el) => {

+ 20 - 20
chat_final_bootstrap/src/Layout/Header.js

@@ -6,25 +6,25 @@ import { Nav, Navbar } from "react-bootstrap";
 import logo from "../images/logo23.png";
 
 const Logo = ({ link = "" }) => <img src={logo} width="50px" />;
-
 const CLogo = connect((s) => ({ link: s.auth.payloadId }))(Logo);
 
-export const Header = () => (
-    <>
-        {/* <Navbar className="gradient" collapseOnSelect expand="sm" bg="dark" variant="dark"> */}
-        <Navbar className="gradient header mb-2" collapseOnSelect bg="dark" variant="dark">
-            <Navbar.Toggle aria-controls="responsive-navbar-nav" />
-            <Navbar.Collapse id="responsive-navbar-nav">
-                <Nav>
-                    <Link to={`/about`} className="noUnderLine m-2">
-                        <CLogo />
-                        <Navbar.Brand className="mx-2 h1">Socket Chat</Navbar.Brand>{" "}
-                    </Link>
-                </Nav>
-                <Nav className="ms-auto">
-                    <CButtonLogout />
-                </Nav>
-            </Navbar.Collapse>
-        </Navbar>
-    </>
-);
+export const Header = () => {
+    return (
+        <>
+            <Navbar className="gradient header mb-2" collapseOnSelect bg="dark" variant="dark">
+                <Navbar.Toggle aria-controls="responsive-navbar-nav" />
+                <Navbar.Collapse id="responsive-navbar-nav">
+                    <Nav>
+                        <Link to={`/about`} className="noUnderLine m-2">
+                            <CLogo />
+                            <Navbar.Brand className="mx-2 h1">Socket Chat</Navbar.Brand>{" "}
+                        </Link>
+                    </Nav>
+                    <Nav className="ms-auto">
+                        <CButtonLogout />
+                    </Nav>
+                </Navbar.Collapse>
+            </Navbar>
+        </>
+    );
+};

+ 1 - 4
chat_final_bootstrap/src/Layout/UserInfo.js

@@ -160,10 +160,9 @@ const UserInfo = ({ myId, avatarUrl, login, nick, doUpdate = null }) => {
                                 ref={inpNickRef}
                                 onChange={(e) => {
                                     setInpNick(e.target.value);
-                                    setIsNeedLogout(true);
+                                    // setIsNeedLogout(true);
                                 }}
                             />
-                            {/* <Form.Text className="text-muted">muted text</Form.Text> */}
                         </Form.Group>
 
                         <Form.Group controlId="fromBasicPassword" className="mb-4">
@@ -181,8 +180,6 @@ const UserInfo = ({ myId, avatarUrl, login, nick, doUpdate = null }) => {
                         </Form.Group>
 
                         <Form.Group>
-                            {/* <Form.Check type="checkbox" label="Remeber me" /> */}
-                            {/* <Button onClick={handleClose}>Close</Button> */}
                             <ButtonCancel cansel={handleClose} />
                             <Button
                                 className="gradient rounded-3 ms-2 mb-2"

+ 117 - 27
chat_final_bootstrap/src/Pages/PageMain.js

@@ -6,10 +6,17 @@ import { actionSearchMessagesByChatId, actionCurChatId, actionMessageUpsert } fr
 import { connect } from "react-redux";
 import { useState, useEffect, useRef } from "react";
 import userEvent from "@testing-library/user-event";
+import { useDropzone } from "react-dropzone";
+import React, { useCallback } from "react";
+import { Button, Form, Modal } from "react-bootstrap";
+import { urlUploadConst } from "../const";
 
 const MessageInput = ({ curChatId: { curChatId } = {}, messageUpsert }) => {
     const textRef = useRef({});
+    const textRefModal = useRef({});
     const [text, setText] = useState("");
+    const [dropFiles, setDropFiles] = useState([]);
+    const resultArray = useRef([]);
 
     const textTyping = (e) => {
         setText(e.target.value);
@@ -27,46 +34,128 @@ const MessageInput = ({ curChatId: { curChatId } = {}, messageUpsert }) => {
         if (text.trim()) {
             // console.log(text.trim());
             setText("");
+            setShow(false);
             if (textRef.current) {
                 textRef.current.value = "";
+                textRefModal.current.value = "";
             }
-            messageUpsert({ text: text.trim(), chatId: curChatId });
+            messageUpsert({ text: text.trim(), chatId: curChatId, imgArray: resultArray });
         }
     };
 
     useEffect(() => {
         setText("");
+
         if (textRef.current) {
             textRef.current.value = "";
         }
     }, [curChatId]);
 
+    const onDrop = useCallback(async (acceptedFiles) => {
+        resultArray.current = [];
+
+        if (acceptedFiles[0]) {
+            handleShow();
+        }
+
+        let aaryOfFatchs = acceptedFiles.map((file) => {
+            let dataSingl = new FormData();
+            dataSingl.set("media", file);
+            return fetch(`${urlUploadConst}/upload`, {
+                method: "POST",
+                headers: localStorage.authToken ? { Authorization: "Bearer " + localStorage.authToken } : {},
+                body: dataSingl,
+            }).then((res) => res.json());
+        });
+
+        resultArray.current = [];
+
+        await Promise.all(aaryOfFatchs)
+            .then((responses) => responses.forEach((response) => resultArray.current.push(response)))
+            .catch((e) => alert("Ошибка загрузки.", e));
+
+        // console.log(resultArray.current);
+        setDropFiles([...resultArray.current]);
+    }, []);
+
+    const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
+    const [show, setShow] = useState(false);
+    const handleClose = () => setShow(false);
+    const handleShow = () => setShow(true);
+
     return (
         <>
             {curChatId && (
-                <div className="bg-light shadow-sm border border-2 rounded-3 MessageInput">
-                    {/* <div> InputArea</div>
-                    <div> {`${curChatId}`}</div> */}
-
-                    {/* -------------- Поле отправки сообщения -----------------*/}
-
-                    <div className="position-relative">
-                        <textarea
-                            className="form-control h-75"
-                            placeholder="Write a message..."
-                            ref={textRef}
-                            onChange={textTyping}
-                            onKeyUp={sendMsgByEnterKey}
-                        ></textarea>
-                        <span
-                            className="position-absolute bottom-0 end-0  badge rounded-pill bg-success"
-                            role-button="true"
-                            onClick={sendMsg}
-                        >
-                            <i className="bi bi-chat-dots"></i> <i className="bi bi-reply-fill"></i>
-                        </span>
+                <>
+                    <div className="bg-light shadow-sm border border-2 rounded-3 MessageInput">
+                        {/* -------------- Поле отправки сообщения -----------------*/}
+                        <div className="position-relative">
+                            <textarea
+                                className="form-control h-75"
+                                placeholder="Write a message..."
+                                ref={textRef}
+                                onChange={textTyping}
+                                onKeyUp={sendMsgByEnterKey}
+                                key={"mainTextarea"}
+                            ></textarea>
+                            <span
+                                className="position-absolute bottom-0 end-0  badge rounded-pill bg-success"
+                                role-button="true"
+                                onClick={sendMsg}
+                            >
+                                <i className="bi bi-chat-dots"></i> <i className="bi bi-reply-fill"></i>
+                            </span>
+                        </div>
+                        <div className="border border-warning">
+                            <div {...getRootProps()}>
+                                <input {...getInputProps()} />
+
+                                {isDragActive ? (
+                                    <>
+                                        <i className="bi bi-paperclip fs-3 text-secondary"></i>
+                                        <span className="text-secondary">Drop the files here ...</span>
+                                    </>
+                                ) : (
+                                    <>
+                                        <i className="bi bi-paperclip fs-3 text-secondary"></i>{" "}
+                                        <span className="text-secondary">Drop the files here ...</span>
+                                    </>
+                                )}
+                            </div>
+                        </div>
                     </div>
-                </div>
+                    <Modal show={show} onHide={handleClose} key={"sendModal"}>
+                        <Modal.Header closeButton>
+                            <Modal.Title></Modal.Title>
+                        </Modal.Header>
+                        <Modal.Body>
+                            <div className="modalImgSend d-flex" key={"sendModalDiv"}>
+                                {/* {"Images"} */}
+                                {dropFiles.map((file) => (
+                                    <div>
+                                        <img src={`${urlUploadConst}/${file.url}`} key={file._id} />
+                                    </div>
+                                ))}
+                            </div>
+                            <div className="position-relative">
+                                <textarea
+                                    className="form-control h-75"
+                                    placeholder="Write a message..."
+                                    ref={textRefModal}
+                                    onChange={textTyping}
+                                    onKeyUp={sendMsgByEnterKey}
+                                ></textarea>
+                                <span
+                                    className="position-absolute bottom-0 end-0  badge rounded-pill bg-success"
+                                    role-button="true"
+                                    onClick={sendMsg}
+                                >
+                                    <i className="bi bi-chat-dots"></i> <i className="bi bi-reply-fill"></i>
+                                </span>
+                            </div>
+                        </Modal.Body>
+                    </Modal>
+                </>
             )}
         </>
     );
@@ -84,14 +173,14 @@ const PageMain = ({
     getMesagesList = null,
     setCurId = null, // из редакса через коннект
 }) => {
-    // console.log(_chatId);
-    // console.log(messages);
+ 
 
     if (
         !_userId ||
         !store.getState().auth ||
         !store.getState().auth.login ||
         _userId !== store.getState().auth.payloadId
+        // чтобы не зайти в чужой чат и не увидеть то, что тебе видеть не положено
     ) {
         history.push("/");
     }
@@ -100,6 +189,8 @@ const PageMain = ({
     //
     //
     useEffect(() => {
+        if (typeof setCurId === "function") setCurId(_chatId);
+
         if (
             typeof getMesagesList === "function" &&
             _chatId &&
@@ -107,7 +198,6 @@ const PageMain = ({
         ) {
             getMesagesList(_chatId);
         }
-        if (typeof setCurId === "function") setCurId(_chatId);
     }, [_chatId]);
 
   
@@ -138,7 +228,7 @@ const PageMain = ({
 };
 
 // prettier-ignore
-export const CPageMain = connect((s) => ({ _userId: s.auth && s.auth.payloadId, messages: s.msg }), {
+export const CPageMain = connect((s) => ({ _userId: s.auth && s.auth.payloadId, messages: s.msg, myChats:s.auth.chats }), {
     setCurId: actionCurChatId,
     getMesagesList: actionSearchMessagesByChatId,
 })(PageMain);

+ 0 - 10
chat_final_bootstrap/src/Pages/PageNewChat.js

@@ -29,11 +29,6 @@ const PageNewChat = ({ doSearchUsers = null, match: { params: { _chatId = "" } =
         }
     }, [searchUserStr]);
 
-    //FIXME: это для чего???? уже не помню и похоже не надо ))))
-    // useEffect(() => {
-    //     store.dispatch({ type: "CLEAR_USERS" });
-    // }, []);
-
     return (
         <div className="maxWidthPageMain">
             <div className="container-fluid">
@@ -41,9 +36,6 @@ const PageNewChat = ({ doSearchUsers = null, match: { params: { _chatId = "" } =
                     <div className="col-md-4">
                         <div className="maxWidthForSideBar shadow">
                             <div className="bg-light gradient shadow-sm border-2 rounded-3 flex-grow-1 pt-2">
-                                {/* <CUserInfo /> */}
-                                {/* <ButtonToMain /> */}
-                                {/* <ButtonCancel /> */}
                                 <h6 className="ms-2 fs-5">Choose members:</h6>
                                 <div className="p-2">
                                     <input
@@ -55,8 +47,6 @@ const PageNewChat = ({ doSearchUsers = null, match: { params: { _chatId = "" } =
                                     ></input>
                                 </div>
 
-                                {/* <h6 className="ms-2 fs-4">Choose members:</h6> */}
-
                                 <CAllUsersList searchUserStr={searchUserStr} />
                             </div>
                         </div>

+ 15 - 15
chat_final_bootstrap/src/Pages/PageUpload.js

@@ -11,7 +11,7 @@ export const PageUpload = () => {
 
     function MyDropzone() {
         const onDrop = useCallback(async (acceptedFiles) => {
-            console.log("МАССИВ", acceptedFiles);
+            // console.log("МАССИВ", acceptedFiles);
 
             let reader = new FileReader();
 
@@ -27,23 +27,23 @@ export const PageUpload = () => {
 
             setFl(false);
 
-            // let aaryOfFatchs = acceptedFiles.map((file) => {
-            //     let dataSingl = new FormData();
-            //     dataSingl.set("media", file);
-            //     return fetch(`${urlUploadConst}/upload`, {
-            //         method: "POST",
-            //         headers: localStorage.authToken ? { Authorization: "Bearer " + localStorage.authToken } : {},
-            //         body: dataSingl,
-            //     }).then((res) => res.json());
-            // });
+            let aaryOfFatchs = acceptedFiles.map((file) => {
+                let dataSingl = new FormData();
+                dataSingl.set("media", file);
+                return fetch(`${urlUploadConst}/upload`, {
+                    method: "POST",
+                    headers: localStorage.authToken ? { Authorization: "Bearer " + localStorage.authToken } : {},
+                    body: dataSingl,
+                }).then((res) => res.json());
+            });
 
-            // resultArray.current = [];
+            resultArray.current = [];
 
-            // await Promise.all(aaryOfFatchs)
-            //     .then((responses) => responses.forEach((response) => resultArray.current.push(response)))
-            //     .catch((e) => alert("Произошла ошибка.", e));
+            await Promise.all(aaryOfFatchs)
+                .then((responses) => responses.forEach((response) => resultArray.current.push(response)))
+                .catch((e) => alert("Произошла ошибка.", e));
 
-            // console.log(resultArray.current);
+            console.log(resultArray.current);
 
             setFl(true);
         }, []);

+ 138 - 12
chat_final_bootstrap/src/Reducers/index.js

@@ -3,7 +3,50 @@ import { createStore, combineReducers, applyMiddleware } from "redux";
 import thunk from "redux-thunk";
 import jwt_decode from "jwt-decode";
 import history from "../history";
-import { actionUserInfo } from "../Actions";
+import { actionUserInfo, countMsgInChat } from "../Actions";
+import { urlUploadConst } from "../const";
+
+export const socket = window.io(urlUploadConst);
+
+const actionSocketMessage = (msg) => ({ type: "SOCKET_MSG", msg });
+const actionSocketChat = (chat) => ({ type: "SOCKET_CHAT", chat });
+const actionSocketChatLeft = (chat_left) => ({ type: "SOCKET_CHAT_LEFT", chat_left });
+
+console.log("- socket start -");
+
+socket.on("jwt_ok", (data) => console.log("jwt_ok", data));
+socket.on("jwt_fail", (error) => console.log("jwt_fail", error));
+
+socket.on("msg", (msg) => {
+    console.log("soket msg - ", msg);
+    store.dispatch(actionSocketMessage(msg));
+});
+
+socket.on("chat", (chat) => {
+    console.log("soket chat - ", chat);
+    store.dispatch(actionSocketChat(chat));
+});
+
+socket.on("chat_left", (chat_left) => {
+    console.log("soket chat_left - ", chat_left);
+    store.dispatch(actionSocketChatLeft(chat_left));
+});
+
+// этот редьюсер как бы совсем не нужен
+// пусть побудет на период отладки
+function socketReducer(state = {}, action) {
+    if (["LOGOUT", "LOGIN"].includes(action.type)) return {};
+
+    // console.log("-ACTION- : ", action);
+
+    if (["SOCKET_MSG", "SOCKET_CHAT", "SOCKET_CHAT_LEFT"].includes(action.type)) {
+        // console.log("-ACTION- : ", action);
+
+        return { ...state, ...action };
+    }
+
+    return state;
+}
 
 function authReducer(state, action) {
     if (state === undefined) {
@@ -18,7 +61,8 @@ function authReducer(state, action) {
     if (action.type === "LOGIN") {
         try {
             localStorage.authToken = action.jwt;
-            console.log("ЛОГИН", jwt_decode(action.jwt).sub.login);
+            socket.emit("jwt", localStorage.authToken);
+            // console.log("ЛОГИН", jwt_decode(action.jwt).sub.login);
             return {
                 login: true,
                 token: action.jwt,
@@ -52,7 +96,7 @@ function authReducer(state, action) {
             chats: action.userInfo.chats,
         };
 
-        // для сортировки чатов по дате
+        // для сортировки чатов по дате добавление поля lastModified
         if (Array.isArray(tempObj.chats)) {
             tempObj.chats = tempObj.chats.map((chat) => {
                 chat.lastModified = chat.createdAt;
@@ -74,6 +118,7 @@ function authReducer(state, action) {
             }
         }
 
+        // сортировка списка чатов согласно полю lastModified
         if (Array.isArray(state.chats)) {
             state.chats.sort((a, b) => b.lastModified - a.lastModified);
         }
@@ -87,11 +132,62 @@ function authReducer(state, action) {
         return { ...state };
     }
 
+    // пришло обновление чата
+    if (action.type === "SOCKET_CHAT") {
+        let newChat = {
+            _id: action.chat._id,
+            title: action.chat.title,
+            createdAt: action.chat.createdAt,
+            owner: action.chat.owner,
+            avatar: action.chat.avatar,
+            members: action.chat.members,
+            lastModified: action.chat.lastModified,
+        };
+
+        state.chats = state.chats.filter((chat) => {
+            return chat._id !== action.chat._id;
+        });
+
+        state.chats = [newChat, ...state.chats];
+
+        // обновить счетчик
+        countMsgInChat(action.chat._id);
+
+        return { ...state };
+    }
+
+    // выдвигаем чат с новым сообщением на 1-е место
+    // для всплытия этого чата на экране на самый верх
+    if (action.type === "SOCKET_MSG") {
+        let topChat;
+
+        if (Array.isArray(state.chats)) {
+            topChat = state.chats.find((el) => el._id === action.msg.chat._id);
+        }
+
+        // именно filter - чтобы удалить дубликаты, вдруг они там есть
+        state.chats = state.chats.filter((chat) => chat._id !== action.msg.chat._id);
+
+        state.chats = [topChat, ...state.chats];
+
+        return { ...state };
+    }
+
+    if (action.type === "SOCKET_CHAT_LEFT") {
+        state.chats = state.chats.filter((chat) => {
+            return chat._id !== action.chat_left._id;
+        });
+
+        state.chats = [...state.chats];
+
+        return { ...state };
+    }
+
     return state;
 }
 
 // счетчики общего числа сообщений в чатах
-function countReduser(state = {}, action) {
+function countReducer(state = {}, action) {
     if (["LOGOUT", "LOGIN"].includes(action.type)) return {};
 
     if (action.type === "NEW_COUNT") {
@@ -101,7 +197,7 @@ function countReduser(state = {}, action) {
     return state;
 }
 
-function msgReduser(state = {}, action) {
+function msgReducer(state = {}, action) {
     if (["LOGOUT", "LOGIN"].includes(action.type)) return {};
 
     if (action.type === "NEW_CHAT") {
@@ -113,10 +209,39 @@ function msgReduser(state = {}, action) {
         return { ...state, [key]: [...value, ...state[key]] };
     }
 
+    if (action.type === "SOCKET_MSG") {
+        let newMsgItem = {
+            _id: action.msg._id,
+            createdAt: action.msg.createdAt,
+            owner: {
+                _id: action.msg.owner._id,
+                login: action.msg.owner.login,
+                nick: action.msg.owner.nick,
+                avatar: { url: (action.msg.owner.avatar && action.msg.owner.avatar.url) || "" },
+            },
+            text: action.msg.text,
+            chat: {
+                _id: action.msg.chat._id,
+                title: action.msg.chat.title,
+                avatar: { url: (action.msg.chat.avatr && action.msg.chat.avatr.url) || "" },
+            },
+        };
+
+        if (action.msg.media && action.msg.media[0]) {
+            newMsgItem.media = [...action.msg.media];
+        }
+
+        // обновить счетчик
+        countMsgInChat(action.msg.chat._id);
+
+        return { ...state, ...{ [action.msg.chat._id]: [...state[action.msg.chat._id], newMsgItem] } };
+    }
+
     return state;
 }
 
-function newChatUsersReduser(state = {}, action) {
+// список пользователей для нового создаваемого чата
+function newChatUsersReducer(state = {}, action) {
     if (["LOGOUT", "LOGIN"].includes(action.type)) return {};
 
     if (action.type === "ADD_USER_TO_CHAT_LIST") {
@@ -135,7 +260,7 @@ function newChatUsersReduser(state = {}, action) {
     return state;
 }
 
-function currentChatIdReduser(state = {}, action) {
+function currentChatIdReducer(state = {}, action) {
     if (["LOGOUT", "LOGIN"].includes(action.type)) return {};
 
     if (action.type === "CURRENTID") {
@@ -185,12 +310,13 @@ export const store = createStore(
     combineReducers({
         auth: authReducer,
         promise: promiseReducer,
-        msg: msgReduser,
-        curChatId: currentChatIdReduser,
-        newChatUsers: newChatUsersReduser,
-        countMsg: countReduser,
+        msg: msgReducer,
+        curChatId: currentChatIdReducer,
+        newChatUsers: newChatUsersReducer,
+        countMsg: countReducer,
+        socket: socketReducer,
     }),
     applyMiddleware(thunk)
 );
 
-store.subscribe(() => console.log(store.getState()));
+// store.subscribe(() => console.log(store.getState()));